[Vendor] Update go-redis to v8.5.0 (#13749)
	
		
	
				
					
				
			* Update go-redis to v8.4.0 * github.com/go-redis/redis/v8 v8.4.0 -> v8.5.0 * Apply suggestions from code review Co-authored-by: zeripath <art27@cantab.net> * TODO * Use the Queue termination channel as the default context for pushes Signed-off-by: Andrew Thornton <art27@cantab.net> * missed one Signed-off-by: Andrew Thornton <art27@cantab.net> Co-authored-by: zeripath <art27@cantab.net>tokarchuk/v1.17
							parent
							
								
									4cffc46f65
								
							
						
					
					
						commit
						ac97ea573c
					
				@ -0,0 +1,21 @@ | 
				
			||||
The MIT License (MIT) | 
				
			||||
 | 
				
			||||
Copyright (c) 2017-2020 Damian Gryski <damian@gryski.com> | 
				
			||||
 | 
				
			||||
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. | 
				
			||||
@ -0,0 +1,79 @@ | 
				
			||||
package rendezvous | 
				
			||||
 | 
				
			||||
type Rendezvous struct { | 
				
			||||
	nodes map[string]int | 
				
			||||
	nstr  []string | 
				
			||||
	nhash []uint64 | 
				
			||||
	hash  Hasher | 
				
			||||
} | 
				
			||||
 | 
				
			||||
type Hasher func(s string) uint64 | 
				
			||||
 | 
				
			||||
func New(nodes []string, hash Hasher) *Rendezvous { | 
				
			||||
	r := &Rendezvous{ | 
				
			||||
		nodes: make(map[string]int, len(nodes)), | 
				
			||||
		nstr:  make([]string, len(nodes)), | 
				
			||||
		nhash: make([]uint64, len(nodes)), | 
				
			||||
		hash:  hash, | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	for i, n := range nodes { | 
				
			||||
		r.nodes[n] = i | 
				
			||||
		r.nstr[i] = n | 
				
			||||
		r.nhash[i] = hash(n) | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	return r | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (r *Rendezvous) Lookup(k string) string { | 
				
			||||
	// short-circuit if we're empty
 | 
				
			||||
	if len(r.nodes) == 0 { | 
				
			||||
		return "" | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	khash := r.hash(k) | 
				
			||||
 | 
				
			||||
	var midx int | 
				
			||||
	var mhash = xorshiftMult64(khash ^ r.nhash[0]) | 
				
			||||
 | 
				
			||||
	for i, nhash := range r.nhash[1:] { | 
				
			||||
		if h := xorshiftMult64(khash ^ nhash); h > mhash { | 
				
			||||
			midx = i + 1 | 
				
			||||
			mhash = h | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	return r.nstr[midx] | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (r *Rendezvous) Add(node string) { | 
				
			||||
	r.nodes[node] = len(r.nstr) | 
				
			||||
	r.nstr = append(r.nstr, node) | 
				
			||||
	r.nhash = append(r.nhash, r.hash(node)) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (r *Rendezvous) Remove(node string) { | 
				
			||||
	// find index of node to remove
 | 
				
			||||
	nidx := r.nodes[node] | 
				
			||||
 | 
				
			||||
	// remove from the slices
 | 
				
			||||
	l := len(r.nstr) | 
				
			||||
	r.nstr[nidx] = r.nstr[l] | 
				
			||||
	r.nstr = r.nstr[:l] | 
				
			||||
 | 
				
			||||
	r.nhash[nidx] = r.nhash[l] | 
				
			||||
	r.nhash = r.nhash[:l] | 
				
			||||
 | 
				
			||||
	// update the map
 | 
				
			||||
	delete(r.nodes, node) | 
				
			||||
	moved := r.nstr[nidx] | 
				
			||||
	r.nodes[moved] = nidx | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func xorshiftMult64(x uint64) uint64 { | 
				
			||||
	x ^= x >> 12 // a
 | 
				
			||||
	x ^= x << 25 // b
 | 
				
			||||
	x ^= x >> 27 // c
 | 
				
			||||
	return x * 2685821657736338717 | 
				
			||||
} | 
				
			||||
@ -1,22 +0,0 @@ | 
				
			||||
dist: xenial | 
				
			||||
language: go | 
				
			||||
 | 
				
			||||
services: | 
				
			||||
  - redis-server | 
				
			||||
 | 
				
			||||
go: | 
				
			||||
  - 1.12.x | 
				
			||||
  - 1.13.x | 
				
			||||
  - tip | 
				
			||||
 | 
				
			||||
matrix: | 
				
			||||
  allow_failures: | 
				
			||||
    - go: tip | 
				
			||||
 | 
				
			||||
env: | 
				
			||||
  - GO111MODULE=on | 
				
			||||
 | 
				
			||||
go_import_path: github.com/go-redis/redis | 
				
			||||
 | 
				
			||||
before_install: | 
				
			||||
  - curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin v1.21.0 | 
				
			||||
@ -1,46 +0,0 @@ | 
				
			||||
# Changelog | 
				
			||||
 | 
				
			||||
## v7.2 | 
				
			||||
 | 
				
			||||
- Existing `HMSet` is renamed to `HSet` and old deprecated `HMSet` is restored for Redis 3 users. | 
				
			||||
 | 
				
			||||
## v7.1 | 
				
			||||
 | 
				
			||||
- Existing `Cmd.String` is renamed to `Cmd.Text`. New `Cmd.String` implements `fmt.Stringer` interface. | 
				
			||||
 | 
				
			||||
## v7 | 
				
			||||
 | 
				
			||||
- *Important*. Tx.Pipeline now returns a non-transactional pipeline. Use Tx.TxPipeline for a transactional pipeline. | 
				
			||||
- WrapProcess is replaced with more convenient AddHook that has access to context.Context. | 
				
			||||
- WithContext now can not be used to create a shallow copy of the client. | 
				
			||||
- New methods ProcessContext, DoContext, and ExecContext. | 
				
			||||
- Client respects Context.Deadline when setting net.Conn deadline. | 
				
			||||
- Client listens on Context.Done while waiting for a connection from the pool and returns an error when context context is cancelled. | 
				
			||||
- Add PubSub.ChannelWithSubscriptions that sends `*Subscription` in addition to `*Message` to allow detecting reconnections. | 
				
			||||
- `time.Time` is now marshalled in RFC3339 format. `rdb.Get("foo").Time()` helper is added to parse the time. | 
				
			||||
- `SetLimiter` is removed and added `Options.Limiter` instead. | 
				
			||||
- `HMSet` is deprecated as of Redis v4. | 
				
			||||
 | 
				
			||||
## v6.15 | 
				
			||||
 | 
				
			||||
- Cluster and Ring pipelines process commands for each node in its own goroutine. | 
				
			||||
 | 
				
			||||
## 6.14 | 
				
			||||
 | 
				
			||||
- Added Options.MinIdleConns. | 
				
			||||
- Added Options.MaxConnAge. | 
				
			||||
- PoolStats.FreeConns is renamed to PoolStats.IdleConns. | 
				
			||||
- Add Client.Do to simplify creating custom commands. | 
				
			||||
- Add Cmd.String, Cmd.Int, Cmd.Int64, Cmd.Uint64, Cmd.Float64, and Cmd.Bool helpers. | 
				
			||||
- Lower memory usage. | 
				
			||||
 | 
				
			||||
## v6.13 | 
				
			||||
 | 
				
			||||
- Ring got new options called `HashReplicas` and `Hash`. It is recommended to set `HashReplicas = 1000` for better keys distribution between shards. | 
				
			||||
- Cluster client was optimized to use much less memory when reloading cluster state. | 
				
			||||
- PubSub.ReceiveMessage is re-worked to not use ReceiveTimeout so it does not lose data when timeout occurres. In most cases it is recommended to use PubSub.Channel instead. | 
				
			||||
- Dialer.KeepAlive is set to 5 minutes by default. | 
				
			||||
 | 
				
			||||
## v6.12 | 
				
			||||
 | 
				
			||||
- ClusterClient got new option called `ClusterSlots` which allows to build cluster of normal Redis Servers that don't have cluster mode enabled. See https://godoc.org/github.com/go-redis/redis#example-NewClusterClient--ManualSetup | 
				
			||||
@ -1,128 +0,0 @@ | 
				
			||||
# Redis client for Golang | 
				
			||||
 | 
				
			||||
[](https://travis-ci.org/go-redis/redis) | 
				
			||||
[](https://godoc.org/github.com/go-redis/redis) | 
				
			||||
[](https://airbrake.io) | 
				
			||||
 | 
				
			||||
Supports: | 
				
			||||
 | 
				
			||||
- Redis 3 commands except QUIT, MONITOR, SLOWLOG and SYNC. | 
				
			||||
- Automatic connection pooling with [circuit breaker](https://en.wikipedia.org/wiki/Circuit_breaker_design_pattern) support. | 
				
			||||
- [Pub/Sub](https://godoc.org/github.com/go-redis/redis#PubSub). | 
				
			||||
- [Transactions](https://godoc.org/github.com/go-redis/redis#example-Client-TxPipeline). | 
				
			||||
- [Pipeline](https://godoc.org/github.com/go-redis/redis#example-Client-Pipeline) and [TxPipeline](https://godoc.org/github.com/go-redis/redis#example-Client-TxPipeline). | 
				
			||||
- [Scripting](https://godoc.org/github.com/go-redis/redis#Script). | 
				
			||||
- [Timeouts](https://godoc.org/github.com/go-redis/redis#Options). | 
				
			||||
- [Redis Sentinel](https://godoc.org/github.com/go-redis/redis#NewFailoverClient). | 
				
			||||
- [Redis Cluster](https://godoc.org/github.com/go-redis/redis#NewClusterClient). | 
				
			||||
- [Cluster of Redis Servers](https://godoc.org/github.com/go-redis/redis#example-NewClusterClient--ManualSetup) without using cluster mode and Redis Sentinel. | 
				
			||||
- [Ring](https://godoc.org/github.com/go-redis/redis#NewRing). | 
				
			||||
- [Instrumentation](https://godoc.org/github.com/go-redis/redis#ex-package--Instrumentation). | 
				
			||||
- [Cache friendly](https://github.com/go-redis/cache). | 
				
			||||
- [Rate limiting](https://github.com/go-redis/redis_rate). | 
				
			||||
- [Distributed Locks](https://github.com/bsm/redislock). | 
				
			||||
 | 
				
			||||
API docs: https://godoc.org/github.com/go-redis/redis. | 
				
			||||
Examples: https://godoc.org/github.com/go-redis/redis#pkg-examples. | 
				
			||||
 | 
				
			||||
## Installation | 
				
			||||
 | 
				
			||||
go-redis requires a Go version with [Modules](https://github.com/golang/go/wiki/Modules) support and uses import versioning. So please make sure to initialize a Go module before installing go-redis: | 
				
			||||
 | 
				
			||||
``` shell | 
				
			||||
go mod init github.com/my/repo | 
				
			||||
go get github.com/go-redis/redis/v7 | 
				
			||||
``` | 
				
			||||
 | 
				
			||||
Import: | 
				
			||||
 | 
				
			||||
``` go | 
				
			||||
import "github.com/go-redis/redis/v7" | 
				
			||||
``` | 
				
			||||
 | 
				
			||||
## Quickstart | 
				
			||||
 | 
				
			||||
``` go | 
				
			||||
func ExampleNewClient() { | 
				
			||||
	client := redis.NewClient(&redis.Options{ | 
				
			||||
		Addr:     "localhost:6379", | 
				
			||||
		Password: "", // no password set | 
				
			||||
		DB:       0,  // use default DB | 
				
			||||
	}) | 
				
			||||
 | 
				
			||||
	pong, err := client.Ping().Result() | 
				
			||||
	fmt.Println(pong, err) | 
				
			||||
	// Output: PONG <nil> | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func ExampleClient() { | 
				
			||||
	client := redis.NewClient(&redis.Options{ | 
				
			||||
		Addr:     "localhost:6379", | 
				
			||||
		Password: "", // no password set | 
				
			||||
		DB:       0,  // use default DB | 
				
			||||
	}) | 
				
			||||
	err := client.Set("key", "value", 0).Err() | 
				
			||||
	if err != nil { | 
				
			||||
		panic(err) | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	val, err := client.Get("key").Result() | 
				
			||||
	if err != nil { | 
				
			||||
		panic(err) | 
				
			||||
	} | 
				
			||||
	fmt.Println("key", val) | 
				
			||||
 | 
				
			||||
	val2, err := client.Get("key2").Result() | 
				
			||||
	if err == redis.Nil { | 
				
			||||
		fmt.Println("key2 does not exist") | 
				
			||||
	} else if err != nil { | 
				
			||||
		panic(err) | 
				
			||||
	} else { | 
				
			||||
		fmt.Println("key2", val2) | 
				
			||||
	} | 
				
			||||
	// Output: key value | 
				
			||||
	// key2 does not exist | 
				
			||||
} | 
				
			||||
``` | 
				
			||||
 | 
				
			||||
## Howto | 
				
			||||
 | 
				
			||||
Please go through [examples](https://godoc.org/github.com/go-redis/redis#pkg-examples) to get an idea how to use this package. | 
				
			||||
 | 
				
			||||
## Look and feel | 
				
			||||
 | 
				
			||||
Some corner cases: | 
				
			||||
 | 
				
			||||
``` go | 
				
			||||
// SET key value EX 10 NX | 
				
			||||
set, err := client.SetNX("key", "value", 10*time.Second).Result() | 
				
			||||
 | 
				
			||||
// SORT list LIMIT 0 2 ASC | 
				
			||||
vals, err := client.Sort("list", &redis.Sort{Offset: 0, Count: 2, Order: "ASC"}).Result() | 
				
			||||
 | 
				
			||||
// ZRANGEBYSCORE zset -inf +inf WITHSCORES LIMIT 0 2 | 
				
			||||
vals, err := client.ZRangeByScoreWithScores("zset", &redis.ZRangeBy{ | 
				
			||||
	Min: "-inf", | 
				
			||||
	Max: "+inf", | 
				
			||||
	Offset: 0, | 
				
			||||
	Count: 2, | 
				
			||||
}).Result() | 
				
			||||
 | 
				
			||||
// ZINTERSTORE out 2 zset1 zset2 WEIGHTS 2 3 AGGREGATE SUM | 
				
			||||
vals, err := client.ZInterStore("out", &redis.ZStore{ | 
				
			||||
	Keys: []string{"zset1", "zset2"}, | 
				
			||||
	Weights: []int64{2, 3} | 
				
			||||
}).Result() | 
				
			||||
 | 
				
			||||
// EVAL "return {KEYS[1],ARGV[1]}" 1 "key" "hello" | 
				
			||||
vals, err := client.Eval("return {KEYS[1],ARGV[1]}", []string{"key"}, "hello").Result() | 
				
			||||
 | 
				
			||||
// custom command | 
				
			||||
res, err := client.Do("set", "key", "value").Result() | 
				
			||||
``` | 
				
			||||
 | 
				
			||||
## See also | 
				
			||||
 | 
				
			||||
- [Golang PostgreSQL ORM](https://github.com/go-pg/pg) | 
				
			||||
- [Golang msgpack](https://github.com/vmihailenco/msgpack) | 
				
			||||
- [Golang message task queue](https://github.com/vmihailenco/taskq) | 
				
			||||
@ -1,22 +0,0 @@ | 
				
			||||
package redis | 
				
			||||
 | 
				
			||||
import "sync/atomic" | 
				
			||||
 | 
				
			||||
func (c *ClusterClient) DBSize() *IntCmd { | 
				
			||||
	cmd := NewIntCmd("dbsize") | 
				
			||||
	var size int64 | 
				
			||||
	err := c.ForEachMaster(func(master *Client) error { | 
				
			||||
		n, err := master.DBSize().Result() | 
				
			||||
		if err != nil { | 
				
			||||
			return err | 
				
			||||
		} | 
				
			||||
		atomic.AddInt64(&size, n) | 
				
			||||
		return nil | 
				
			||||
	}) | 
				
			||||
	if err != nil { | 
				
			||||
		cmd.SetErr(err) | 
				
			||||
		return cmd | 
				
			||||
	} | 
				
			||||
	cmd.val = size | 
				
			||||
	return cmd | 
				
			||||
} | 
				
			||||
									
										
											File diff suppressed because it is too large
											Load Diff
										
									
								
							
						@ -1,15 +0,0 @@ | 
				
			||||
module github.com/go-redis/redis/v7 | 
				
			||||
 | 
				
			||||
require ( | 
				
			||||
	github.com/golang/protobuf v1.3.2 // indirect | 
				
			||||
	github.com/kr/pretty v0.1.0 // indirect | 
				
			||||
	github.com/onsi/ginkgo v1.10.1 | 
				
			||||
	github.com/onsi/gomega v1.7.0 | 
				
			||||
	golang.org/x/net v0.0.0-20190923162816-aa69164e4478 // indirect | 
				
			||||
	golang.org/x/sys v0.0.0-20191010194322-b09406accb47 // indirect | 
				
			||||
	golang.org/x/text v0.3.2 // indirect | 
				
			||||
	gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect | 
				
			||||
	gopkg.in/yaml.v2 v2.2.4 // indirect | 
				
			||||
) | 
				
			||||
 | 
				
			||||
go 1.11 | 
				
			||||
@ -1,47 +0,0 @@ | 
				
			||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= | 
				
			||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= | 
				
			||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= | 
				
			||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | 
				
			||||
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= | 
				
			||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | 
				
			||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= | 
				
			||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= | 
				
			||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= | 
				
			||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= | 
				
			||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= | 
				
			||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= | 
				
			||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= | 
				
			||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= | 
				
			||||
github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo= | 
				
			||||
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= | 
				
			||||
github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME= | 
				
			||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= | 
				
			||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | 
				
			||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA= | 
				
			||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | 
				
			||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7MvYXnkV9/iQNo1lX6g= | 
				
			||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | 
				
			||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= | 
				
			||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | 
				
			||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e h1:o3PsSEY8E4eXWkXrIP9YJALUkVZqzHJT5DOasTyn8Vs= | 
				
			||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | 
				
			||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | 
				
			||||
golang.org/x/sys v0.0.0-20191010194322-b09406accb47 h1:/XfQ9z7ib8eEJX2hdgFTZJ/ntt0swNk5oYBziWeTCvY= | 
				
			||||
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | 
				
			||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= | 
				
			||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | 
				
			||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= | 
				
			||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= | 
				
			||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | 
				
			||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= | 
				
			||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | 
				
			||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= | 
				
			||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | 
				
			||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= | 
				
			||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= | 
				
			||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= | 
				
			||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= | 
				
			||||
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= | 
				
			||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | 
				
			||||
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= | 
				
			||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | 
				
			||||
@ -1,81 +0,0 @@ | 
				
			||||
/* | 
				
			||||
Copyright 2013 Google Inc. | 
				
			||||
 | 
				
			||||
Licensed under the Apache License, Version 2.0 (the "License"); | 
				
			||||
you may not use this file except in compliance with the License. | 
				
			||||
You may obtain a copy of the License at | 
				
			||||
 | 
				
			||||
     http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||
 | 
				
			||||
Unless required by applicable law or agreed to in writing, software | 
				
			||||
distributed under the License is distributed on an "AS IS" BASIS, | 
				
			||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
				
			||||
See the License for the specific language governing permissions and | 
				
			||||
limitations under the License. | 
				
			||||
*/ | 
				
			||||
 | 
				
			||||
// Package consistenthash provides an implementation of a ring hash.
 | 
				
			||||
package consistenthash | 
				
			||||
 | 
				
			||||
import ( | 
				
			||||
	"hash/crc32" | 
				
			||||
	"sort" | 
				
			||||
	"strconv" | 
				
			||||
) | 
				
			||||
 | 
				
			||||
type Hash func(data []byte) uint32 | 
				
			||||
 | 
				
			||||
type Map struct { | 
				
			||||
	hash     Hash | 
				
			||||
	replicas int | 
				
			||||
	keys     []int // Sorted
 | 
				
			||||
	hashMap  map[int]string | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func New(replicas int, fn Hash) *Map { | 
				
			||||
	m := &Map{ | 
				
			||||
		replicas: replicas, | 
				
			||||
		hash:     fn, | 
				
			||||
		hashMap:  make(map[int]string), | 
				
			||||
	} | 
				
			||||
	if m.hash == nil { | 
				
			||||
		m.hash = crc32.ChecksumIEEE | 
				
			||||
	} | 
				
			||||
	return m | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Returns true if there are no items available.
 | 
				
			||||
func (m *Map) IsEmpty() bool { | 
				
			||||
	return len(m.keys) == 0 | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Adds some keys to the hash.
 | 
				
			||||
func (m *Map) Add(keys ...string) { | 
				
			||||
	for _, key := range keys { | 
				
			||||
		for i := 0; i < m.replicas; i++ { | 
				
			||||
			hash := int(m.hash([]byte(strconv.Itoa(i) + key))) | 
				
			||||
			m.keys = append(m.keys, hash) | 
				
			||||
			m.hashMap[hash] = key | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
	sort.Ints(m.keys) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Gets the closest item in the hash to the provided key.
 | 
				
			||||
func (m *Map) Get(key string) string { | 
				
			||||
	if m.IsEmpty() { | 
				
			||||
		return "" | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	hash := int(m.hash([]byte(key))) | 
				
			||||
 | 
				
			||||
	// Binary search for appropriate replica.
 | 
				
			||||
	idx := sort.Search(len(m.keys), func(i int) bool { return m.keys[i] >= hash }) | 
				
			||||
 | 
				
			||||
	// Means we have cycled back to the first replica.
 | 
				
			||||
	if idx == len(m.keys) { | 
				
			||||
		idx = 0 | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	return m.hashMap[m.keys[idx]] | 
				
			||||
} | 
				
			||||
@ -1,24 +0,0 @@ | 
				
			||||
package internal | 
				
			||||
 | 
				
			||||
import ( | 
				
			||||
	"math/rand" | 
				
			||||
	"time" | 
				
			||||
) | 
				
			||||
 | 
				
			||||
// Retry backoff with jitter sleep to prevent overloaded conditions during intervals
 | 
				
			||||
// https://www.awsarchitectureblog.com/2015/03/backoff.html
 | 
				
			||||
func RetryBackoff(retry int, minBackoff, maxBackoff time.Duration) time.Duration { | 
				
			||||
	if retry < 0 { | 
				
			||||
		retry = 0 | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	backoff := minBackoff << uint(retry) | 
				
			||||
	if backoff > maxBackoff || backoff < minBackoff { | 
				
			||||
		backoff = maxBackoff | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	if backoff == 0 { | 
				
			||||
		return 0 | 
				
			||||
	} | 
				
			||||
	return time.Duration(rand.Int63n(int64(backoff))) | 
				
			||||
} | 
				
			||||
@ -1,8 +0,0 @@ | 
				
			||||
package internal | 
				
			||||
 | 
				
			||||
import ( | 
				
			||||
	"log" | 
				
			||||
	"os" | 
				
			||||
) | 
				
			||||
 | 
				
			||||
var Logger = log.New(os.Stderr, "redis: ", log.LstdFlags|log.Lshortfile) | 
				
			||||
@ -1,112 +0,0 @@ | 
				
			||||
package pool | 
				
			||||
 | 
				
			||||
import ( | 
				
			||||
	"context" | 
				
			||||
	"sync" | 
				
			||||
) | 
				
			||||
 | 
				
			||||
type StickyConnPool struct { | 
				
			||||
	pool     *ConnPool | 
				
			||||
	reusable bool | 
				
			||||
 | 
				
			||||
	cn     *Conn | 
				
			||||
	closed bool | 
				
			||||
	mu     sync.Mutex | 
				
			||||
} | 
				
			||||
 | 
				
			||||
var _ Pooler = (*StickyConnPool)(nil) | 
				
			||||
 | 
				
			||||
func NewStickyConnPool(pool *ConnPool, reusable bool) *StickyConnPool { | 
				
			||||
	return &StickyConnPool{ | 
				
			||||
		pool:     pool, | 
				
			||||
		reusable: reusable, | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (p *StickyConnPool) NewConn(context.Context) (*Conn, error) { | 
				
			||||
	panic("not implemented") | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (p *StickyConnPool) CloseConn(*Conn) error { | 
				
			||||
	panic("not implemented") | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (p *StickyConnPool) Get(ctx context.Context) (*Conn, error) { | 
				
			||||
	p.mu.Lock() | 
				
			||||
	defer p.mu.Unlock() | 
				
			||||
 | 
				
			||||
	if p.closed { | 
				
			||||
		return nil, ErrClosed | 
				
			||||
	} | 
				
			||||
	if p.cn != nil { | 
				
			||||
		return p.cn, nil | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	cn, err := p.pool.Get(ctx) | 
				
			||||
	if err != nil { | 
				
			||||
		return nil, err | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	p.cn = cn | 
				
			||||
	return cn, nil | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (p *StickyConnPool) putUpstream() { | 
				
			||||
	p.pool.Put(p.cn) | 
				
			||||
	p.cn = nil | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (p *StickyConnPool) Put(cn *Conn) {} | 
				
			||||
 | 
				
			||||
func (p *StickyConnPool) removeUpstream(reason error) { | 
				
			||||
	p.pool.Remove(p.cn, reason) | 
				
			||||
	p.cn = nil | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (p *StickyConnPool) Remove(cn *Conn, reason error) { | 
				
			||||
	p.removeUpstream(reason) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (p *StickyConnPool) Len() int { | 
				
			||||
	p.mu.Lock() | 
				
			||||
	defer p.mu.Unlock() | 
				
			||||
 | 
				
			||||
	if p.cn == nil { | 
				
			||||
		return 0 | 
				
			||||
	} | 
				
			||||
	return 1 | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (p *StickyConnPool) IdleLen() int { | 
				
			||||
	p.mu.Lock() | 
				
			||||
	defer p.mu.Unlock() | 
				
			||||
 | 
				
			||||
	if p.cn == nil { | 
				
			||||
		return 1 | 
				
			||||
	} | 
				
			||||
	return 0 | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (p *StickyConnPool) Stats() *Stats { | 
				
			||||
	return nil | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (p *StickyConnPool) Close() error { | 
				
			||||
	p.mu.Lock() | 
				
			||||
	defer p.mu.Unlock() | 
				
			||||
 | 
				
			||||
	if p.closed { | 
				
			||||
		return ErrClosed | 
				
			||||
	} | 
				
			||||
	p.closed = true | 
				
			||||
 | 
				
			||||
	if p.cn != nil { | 
				
			||||
		if p.reusable { | 
				
			||||
			p.putUpstream() | 
				
			||||
		} else { | 
				
			||||
			p.removeUpstream(ErrClosed) | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	return nil | 
				
			||||
} | 
				
			||||
@ -1,56 +0,0 @@ | 
				
			||||
package internal | 
				
			||||
 | 
				
			||||
import ( | 
				
			||||
	"context" | 
				
			||||
	"time" | 
				
			||||
 | 
				
			||||
	"github.com/go-redis/redis/v7/internal/util" | 
				
			||||
) | 
				
			||||
 | 
				
			||||
func Sleep(ctx context.Context, dur time.Duration) error { | 
				
			||||
	t := time.NewTimer(dur) | 
				
			||||
	defer t.Stop() | 
				
			||||
 | 
				
			||||
	select { | 
				
			||||
	case <-t.C: | 
				
			||||
		return nil | 
				
			||||
	case <-ctx.Done(): | 
				
			||||
		return ctx.Err() | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func ToLower(s string) string { | 
				
			||||
	if isLower(s) { | 
				
			||||
		return s | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	b := make([]byte, len(s)) | 
				
			||||
	for i := range b { | 
				
			||||
		c := s[i] | 
				
			||||
		if c >= 'A' && c <= 'Z' { | 
				
			||||
			c += 'a' - 'A' | 
				
			||||
		} | 
				
			||||
		b[i] = c | 
				
			||||
	} | 
				
			||||
	return util.BytesToString(b) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func isLower(s string) bool { | 
				
			||||
	for i := 0; i < len(s); i++ { | 
				
			||||
		c := s[i] | 
				
			||||
		if c >= 'A' && c <= 'Z' { | 
				
			||||
			return false | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
	return true | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func Unwrap(err error) error { | 
				
			||||
	u, ok := err.(interface { | 
				
			||||
		Unwrap() error | 
				
			||||
	}) | 
				
			||||
	if !ok { | 
				
			||||
		return nil | 
				
			||||
	} | 
				
			||||
	return u.Unwrap() | 
				
			||||
} | 
				
			||||
@ -1,62 +0,0 @@ | 
				
			||||
package redis | 
				
			||||
 | 
				
			||||
import ( | 
				
			||||
	"crypto/sha1" | 
				
			||||
	"encoding/hex" | 
				
			||||
	"io" | 
				
			||||
	"strings" | 
				
			||||
) | 
				
			||||
 | 
				
			||||
type scripter interface { | 
				
			||||
	Eval(script string, keys []string, args ...interface{}) *Cmd | 
				
			||||
	EvalSha(sha1 string, keys []string, args ...interface{}) *Cmd | 
				
			||||
	ScriptExists(hashes ...string) *BoolSliceCmd | 
				
			||||
	ScriptLoad(script string) *StringCmd | 
				
			||||
} | 
				
			||||
 | 
				
			||||
var _ scripter = (*Client)(nil) | 
				
			||||
var _ scripter = (*Ring)(nil) | 
				
			||||
var _ scripter = (*ClusterClient)(nil) | 
				
			||||
 | 
				
			||||
type Script struct { | 
				
			||||
	src, hash string | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func NewScript(src string) *Script { | 
				
			||||
	h := sha1.New() | 
				
			||||
	_, _ = io.WriteString(h, src) | 
				
			||||
	return &Script{ | 
				
			||||
		src:  src, | 
				
			||||
		hash: hex.EncodeToString(h.Sum(nil)), | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (s *Script) Hash() string { | 
				
			||||
	return s.hash | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (s *Script) Load(c scripter) *StringCmd { | 
				
			||||
	return c.ScriptLoad(s.src) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (s *Script) Exists(c scripter) *BoolSliceCmd { | 
				
			||||
	return c.ScriptExists(s.hash) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (s *Script) Eval(c scripter, keys []string, args ...interface{}) *Cmd { | 
				
			||||
	return c.Eval(s.src, keys, args...) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (s *Script) EvalSha(c scripter, keys []string, args ...interface{}) *Cmd { | 
				
			||||
	return c.EvalSha(s.hash, keys, args...) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Run optimistically uses EVALSHA to run the script. If script does not exist
 | 
				
			||||
// it is retried using EVAL.
 | 
				
			||||
func (s *Script) Run(c scripter, keys []string, args ...interface{}) *Cmd { | 
				
			||||
	r := s.EvalSha(c, keys, args...) | 
				
			||||
	if err := r.Err(); err != nil && strings.HasPrefix(err.Error(), "NOSCRIPT ") { | 
				
			||||
		return s.Eval(c, keys, args...) | 
				
			||||
	} | 
				
			||||
	return r | 
				
			||||
} | 
				
			||||
@ -1,509 +0,0 @@ | 
				
			||||
package redis | 
				
			||||
 | 
				
			||||
import ( | 
				
			||||
	"context" | 
				
			||||
	"crypto/tls" | 
				
			||||
	"errors" | 
				
			||||
	"net" | 
				
			||||
	"strings" | 
				
			||||
	"sync" | 
				
			||||
	"time" | 
				
			||||
 | 
				
			||||
	"github.com/go-redis/redis/v7/internal" | 
				
			||||
	"github.com/go-redis/redis/v7/internal/pool" | 
				
			||||
) | 
				
			||||
 | 
				
			||||
//------------------------------------------------------------------------------
 | 
				
			||||
 | 
				
			||||
// FailoverOptions are used to configure a failover client and should
 | 
				
			||||
// be passed to NewFailoverClient.
 | 
				
			||||
type FailoverOptions struct { | 
				
			||||
	// The master name.
 | 
				
			||||
	MasterName string | 
				
			||||
	// A seed list of host:port addresses of sentinel nodes.
 | 
				
			||||
	SentinelAddrs    []string | 
				
			||||
	SentinelUsername string | 
				
			||||
	SentinelPassword string | 
				
			||||
 | 
				
			||||
	// Following options are copied from Options struct.
 | 
				
			||||
 | 
				
			||||
	Dialer    func(ctx context.Context, network, addr string) (net.Conn, error) | 
				
			||||
	OnConnect func(*Conn) error | 
				
			||||
 | 
				
			||||
	Username string | 
				
			||||
	Password string | 
				
			||||
	DB       int | 
				
			||||
 | 
				
			||||
	MaxRetries      int | 
				
			||||
	MinRetryBackoff time.Duration | 
				
			||||
	MaxRetryBackoff time.Duration | 
				
			||||
 | 
				
			||||
	DialTimeout  time.Duration | 
				
			||||
	ReadTimeout  time.Duration | 
				
			||||
	WriteTimeout time.Duration | 
				
			||||
 | 
				
			||||
	PoolSize           int | 
				
			||||
	MinIdleConns       int | 
				
			||||
	MaxConnAge         time.Duration | 
				
			||||
	PoolTimeout        time.Duration | 
				
			||||
	IdleTimeout        time.Duration | 
				
			||||
	IdleCheckFrequency time.Duration | 
				
			||||
 | 
				
			||||
	TLSConfig *tls.Config | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (opt *FailoverOptions) options() *Options { | 
				
			||||
	return &Options{ | 
				
			||||
		Addr:      "FailoverClient", | 
				
			||||
		Dialer:    opt.Dialer, | 
				
			||||
		OnConnect: opt.OnConnect, | 
				
			||||
 | 
				
			||||
		DB:       opt.DB, | 
				
			||||
		Username: opt.Username, | 
				
			||||
		Password: opt.Password, | 
				
			||||
 | 
				
			||||
		MaxRetries:      opt.MaxRetries, | 
				
			||||
		MinRetryBackoff: opt.MinRetryBackoff, | 
				
			||||
		MaxRetryBackoff: opt.MaxRetryBackoff, | 
				
			||||
 | 
				
			||||
		DialTimeout:  opt.DialTimeout, | 
				
			||||
		ReadTimeout:  opt.ReadTimeout, | 
				
			||||
		WriteTimeout: opt.WriteTimeout, | 
				
			||||
 | 
				
			||||
		PoolSize:           opt.PoolSize, | 
				
			||||
		PoolTimeout:        opt.PoolTimeout, | 
				
			||||
		IdleTimeout:        opt.IdleTimeout, | 
				
			||||
		IdleCheckFrequency: opt.IdleCheckFrequency, | 
				
			||||
		MinIdleConns:       opt.MinIdleConns, | 
				
			||||
		MaxConnAge:         opt.MaxConnAge, | 
				
			||||
 | 
				
			||||
		TLSConfig: opt.TLSConfig, | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// NewFailoverClient returns a Redis client that uses Redis Sentinel
 | 
				
			||||
// for automatic failover. It's safe for concurrent use by multiple
 | 
				
			||||
// goroutines.
 | 
				
			||||
func NewFailoverClient(failoverOpt *FailoverOptions) *Client { | 
				
			||||
	opt := failoverOpt.options() | 
				
			||||
	opt.init() | 
				
			||||
 | 
				
			||||
	failover := &sentinelFailover{ | 
				
			||||
		masterName:    failoverOpt.MasterName, | 
				
			||||
		sentinelAddrs: failoverOpt.SentinelAddrs, | 
				
			||||
		username:      failoverOpt.SentinelUsername, | 
				
			||||
		password:      failoverOpt.SentinelPassword, | 
				
			||||
 | 
				
			||||
		opt: opt, | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	c := Client{ | 
				
			||||
		baseClient: newBaseClient(opt, failover.Pool()), | 
				
			||||
		ctx:        context.Background(), | 
				
			||||
	} | 
				
			||||
	c.cmdable = c.Process | 
				
			||||
	c.onClose = failover.Close | 
				
			||||
 | 
				
			||||
	return &c | 
				
			||||
} | 
				
			||||
 | 
				
			||||
//------------------------------------------------------------------------------
 | 
				
			||||
 | 
				
			||||
type SentinelClient struct { | 
				
			||||
	*baseClient | 
				
			||||
	ctx context.Context | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func NewSentinelClient(opt *Options) *SentinelClient { | 
				
			||||
	opt.init() | 
				
			||||
	c := &SentinelClient{ | 
				
			||||
		baseClient: &baseClient{ | 
				
			||||
			opt:      opt, | 
				
			||||
			connPool: newConnPool(opt), | 
				
			||||
		}, | 
				
			||||
		ctx: context.Background(), | 
				
			||||
	} | 
				
			||||
	return c | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (c *SentinelClient) Context() context.Context { | 
				
			||||
	return c.ctx | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (c *SentinelClient) WithContext(ctx context.Context) *SentinelClient { | 
				
			||||
	if ctx == nil { | 
				
			||||
		panic("nil context") | 
				
			||||
	} | 
				
			||||
	clone := *c | 
				
			||||
	clone.ctx = ctx | 
				
			||||
	return &clone | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (c *SentinelClient) Process(cmd Cmder) error { | 
				
			||||
	return c.ProcessContext(c.ctx, cmd) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (c *SentinelClient) ProcessContext(ctx context.Context, cmd Cmder) error { | 
				
			||||
	return c.baseClient.process(ctx, cmd) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (c *SentinelClient) pubSub() *PubSub { | 
				
			||||
	pubsub := &PubSub{ | 
				
			||||
		opt: c.opt, | 
				
			||||
 | 
				
			||||
		newConn: func(channels []string) (*pool.Conn, error) { | 
				
			||||
			return c.newConn(context.TODO()) | 
				
			||||
		}, | 
				
			||||
		closeConn: c.connPool.CloseConn, | 
				
			||||
	} | 
				
			||||
	pubsub.init() | 
				
			||||
	return pubsub | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Ping is used to test if a connection is still alive, or to
 | 
				
			||||
// measure latency.
 | 
				
			||||
func (c *SentinelClient) Ping() *StringCmd { | 
				
			||||
	cmd := NewStringCmd("ping") | 
				
			||||
	_ = c.Process(cmd) | 
				
			||||
	return cmd | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Subscribe subscribes the client to the specified channels.
 | 
				
			||||
// Channels can be omitted to create empty subscription.
 | 
				
			||||
func (c *SentinelClient) Subscribe(channels ...string) *PubSub { | 
				
			||||
	pubsub := c.pubSub() | 
				
			||||
	if len(channels) > 0 { | 
				
			||||
		_ = pubsub.Subscribe(channels...) | 
				
			||||
	} | 
				
			||||
	return pubsub | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// PSubscribe subscribes the client to the given patterns.
 | 
				
			||||
// Patterns can be omitted to create empty subscription.
 | 
				
			||||
func (c *SentinelClient) PSubscribe(channels ...string) *PubSub { | 
				
			||||
	pubsub := c.pubSub() | 
				
			||||
	if len(channels) > 0 { | 
				
			||||
		_ = pubsub.PSubscribe(channels...) | 
				
			||||
	} | 
				
			||||
	return pubsub | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (c *SentinelClient) GetMasterAddrByName(name string) *StringSliceCmd { | 
				
			||||
	cmd := NewStringSliceCmd("sentinel", "get-master-addr-by-name", name) | 
				
			||||
	_ = c.Process(cmd) | 
				
			||||
	return cmd | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (c *SentinelClient) Sentinels(name string) *SliceCmd { | 
				
			||||
	cmd := NewSliceCmd("sentinel", "sentinels", name) | 
				
			||||
	_ = c.Process(cmd) | 
				
			||||
	return cmd | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Failover forces a failover as if the master was not reachable, and without
 | 
				
			||||
// asking for agreement to other Sentinels.
 | 
				
			||||
func (c *SentinelClient) Failover(name string) *StatusCmd { | 
				
			||||
	cmd := NewStatusCmd("sentinel", "failover", name) | 
				
			||||
	_ = c.Process(cmd) | 
				
			||||
	return cmd | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Reset resets all the masters with matching name. The pattern argument is a
 | 
				
			||||
// glob-style pattern. The reset process clears any previous state in a master
 | 
				
			||||
// (including a failover in progress), and removes every slave and sentinel
 | 
				
			||||
// already discovered and associated with the master.
 | 
				
			||||
func (c *SentinelClient) Reset(pattern string) *IntCmd { | 
				
			||||
	cmd := NewIntCmd("sentinel", "reset", pattern) | 
				
			||||
	_ = c.Process(cmd) | 
				
			||||
	return cmd | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// FlushConfig forces Sentinel to rewrite its configuration on disk, including
 | 
				
			||||
// the current Sentinel state.
 | 
				
			||||
func (c *SentinelClient) FlushConfig() *StatusCmd { | 
				
			||||
	cmd := NewStatusCmd("sentinel", "flushconfig") | 
				
			||||
	_ = c.Process(cmd) | 
				
			||||
	return cmd | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Master shows the state and info of the specified master.
 | 
				
			||||
func (c *SentinelClient) Master(name string) *StringStringMapCmd { | 
				
			||||
	cmd := NewStringStringMapCmd("sentinel", "master", name) | 
				
			||||
	_ = c.Process(cmd) | 
				
			||||
	return cmd | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Masters shows a list of monitored masters and their state.
 | 
				
			||||
func (c *SentinelClient) Masters() *SliceCmd { | 
				
			||||
	cmd := NewSliceCmd("sentinel", "masters") | 
				
			||||
	_ = c.Process(cmd) | 
				
			||||
	return cmd | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Slaves shows a list of slaves for the specified master and their state.
 | 
				
			||||
func (c *SentinelClient) Slaves(name string) *SliceCmd { | 
				
			||||
	cmd := NewSliceCmd("sentinel", "slaves", name) | 
				
			||||
	_ = c.Process(cmd) | 
				
			||||
	return cmd | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// CkQuorum checks if the current Sentinel configuration is able to reach the
 | 
				
			||||
// quorum needed to failover a master, and the majority needed to authorize the
 | 
				
			||||
// failover. This command should be used in monitoring systems to check if a
 | 
				
			||||
// Sentinel deployment is ok.
 | 
				
			||||
func (c *SentinelClient) CkQuorum(name string) *StringCmd { | 
				
			||||
	cmd := NewStringCmd("sentinel", "ckquorum", name) | 
				
			||||
	_ = c.Process(cmd) | 
				
			||||
	return cmd | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Monitor tells the Sentinel to start monitoring a new master with the specified
 | 
				
			||||
// name, ip, port, and quorum.
 | 
				
			||||
func (c *SentinelClient) Monitor(name, ip, port, quorum string) *StringCmd { | 
				
			||||
	cmd := NewStringCmd("sentinel", "monitor", name, ip, port, quorum) | 
				
			||||
	_ = c.Process(cmd) | 
				
			||||
	return cmd | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Set is used in order to change configuration parameters of a specific master.
 | 
				
			||||
func (c *SentinelClient) Set(name, option, value string) *StringCmd { | 
				
			||||
	cmd := NewStringCmd("sentinel", "set", name, option, value) | 
				
			||||
	_ = c.Process(cmd) | 
				
			||||
	return cmd | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Remove is used in order to remove the specified master: the master will no
 | 
				
			||||
// longer be monitored, and will totally be removed from the internal state of
 | 
				
			||||
// the Sentinel.
 | 
				
			||||
func (c *SentinelClient) Remove(name string) *StringCmd { | 
				
			||||
	cmd := NewStringCmd("sentinel", "remove", name) | 
				
			||||
	_ = c.Process(cmd) | 
				
			||||
	return cmd | 
				
			||||
} | 
				
			||||
 | 
				
			||||
type sentinelFailover struct { | 
				
			||||
	sentinelAddrs []string | 
				
			||||
 | 
				
			||||
	opt      *Options | 
				
			||||
	username string | 
				
			||||
	password string | 
				
			||||
 | 
				
			||||
	pool     *pool.ConnPool | 
				
			||||
	poolOnce sync.Once | 
				
			||||
 | 
				
			||||
	mu          sync.RWMutex | 
				
			||||
	masterName  string | 
				
			||||
	_masterAddr string | 
				
			||||
	sentinel    *SentinelClient | 
				
			||||
	pubsub      *PubSub | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (c *sentinelFailover) Close() error { | 
				
			||||
	c.mu.Lock() | 
				
			||||
	defer c.mu.Unlock() | 
				
			||||
	if c.sentinel != nil { | 
				
			||||
		return c.closeSentinel() | 
				
			||||
	} | 
				
			||||
	return nil | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (c *sentinelFailover) closeSentinel() error { | 
				
			||||
	firstErr := c.pubsub.Close() | 
				
			||||
	c.pubsub = nil | 
				
			||||
 | 
				
			||||
	err := c.sentinel.Close() | 
				
			||||
	if err != nil && firstErr == nil { | 
				
			||||
		firstErr = err | 
				
			||||
	} | 
				
			||||
	c.sentinel = nil | 
				
			||||
 | 
				
			||||
	return firstErr | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (c *sentinelFailover) Pool() *pool.ConnPool { | 
				
			||||
	c.poolOnce.Do(func() { | 
				
			||||
		opt := *c.opt | 
				
			||||
		opt.Dialer = c.dial | 
				
			||||
		c.pool = newConnPool(&opt) | 
				
			||||
	}) | 
				
			||||
	return c.pool | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (c *sentinelFailover) dial(ctx context.Context, network, _ string) (net.Conn, error) { | 
				
			||||
	addr, err := c.MasterAddr() | 
				
			||||
	if err != nil { | 
				
			||||
		return nil, err | 
				
			||||
	} | 
				
			||||
	if c.opt.Dialer != nil { | 
				
			||||
		return c.opt.Dialer(ctx, network, addr) | 
				
			||||
	} | 
				
			||||
	return net.DialTimeout("tcp", addr, c.opt.DialTimeout) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (c *sentinelFailover) MasterAddr() (string, error) { | 
				
			||||
	addr, err := c.masterAddr() | 
				
			||||
	if err != nil { | 
				
			||||
		return "", err | 
				
			||||
	} | 
				
			||||
	c.switchMaster(addr) | 
				
			||||
	return addr, nil | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (c *sentinelFailover) masterAddr() (string, error) { | 
				
			||||
	c.mu.RLock() | 
				
			||||
	sentinel := c.sentinel | 
				
			||||
	c.mu.RUnlock() | 
				
			||||
 | 
				
			||||
	if sentinel != nil { | 
				
			||||
		addr := c.getMasterAddr(sentinel) | 
				
			||||
		if addr != "" { | 
				
			||||
			return addr, nil | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	c.mu.Lock() | 
				
			||||
	defer c.mu.Unlock() | 
				
			||||
 | 
				
			||||
	if c.sentinel != nil { | 
				
			||||
		addr := c.getMasterAddr(c.sentinel) | 
				
			||||
		if addr != "" { | 
				
			||||
			return addr, nil | 
				
			||||
		} | 
				
			||||
		_ = c.closeSentinel() | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	for i, sentinelAddr := range c.sentinelAddrs { | 
				
			||||
		sentinel := NewSentinelClient(&Options{ | 
				
			||||
			Addr:   sentinelAddr, | 
				
			||||
			Dialer: c.opt.Dialer, | 
				
			||||
 | 
				
			||||
			Username: c.username, | 
				
			||||
			Password: c.password, | 
				
			||||
 | 
				
			||||
			MaxRetries: c.opt.MaxRetries, | 
				
			||||
 | 
				
			||||
			DialTimeout:  c.opt.DialTimeout, | 
				
			||||
			ReadTimeout:  c.opt.ReadTimeout, | 
				
			||||
			WriteTimeout: c.opt.WriteTimeout, | 
				
			||||
 | 
				
			||||
			PoolSize:           c.opt.PoolSize, | 
				
			||||
			PoolTimeout:        c.opt.PoolTimeout, | 
				
			||||
			IdleTimeout:        c.opt.IdleTimeout, | 
				
			||||
			IdleCheckFrequency: c.opt.IdleCheckFrequency, | 
				
			||||
 | 
				
			||||
			TLSConfig: c.opt.TLSConfig, | 
				
			||||
		}) | 
				
			||||
 | 
				
			||||
		masterAddr, err := sentinel.GetMasterAddrByName(c.masterName).Result() | 
				
			||||
		if err != nil { | 
				
			||||
			internal.Logger.Printf("sentinel: GetMasterAddrByName master=%q failed: %s", | 
				
			||||
				c.masterName, err) | 
				
			||||
			_ = sentinel.Close() | 
				
			||||
			continue | 
				
			||||
		} | 
				
			||||
 | 
				
			||||
		// Push working sentinel to the top.
 | 
				
			||||
		c.sentinelAddrs[0], c.sentinelAddrs[i] = c.sentinelAddrs[i], c.sentinelAddrs[0] | 
				
			||||
		c.setSentinel(sentinel) | 
				
			||||
 | 
				
			||||
		addr := net.JoinHostPort(masterAddr[0], masterAddr[1]) | 
				
			||||
		return addr, nil | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	return "", errors.New("redis: all sentinels are unreachable") | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (c *sentinelFailover) getMasterAddr(sentinel *SentinelClient) string { | 
				
			||||
	addr, err := sentinel.GetMasterAddrByName(c.masterName).Result() | 
				
			||||
	if err != nil { | 
				
			||||
		internal.Logger.Printf("sentinel: GetMasterAddrByName name=%q failed: %s", | 
				
			||||
			c.masterName, err) | 
				
			||||
		return "" | 
				
			||||
	} | 
				
			||||
	return net.JoinHostPort(addr[0], addr[1]) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (c *sentinelFailover) switchMaster(addr string) { | 
				
			||||
	c.mu.RLock() | 
				
			||||
	masterAddr := c._masterAddr | 
				
			||||
	c.mu.RUnlock() | 
				
			||||
	if masterAddr == addr { | 
				
			||||
		return | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	c.mu.Lock() | 
				
			||||
	defer c.mu.Unlock() | 
				
			||||
 | 
				
			||||
	if c._masterAddr == addr { | 
				
			||||
		return | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	internal.Logger.Printf("sentinel: new master=%q addr=%q", | 
				
			||||
		c.masterName, addr) | 
				
			||||
	_ = c.Pool().Filter(func(cn *pool.Conn) bool { | 
				
			||||
		return cn.RemoteAddr().String() != addr | 
				
			||||
	}) | 
				
			||||
	c._masterAddr = addr | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (c *sentinelFailover) setSentinel(sentinel *SentinelClient) { | 
				
			||||
	if c.sentinel != nil { | 
				
			||||
		panic("not reached") | 
				
			||||
	} | 
				
			||||
	c.sentinel = sentinel | 
				
			||||
	c.discoverSentinels() | 
				
			||||
 | 
				
			||||
	c.pubsub = sentinel.Subscribe("+switch-master") | 
				
			||||
	go c.listen(c.pubsub) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (c *sentinelFailover) discoverSentinels() { | 
				
			||||
	sentinels, err := c.sentinel.Sentinels(c.masterName).Result() | 
				
			||||
	if err != nil { | 
				
			||||
		internal.Logger.Printf("sentinel: Sentinels master=%q failed: %s", c.masterName, err) | 
				
			||||
		return | 
				
			||||
	} | 
				
			||||
	for _, sentinel := range sentinels { | 
				
			||||
		vals := sentinel.([]interface{}) | 
				
			||||
		for i := 0; i < len(vals); i += 2 { | 
				
			||||
			key := vals[i].(string) | 
				
			||||
			if key == "name" { | 
				
			||||
				sentinelAddr := vals[i+1].(string) | 
				
			||||
				if !contains(c.sentinelAddrs, sentinelAddr) { | 
				
			||||
					internal.Logger.Printf("sentinel: discovered new sentinel=%q for master=%q", | 
				
			||||
						sentinelAddr, c.masterName) | 
				
			||||
					c.sentinelAddrs = append(c.sentinelAddrs, sentinelAddr) | 
				
			||||
				} | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (c *sentinelFailover) listen(pubsub *PubSub) { | 
				
			||||
	ch := pubsub.Channel() | 
				
			||||
	for { | 
				
			||||
		msg, ok := <-ch | 
				
			||||
		if !ok { | 
				
			||||
			break | 
				
			||||
		} | 
				
			||||
 | 
				
			||||
		if msg.Channel == "+switch-master" { | 
				
			||||
			parts := strings.Split(msg.Payload, " ") | 
				
			||||
			if parts[0] != c.masterName { | 
				
			||||
				internal.Logger.Printf("sentinel: ignore addr for master=%q", parts[0]) | 
				
			||||
				continue | 
				
			||||
			} | 
				
			||||
			addr := net.JoinHostPort(parts[3], parts[4]) | 
				
			||||
			c.switchMaster(addr) | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func contains(slice []string, str string) bool { | 
				
			||||
	for _, s := range slice { | 
				
			||||
		if s == str { | 
				
			||||
			return true | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
	return false | 
				
			||||
} | 
				
			||||
@ -1,2 +1,3 @@ | 
				
			||||
*.rdb | 
				
			||||
testdata/*/ | 
				
			||||
.idea/ | 
				
			||||
@ -0,0 +1,4 @@ | 
				
			||||
semi: false | 
				
			||||
singleQuote: true | 
				
			||||
proseWrap: always | 
				
			||||
printWidth: 100 | 
				
			||||
@ -0,0 +1,20 @@ | 
				
			||||
dist: xenial | 
				
			||||
language: go | 
				
			||||
 | 
				
			||||
services: | 
				
			||||
  - redis-server | 
				
			||||
 | 
				
			||||
go: | 
				
			||||
  - 1.14.x | 
				
			||||
  - 1.15.x | 
				
			||||
  - tip | 
				
			||||
 | 
				
			||||
matrix: | 
				
			||||
  allow_failures: | 
				
			||||
    - go: tip | 
				
			||||
 | 
				
			||||
go_import_path: github.com/go-redis/redis | 
				
			||||
 | 
				
			||||
before_install: | 
				
			||||
  - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- | 
				
			||||
    -b $(go env GOPATH)/bin v1.32.2 | 
				
			||||
@ -0,0 +1,5 @@ | 
				
			||||
# Changelog | 
				
			||||
 | 
				
			||||
> :heart: [**Uptrace.dev** - distributed traces, logs, and errors in one place](https://uptrace.dev) | 
				
			||||
 | 
				
			||||
See https://redis.uptrace.dev/changelog/ | 
				
			||||
@ -0,0 +1,159 @@ | 
				
			||||
# Redis client for Golang | 
				
			||||
 | 
				
			||||
[](https://travis-ci.org/go-redis/redis) | 
				
			||||
[](https://pkg.go.dev/github.com/go-redis/redis/v8?tab=doc) | 
				
			||||
[](https://redis.uptrace.dev/) | 
				
			||||
[](https://discord.gg/rWtp5Aj) | 
				
			||||
 | 
				
			||||
> :heart: [**Uptrace.dev** - distributed traces, logs, and errors in one place](https://uptrace.dev) | 
				
			||||
 | 
				
			||||
- Join [Discord](https://discord.gg/rWtp5Aj) to ask questions. | 
				
			||||
- [Documentation](https://redis.uptrace.dev) | 
				
			||||
- [Reference](https://pkg.go.dev/github.com/go-redis/redis/v8?tab=doc) | 
				
			||||
- [Examples](https://pkg.go.dev/github.com/go-redis/redis/v8?tab=doc#pkg-examples) | 
				
			||||
- [RealWorld example app](https://github.com/uptrace/go-treemux-realworld-example-app) | 
				
			||||
 | 
				
			||||
## Ecosystem | 
				
			||||
 | 
				
			||||
- [Redis Mock](https://github.com/go-redis/redismock). | 
				
			||||
- [Distributed Locks](https://github.com/bsm/redislock). | 
				
			||||
- [Redis Cache](https://github.com/go-redis/cache). | 
				
			||||
- [Rate limiting](https://github.com/go-redis/redis_rate). | 
				
			||||
 | 
				
			||||
## Features | 
				
			||||
 | 
				
			||||
- Redis 3 commands except QUIT, MONITOR, and SYNC. | 
				
			||||
- Automatic connection pooling with | 
				
			||||
  [circuit breaker](https://en.wikipedia.org/wiki/Circuit_breaker_design_pattern) support. | 
				
			||||
- [Pub/Sub](https://pkg.go.dev/github.com/go-redis/redis/v8?tab=doc#PubSub). | 
				
			||||
- [Transactions](https://pkg.go.dev/github.com/go-redis/redis/v8?tab=doc#example-Client-TxPipeline). | 
				
			||||
- [Pipeline](https://pkg.go.dev/github.com/go-redis/redis/v8?tab=doc#example-Client-Pipeline) and | 
				
			||||
  [TxPipeline](https://pkg.go.dev/github.com/go-redis/redis/v8?tab=doc#example-Client-TxPipeline). | 
				
			||||
- [Scripting](https://pkg.go.dev/github.com/go-redis/redis/v8?tab=doc#Script). | 
				
			||||
- [Timeouts](https://pkg.go.dev/github.com/go-redis/redis/v8?tab=doc#Options). | 
				
			||||
- [Redis Sentinel](https://pkg.go.dev/github.com/go-redis/redis/v8?tab=doc#NewFailoverClient). | 
				
			||||
- [Redis Cluster](https://pkg.go.dev/github.com/go-redis/redis/v8?tab=doc#NewClusterClient). | 
				
			||||
- [Cluster of Redis Servers](https://pkg.go.dev/github.com/go-redis/redis/v8?tab=doc#example-NewClusterClient--ManualSetup) | 
				
			||||
  without using cluster mode and Redis Sentinel. | 
				
			||||
- [Ring](https://pkg.go.dev/github.com/go-redis/redis/v8?tab=doc#NewRing). | 
				
			||||
- [Instrumentation](https://pkg.go.dev/github.com/go-redis/redis/v8?tab=doc#ex-package--Instrumentation). | 
				
			||||
 | 
				
			||||
## Installation | 
				
			||||
 | 
				
			||||
go-redis supports 2 last Go versions and requires a Go version with | 
				
			||||
[modules](https://github.com/golang/go/wiki/Modules) support. So make sure to initialize a Go | 
				
			||||
module: | 
				
			||||
 | 
				
			||||
```shell | 
				
			||||
go mod init github.com/my/repo | 
				
			||||
``` | 
				
			||||
 | 
				
			||||
And then install go-redis/v8 (note _v8_ in the import; omitting it is a popular mistake): | 
				
			||||
 | 
				
			||||
```shell | 
				
			||||
go get github.com/go-redis/redis/v8 | 
				
			||||
``` | 
				
			||||
 | 
				
			||||
## Quickstart | 
				
			||||
 | 
				
			||||
```go | 
				
			||||
import ( | 
				
			||||
    "context" | 
				
			||||
    "github.com/go-redis/redis/v8" | 
				
			||||
) | 
				
			||||
 | 
				
			||||
var ctx = context.Background() | 
				
			||||
 | 
				
			||||
func ExampleClient() { | 
				
			||||
    rdb := redis.NewClient(&redis.Options{ | 
				
			||||
        Addr:     "localhost:6379", | 
				
			||||
        Password: "", // no password set | 
				
			||||
        DB:       0,  // use default DB | 
				
			||||
    }) | 
				
			||||
 | 
				
			||||
    err := rdb.Set(ctx, "key", "value", 0).Err() | 
				
			||||
    if err != nil { | 
				
			||||
        panic(err) | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    val, err := rdb.Get(ctx, "key").Result() | 
				
			||||
    if err != nil { | 
				
			||||
        panic(err) | 
				
			||||
    } | 
				
			||||
    fmt.Println("key", val) | 
				
			||||
 | 
				
			||||
    val2, err := rdb.Get(ctx, "key2").Result() | 
				
			||||
    if err == redis.Nil { | 
				
			||||
        fmt.Println("key2 does not exist") | 
				
			||||
    } else if err != nil { | 
				
			||||
        panic(err) | 
				
			||||
    } else { | 
				
			||||
        fmt.Println("key2", val2) | 
				
			||||
    } | 
				
			||||
    // Output: key value | 
				
			||||
    // key2 does not exist | 
				
			||||
} | 
				
			||||
``` | 
				
			||||
 | 
				
			||||
## Look and feel | 
				
			||||
 | 
				
			||||
Some corner cases: | 
				
			||||
 | 
				
			||||
```go | 
				
			||||
// SET key value EX 10 NX | 
				
			||||
set, err := rdb.SetNX(ctx, "key", "value", 10*time.Second).Result() | 
				
			||||
 | 
				
			||||
// SET key value keepttl NX | 
				
			||||
set, err := rdb.SetNX(ctx, "key", "value", redis.KeepTTL).Result() | 
				
			||||
 | 
				
			||||
// SORT list LIMIT 0 2 ASC | 
				
			||||
vals, err := rdb.Sort(ctx, "list", &redis.Sort{Offset: 0, Count: 2, Order: "ASC"}).Result() | 
				
			||||
 | 
				
			||||
// ZRANGEBYSCORE zset -inf +inf WITHSCORES LIMIT 0 2 | 
				
			||||
vals, err := rdb.ZRangeByScoreWithScores(ctx, "zset", &redis.ZRangeBy{ | 
				
			||||
    Min: "-inf", | 
				
			||||
    Max: "+inf", | 
				
			||||
    Offset: 0, | 
				
			||||
    Count: 2, | 
				
			||||
}).Result() | 
				
			||||
 | 
				
			||||
// ZINTERSTORE out 2 zset1 zset2 WEIGHTS 2 3 AGGREGATE SUM | 
				
			||||
vals, err := rdb.ZInterStore(ctx, "out", &redis.ZStore{ | 
				
			||||
    Keys: []string{"zset1", "zset2"}, | 
				
			||||
    Weights: []int64{2, 3} | 
				
			||||
}).Result() | 
				
			||||
 | 
				
			||||
// EVAL "return {KEYS[1],ARGV[1]}" 1 "key" "hello" | 
				
			||||
vals, err := rdb.Eval(ctx, "return {KEYS[1],ARGV[1]}", []string{"key"}, "hello").Result() | 
				
			||||
 | 
				
			||||
// custom command | 
				
			||||
res, err := rdb.Do(ctx, "set", "key", "value").Result() | 
				
			||||
``` | 
				
			||||
## Run the test | 
				
			||||
go-redis will start a redis-server and run the test cases.  | 
				
			||||
 | 
				
			||||
The paths of redis-server bin file and redis config file are definded in `main_test.go`: | 
				
			||||
``` | 
				
			||||
var ( | 
				
			||||
	redisServerBin, _  = filepath.Abs(filepath.Join("testdata", "redis", "src", "redis-server")) | 
				
			||||
	redisServerConf, _ = filepath.Abs(filepath.Join("testdata", "redis", "redis.conf")) | 
				
			||||
) | 
				
			||||
``` | 
				
			||||
 | 
				
			||||
For local testing, you can change the variables to refer to your local files, or create a soft link to the corresponding folder for redis-server and copy the config file to `testdata/redis/`: | 
				
			||||
``` | 
				
			||||
ln -s /usr/bin/redis-server ./go-redis/testdata/redis/src | 
				
			||||
cp ./go-redis/testdata/redis.conf ./go-redis/testdata/redis/ | 
				
			||||
``` | 
				
			||||
 | 
				
			||||
Lastly, run: | 
				
			||||
``` | 
				
			||||
go test | 
				
			||||
``` | 
				
			||||
 | 
				
			||||
## See also | 
				
			||||
 | 
				
			||||
- [Fast and flexible HTTP router](https://github.com/vmihailenco/treemux) | 
				
			||||
- [Golang PostgreSQL ORM](https://github.com/go-pg/pg) | 
				
			||||
- [Golang msgpack](https://github.com/vmihailenco/msgpack) | 
				
			||||
- [Golang message task queue](https://github.com/vmihailenco/taskq) | 
				
			||||
									
										
											File diff suppressed because it is too large
											Load Diff
										
									
								
							
						@ -0,0 +1,25 @@ | 
				
			||||
package redis | 
				
			||||
 | 
				
			||||
import ( | 
				
			||||
	"context" | 
				
			||||
	"sync/atomic" | 
				
			||||
) | 
				
			||||
 | 
				
			||||
func (c *ClusterClient) DBSize(ctx context.Context) *IntCmd { | 
				
			||||
	cmd := NewIntCmd(ctx, "dbsize") | 
				
			||||
	var size int64 | 
				
			||||
	err := c.ForEachMaster(ctx, func(ctx context.Context, master *Client) error { | 
				
			||||
		n, err := master.DBSize(ctx).Result() | 
				
			||||
		if err != nil { | 
				
			||||
			return err | 
				
			||||
		} | 
				
			||||
		atomic.AddInt64(&size, n) | 
				
			||||
		return nil | 
				
			||||
	}) | 
				
			||||
	if err != nil { | 
				
			||||
		cmd.SetErr(err) | 
				
			||||
		return cmd | 
				
			||||
	} | 
				
			||||
	cmd.val = size | 
				
			||||
	return cmd | 
				
			||||
} | 
				
			||||
									
										
											File diff suppressed because it is too large
											Load Diff
										
									
								
							
						
									
										
											File diff suppressed because it is too large
											Load Diff
										
									
								
							
						@ -0,0 +1,11 @@ | 
				
			||||
module github.com/go-redis/redis/v8 | 
				
			||||
 | 
				
			||||
go 1.13 | 
				
			||||
 | 
				
			||||
require ( | 
				
			||||
	github.com/cespare/xxhash/v2 v2.1.1 | 
				
			||||
	github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f | 
				
			||||
	github.com/onsi/ginkgo v1.15.0 | 
				
			||||
	github.com/onsi/gomega v1.10.5 | 
				
			||||
	go.opentelemetry.io/otel v0.16.0 | 
				
			||||
) | 
				
			||||
@ -0,0 +1,97 @@ | 
				
			||||
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= | 
				
			||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= | 
				
			||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= | 
				
			||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | 
				
			||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= | 
				
			||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= | 
				
			||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= | 
				
			||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= | 
				
			||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= | 
				
			||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | 
				
			||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= | 
				
			||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= | 
				
			||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= | 
				
			||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= | 
				
			||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= | 
				
			||||
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= | 
				
			||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= | 
				
			||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | 
				
			||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | 
				
			||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | 
				
			||||
github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= | 
				
			||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | 
				
			||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= | 
				
			||||
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= | 
				
			||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= | 
				
			||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= | 
				
			||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= | 
				
			||||
github.com/onsi/ginkgo v1.14.2 h1:8mVmC9kjFFmA8H4pKMUhcblgifdkOIXPvbhN1T36q1M= | 
				
			||||
github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= | 
				
			||||
github.com/onsi/ginkgo v1.15.0/go.mod h1:hF8qUzuuC8DJGygJH3726JnCZX4MYbRB8yFfISqnKUg= | 
				
			||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= | 
				
			||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= | 
				
			||||
github.com/onsi/gomega v1.10.4 h1:NiTx7EEvBzu9sFOD1zORteLSt3o8gnlvZZwSE9TnY9U= | 
				
			||||
github.com/onsi/gomega v1.10.4/go.mod h1:g/HbgYopi++010VEqkFgJHKC09uJiW9UkXvMUuKHUCQ= | 
				
			||||
github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7mt48= | 
				
			||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | 
				
			||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | 
				
			||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | 
				
			||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= | 
				
			||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | 
				
			||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= | 
				
			||||
go.opentelemetry.io/otel v0.16.0 h1:uIWEbdeb4vpKPGITLsRVUS44L5oDbDUCZxn8lkxhmgw= | 
				
			||||
go.opentelemetry.io/otel v0.16.0/go.mod h1:e4GKElweB8W2gWUqbghw0B8t5MCTccc9212eNHnOHwA= | 
				
			||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | 
				
			||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | 
				
			||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | 
				
			||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= | 
				
			||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | 
				
			||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | 
				
			||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | 
				
			||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= | 
				
			||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= | 
				
			||||
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb h1:eBmm0M9fYhWpKZLjQUUKka/LtIxf46G4fxeEz5KJr9U= | 
				
			||||
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= | 
				
			||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | 
				
			||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | 
				
			||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | 
				
			||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | 
				
			||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | 
				
			||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | 
				
			||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | 
				
			||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | 
				
			||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | 
				
			||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | 
				
			||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | 
				
			||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= | 
				
			||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | 
				
			||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | 
				
			||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | 
				
			||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= | 
				
			||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= | 
				
			||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | 
				
			||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | 
				
			||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | 
				
			||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= | 
				
			||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | 
				
			||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | 
				
			||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= | 
				
			||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | 
				
			||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | 
				
			||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= | 
				
			||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= | 
				
			||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= | 
				
			||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= | 
				
			||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= | 
				
			||||
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= | 
				
			||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= | 
				
			||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= | 
				
			||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | 
				
			||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= | 
				
			||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= | 
				
			||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= | 
				
			||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | 
				
			||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= | 
				
			||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | 
				
			||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= | 
				
			||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | 
				
			||||
@ -0,0 +1,56 @@ | 
				
			||||
package internal | 
				
			||||
 | 
				
			||||
import ( | 
				
			||||
	"fmt" | 
				
			||||
	"strconv" | 
				
			||||
	"time" | 
				
			||||
) | 
				
			||||
 | 
				
			||||
func AppendArg(b []byte, v interface{}) []byte { | 
				
			||||
	switch v := v.(type) { | 
				
			||||
	case nil: | 
				
			||||
		return append(b, "<nil>"...) | 
				
			||||
	case string: | 
				
			||||
		return appendUTF8String(b, Bytes(v)) | 
				
			||||
	case []byte: | 
				
			||||
		return appendUTF8String(b, v) | 
				
			||||
	case int: | 
				
			||||
		return strconv.AppendInt(b, int64(v), 10) | 
				
			||||
	case int8: | 
				
			||||
		return strconv.AppendInt(b, int64(v), 10) | 
				
			||||
	case int16: | 
				
			||||
		return strconv.AppendInt(b, int64(v), 10) | 
				
			||||
	case int32: | 
				
			||||
		return strconv.AppendInt(b, int64(v), 10) | 
				
			||||
	case int64: | 
				
			||||
		return strconv.AppendInt(b, v, 10) | 
				
			||||
	case uint: | 
				
			||||
		return strconv.AppendUint(b, uint64(v), 10) | 
				
			||||
	case uint8: | 
				
			||||
		return strconv.AppendUint(b, uint64(v), 10) | 
				
			||||
	case uint16: | 
				
			||||
		return strconv.AppendUint(b, uint64(v), 10) | 
				
			||||
	case uint32: | 
				
			||||
		return strconv.AppendUint(b, uint64(v), 10) | 
				
			||||
	case uint64: | 
				
			||||
		return strconv.AppendUint(b, v, 10) | 
				
			||||
	case float32: | 
				
			||||
		return strconv.AppendFloat(b, float64(v), 'f', -1, 64) | 
				
			||||
	case float64: | 
				
			||||
		return strconv.AppendFloat(b, v, 'f', -1, 64) | 
				
			||||
	case bool: | 
				
			||||
		if v { | 
				
			||||
			return append(b, "true"...) | 
				
			||||
		} | 
				
			||||
		return append(b, "false"...) | 
				
			||||
	case time.Time: | 
				
			||||
		return v.AppendFormat(b, time.RFC3339Nano) | 
				
			||||
	default: | 
				
			||||
		return append(b, fmt.Sprint(v)...) | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func appendUTF8String(dst []byte, src []byte) []byte { | 
				
			||||
	dst = append(dst, src...) | 
				
			||||
	return dst | 
				
			||||
} | 
				
			||||
@ -1,8 +1,9 @@ | 
				
			||||
package hashtag | 
				
			||||
 | 
				
			||||
import ( | 
				
			||||
	"math/rand" | 
				
			||||
	"strings" | 
				
			||||
 | 
				
			||||
	"github.com/go-redis/redis/v8/internal/rand" | 
				
			||||
) | 
				
			||||
 | 
				
			||||
const slotNumber = 16384 | 
				
			||||
@ -0,0 +1,151 @@ | 
				
			||||
package hscan | 
				
			||||
 | 
				
			||||
import ( | 
				
			||||
	"errors" | 
				
			||||
	"fmt" | 
				
			||||
	"reflect" | 
				
			||||
	"strconv" | 
				
			||||
) | 
				
			||||
 | 
				
			||||
// decoderFunc represents decoding functions for default built-in types.
 | 
				
			||||
type decoderFunc func(reflect.Value, string) error | 
				
			||||
 | 
				
			||||
var ( | 
				
			||||
	// List of built-in decoders indexed by their numeric constant values (eg: reflect.Bool = 1).
 | 
				
			||||
	decoders = []decoderFunc{ | 
				
			||||
		reflect.Bool:          decodeBool, | 
				
			||||
		reflect.Int:           decodeInt, | 
				
			||||
		reflect.Int8:          decodeInt, | 
				
			||||
		reflect.Int16:         decodeInt, | 
				
			||||
		reflect.Int32:         decodeInt, | 
				
			||||
		reflect.Int64:         decodeInt, | 
				
			||||
		reflect.Uint:          decodeUint, | 
				
			||||
		reflect.Uint8:         decodeUint, | 
				
			||||
		reflect.Uint16:        decodeUint, | 
				
			||||
		reflect.Uint32:        decodeUint, | 
				
			||||
		reflect.Uint64:        decodeUint, | 
				
			||||
		reflect.Float32:       decodeFloat, | 
				
			||||
		reflect.Float64:       decodeFloat, | 
				
			||||
		reflect.Complex64:     decodeUnsupported, | 
				
			||||
		reflect.Complex128:    decodeUnsupported, | 
				
			||||
		reflect.Array:         decodeUnsupported, | 
				
			||||
		reflect.Chan:          decodeUnsupported, | 
				
			||||
		reflect.Func:          decodeUnsupported, | 
				
			||||
		reflect.Interface:     decodeUnsupported, | 
				
			||||
		reflect.Map:           decodeUnsupported, | 
				
			||||
		reflect.Ptr:           decodeUnsupported, | 
				
			||||
		reflect.Slice:         decodeSlice, | 
				
			||||
		reflect.String:        decodeString, | 
				
			||||
		reflect.Struct:        decodeUnsupported, | 
				
			||||
		reflect.UnsafePointer: decodeUnsupported, | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	// Global map of struct field specs that is populated once for every new
 | 
				
			||||
	// struct type that is scanned. This caches the field types and the corresponding
 | 
				
			||||
	// decoder functions to avoid iterating through struct fields on subsequent scans.
 | 
				
			||||
	globalStructMap = newStructMap() | 
				
			||||
) | 
				
			||||
 | 
				
			||||
func Struct(dst interface{}) (StructValue, error) { | 
				
			||||
	v := reflect.ValueOf(dst) | 
				
			||||
 | 
				
			||||
	// The dstination to scan into should be a struct pointer.
 | 
				
			||||
	if v.Kind() != reflect.Ptr || v.IsNil() { | 
				
			||||
		return StructValue{}, fmt.Errorf("redis.Scan(non-pointer %T)", dst) | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	v = v.Elem() | 
				
			||||
	if v.Kind() != reflect.Struct { | 
				
			||||
		return StructValue{}, fmt.Errorf("redis.Scan(non-struct %T)", dst) | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	return StructValue{ | 
				
			||||
		spec:  globalStructMap.get(v.Type()), | 
				
			||||
		value: v, | 
				
			||||
	}, nil | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Scan scans the results from a key-value Redis map result set to a destination struct.
 | 
				
			||||
// The Redis keys are matched to the struct's field with the `redis` tag.
 | 
				
			||||
func Scan(dst interface{}, keys []interface{}, vals []interface{}) error { | 
				
			||||
	if len(keys) != len(vals) { | 
				
			||||
		return errors.New("args should have the same number of keys and vals") | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	strct, err := Struct(dst) | 
				
			||||
	if err != nil { | 
				
			||||
		return err | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	// Iterate through the (key, value) sequence.
 | 
				
			||||
	for i := 0; i < len(vals); i++ { | 
				
			||||
		key, ok := keys[i].(string) | 
				
			||||
		if !ok { | 
				
			||||
			continue | 
				
			||||
		} | 
				
			||||
 | 
				
			||||
		val, ok := vals[i].(string) | 
				
			||||
		if !ok { | 
				
			||||
			continue | 
				
			||||
		} | 
				
			||||
 | 
				
			||||
		if err := strct.Scan(key, val); err != nil { | 
				
			||||
			return err | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	return nil | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func decodeBool(f reflect.Value, s string) error { | 
				
			||||
	b, err := strconv.ParseBool(s) | 
				
			||||
	if err != nil { | 
				
			||||
		return err | 
				
			||||
	} | 
				
			||||
	f.SetBool(b) | 
				
			||||
	return nil | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func decodeInt(f reflect.Value, s string) error { | 
				
			||||
	v, err := strconv.ParseInt(s, 10, 0) | 
				
			||||
	if err != nil { | 
				
			||||
		return err | 
				
			||||
	} | 
				
			||||
	f.SetInt(v) | 
				
			||||
	return nil | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func decodeUint(f reflect.Value, s string) error { | 
				
			||||
	v, err := strconv.ParseUint(s, 10, 0) | 
				
			||||
	if err != nil { | 
				
			||||
		return err | 
				
			||||
	} | 
				
			||||
	f.SetUint(v) | 
				
			||||
	return nil | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func decodeFloat(f reflect.Value, s string) error { | 
				
			||||
	v, err := strconv.ParseFloat(s, 0) | 
				
			||||
	if err != nil { | 
				
			||||
		return err | 
				
			||||
	} | 
				
			||||
	f.SetFloat(v) | 
				
			||||
	return nil | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func decodeString(f reflect.Value, s string) error { | 
				
			||||
	f.SetString(s) | 
				
			||||
	return nil | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func decodeSlice(f reflect.Value, s string) error { | 
				
			||||
	// []byte slice ([]uint8).
 | 
				
			||||
	if f.Type().Elem().Kind() == reflect.Uint8 { | 
				
			||||
		f.SetBytes([]byte(s)) | 
				
			||||
	} | 
				
			||||
	return nil | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func decodeUnsupported(v reflect.Value, s string) error { | 
				
			||||
	return fmt.Errorf("redis.Scan(unsupported %s)", v.Type()) | 
				
			||||
} | 
				
			||||
@ -0,0 +1,87 @@ | 
				
			||||
package hscan | 
				
			||||
 | 
				
			||||
import ( | 
				
			||||
	"reflect" | 
				
			||||
	"strings" | 
				
			||||
	"sync" | 
				
			||||
) | 
				
			||||
 | 
				
			||||
// structMap contains the map of struct fields for target structs
 | 
				
			||||
// indexed by the struct type.
 | 
				
			||||
type structMap struct { | 
				
			||||
	m sync.Map | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func newStructMap() *structMap { | 
				
			||||
	return new(structMap) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (s *structMap) get(t reflect.Type) *structSpec { | 
				
			||||
	if v, ok := s.m.Load(t); ok { | 
				
			||||
		return v.(*structSpec) | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	spec := newStructSpec(t, "redis") | 
				
			||||
	s.m.Store(t, spec) | 
				
			||||
	return spec | 
				
			||||
} | 
				
			||||
 | 
				
			||||
//------------------------------------------------------------------------------
 | 
				
			||||
 | 
				
			||||
// structSpec contains the list of all fields in a target struct.
 | 
				
			||||
type structSpec struct { | 
				
			||||
	m map[string]*structField | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (s *structSpec) set(tag string, sf *structField) { | 
				
			||||
	s.m[tag] = sf | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func newStructSpec(t reflect.Type, fieldTag string) *structSpec { | 
				
			||||
	out := &structSpec{ | 
				
			||||
		m: make(map[string]*structField), | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	num := t.NumField() | 
				
			||||
	for i := 0; i < num; i++ { | 
				
			||||
		f := t.Field(i) | 
				
			||||
 | 
				
			||||
		tag := f.Tag.Get(fieldTag) | 
				
			||||
		if tag == "" || tag == "-" { | 
				
			||||
			continue | 
				
			||||
		} | 
				
			||||
 | 
				
			||||
		tag = strings.Split(tag, ",")[0] | 
				
			||||
		if tag == "" { | 
				
			||||
			continue | 
				
			||||
		} | 
				
			||||
 | 
				
			||||
		// Use the built-in decoder.
 | 
				
			||||
		out.set(tag, &structField{index: i, fn: decoders[f.Type.Kind()]}) | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	return out | 
				
			||||
} | 
				
			||||
 | 
				
			||||
//------------------------------------------------------------------------------
 | 
				
			||||
 | 
				
			||||
// structField represents a single field in a target struct.
 | 
				
			||||
type structField struct { | 
				
			||||
	index int | 
				
			||||
	fn    decoderFunc | 
				
			||||
} | 
				
			||||
 | 
				
			||||
//------------------------------------------------------------------------------
 | 
				
			||||
 | 
				
			||||
type StructValue struct { | 
				
			||||
	spec  *structSpec | 
				
			||||
	value reflect.Value | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (s StructValue) Scan(key string, value string) error { | 
				
			||||
	field, ok := s.spec.m[key] | 
				
			||||
	if !ok { | 
				
			||||
		return nil | 
				
			||||
	} | 
				
			||||
	return field.fn(s.value.Field(field.index), value) | 
				
			||||
} | 
				
			||||
@ -0,0 +1,33 @@ | 
				
			||||
package internal | 
				
			||||
 | 
				
			||||
import ( | 
				
			||||
	"context" | 
				
			||||
 | 
				
			||||
	"go.opentelemetry.io/otel" | 
				
			||||
	"go.opentelemetry.io/otel/metric" | 
				
			||||
) | 
				
			||||
 | 
				
			||||
var ( | 
				
			||||
	// WritesCounter is a count of write commands performed.
 | 
				
			||||
	WritesCounter metric.Int64Counter | 
				
			||||
	// NewConnectionsCounter is a count of new connections.
 | 
				
			||||
	NewConnectionsCounter metric.Int64Counter | 
				
			||||
) | 
				
			||||
 | 
				
			||||
func init() { | 
				
			||||
	defer func() { | 
				
			||||
		if r := recover(); r != nil { | 
				
			||||
			Logger.Printf(context.Background(), "Error creating meter github.com/go-redis/redis for Instruments", r) | 
				
			||||
		} | 
				
			||||
	}() | 
				
			||||
 | 
				
			||||
	meter := metric.Must(otel.Meter("github.com/go-redis/redis")) | 
				
			||||
 | 
				
			||||
	WritesCounter = meter.NewInt64Counter("redis.writes", | 
				
			||||
		metric.WithDescription("the number of writes initiated"), | 
				
			||||
	) | 
				
			||||
 | 
				
			||||
	NewConnectionsCounter = meter.NewInt64Counter("redis.new_connections", | 
				
			||||
		metric.WithDescription("the number of connections created"), | 
				
			||||
	) | 
				
			||||
} | 
				
			||||
@ -0,0 +1,29 @@ | 
				
			||||
package internal | 
				
			||||
 | 
				
			||||
import ( | 
				
			||||
	"time" | 
				
			||||
 | 
				
			||||
	"github.com/go-redis/redis/v8/internal/rand" | 
				
			||||
) | 
				
			||||
 | 
				
			||||
func RetryBackoff(retry int, minBackoff, maxBackoff time.Duration) time.Duration { | 
				
			||||
	if retry < 0 { | 
				
			||||
		panic("not reached") | 
				
			||||
	} | 
				
			||||
	if minBackoff == 0 { | 
				
			||||
		return 0 | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	d := minBackoff << uint(retry) | 
				
			||||
	if d < minBackoff { | 
				
			||||
		return maxBackoff | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	d = minBackoff + time.Duration(rand.Int63n(int64(d))) | 
				
			||||
 | 
				
			||||
	if d > maxBackoff || d < minBackoff { | 
				
			||||
		d = maxBackoff | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	return d | 
				
			||||
} | 
				
			||||
@ -0,0 +1,24 @@ | 
				
			||||
package internal | 
				
			||||
 | 
				
			||||
import ( | 
				
			||||
	"context" | 
				
			||||
	"fmt" | 
				
			||||
	"log" | 
				
			||||
	"os" | 
				
			||||
) | 
				
			||||
 | 
				
			||||
type Logging interface { | 
				
			||||
	Printf(ctx context.Context, format string, v ...interface{}) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
type logger struct { | 
				
			||||
	log *log.Logger | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (l *logger) Printf(ctx context.Context, format string, v ...interface{}) { | 
				
			||||
	_ = l.log.Output(2, fmt.Sprintf(format, v...)) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
var Logger Logging = &logger{ | 
				
			||||
	log: log.New(os.Stderr, "redis: ", log.LstdFlags|log.Lshortfile), | 
				
			||||
} | 
				
			||||
@ -0,0 +1,58 @@ | 
				
			||||
package pool | 
				
			||||
 | 
				
			||||
import "context" | 
				
			||||
 | 
				
			||||
type SingleConnPool struct { | 
				
			||||
	pool      Pooler | 
				
			||||
	cn        *Conn | 
				
			||||
	stickyErr error | 
				
			||||
} | 
				
			||||
 | 
				
			||||
var _ Pooler = (*SingleConnPool)(nil) | 
				
			||||
 | 
				
			||||
func NewSingleConnPool(pool Pooler, cn *Conn) *SingleConnPool { | 
				
			||||
	return &SingleConnPool{ | 
				
			||||
		pool: pool, | 
				
			||||
		cn:   cn, | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (p *SingleConnPool) NewConn(ctx context.Context) (*Conn, error) { | 
				
			||||
	return p.pool.NewConn(ctx) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (p *SingleConnPool) CloseConn(cn *Conn) error { | 
				
			||||
	return p.pool.CloseConn(cn) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (p *SingleConnPool) Get(ctx context.Context) (*Conn, error) { | 
				
			||||
	if p.stickyErr != nil { | 
				
			||||
		return nil, p.stickyErr | 
				
			||||
	} | 
				
			||||
	return p.cn, nil | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (p *SingleConnPool) Put(ctx context.Context, cn *Conn) {} | 
				
			||||
 | 
				
			||||
func (p *SingleConnPool) Remove(ctx context.Context, cn *Conn, reason error) { | 
				
			||||
	p.cn = nil | 
				
			||||
	p.stickyErr = reason | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (p *SingleConnPool) Close() error { | 
				
			||||
	p.cn = nil | 
				
			||||
	p.stickyErr = ErrClosed | 
				
			||||
	return nil | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (p *SingleConnPool) Len() int { | 
				
			||||
	return 0 | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (p *SingleConnPool) IdleLen() int { | 
				
			||||
	return 0 | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (p *SingleConnPool) Stats() *Stats { | 
				
			||||
	return &Stats{} | 
				
			||||
} | 
				
			||||
@ -0,0 +1,45 @@ | 
				
			||||
package rand | 
				
			||||
 | 
				
			||||
import ( | 
				
			||||
	"math/rand" | 
				
			||||
	"sync" | 
				
			||||
) | 
				
			||||
 | 
				
			||||
// Int returns a non-negative pseudo-random int.
 | 
				
			||||
func Int() int { return pseudo.Int() } | 
				
			||||
 | 
				
			||||
// Intn returns, as an int, a non-negative pseudo-random number in [0,n).
 | 
				
			||||
// It panics if n <= 0.
 | 
				
			||||
func Intn(n int) int { return pseudo.Intn(n) } | 
				
			||||
 | 
				
			||||
// Int63n returns, as an int64, a non-negative pseudo-random number in [0,n).
 | 
				
			||||
// It panics if n <= 0.
 | 
				
			||||
func Int63n(n int64) int64 { return pseudo.Int63n(n) } | 
				
			||||
 | 
				
			||||
// Perm returns, as a slice of n ints, a pseudo-random permutation of the integers [0,n).
 | 
				
			||||
func Perm(n int) []int { return pseudo.Perm(n) } | 
				
			||||
 | 
				
			||||
// Seed uses the provided seed value to initialize the default Source to a
 | 
				
			||||
// deterministic state. If Seed is not called, the generator behaves as if
 | 
				
			||||
// seeded by Seed(1).
 | 
				
			||||
func Seed(n int64) { pseudo.Seed(n) } | 
				
			||||
 | 
				
			||||
var pseudo = rand.New(&source{src: rand.NewSource(1)}) | 
				
			||||
 | 
				
			||||
type source struct { | 
				
			||||
	src rand.Source | 
				
			||||
	mu  sync.Mutex | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (s *source) Int63() int64 { | 
				
			||||
	s.mu.Lock() | 
				
			||||
	n := s.src.Int63() | 
				
			||||
	s.mu.Unlock() | 
				
			||||
	return n | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (s *source) Seed(seed int64) { | 
				
			||||
	s.mu.Lock() | 
				
			||||
	s.src.Seed(seed) | 
				
			||||
	s.mu.Unlock() | 
				
			||||
} | 
				
			||||
@ -0,0 +1,11 @@ | 
				
			||||
// +build appengine
 | 
				
			||||
 | 
				
			||||
package internal | 
				
			||||
 | 
				
			||||
func String(b []byte) string { | 
				
			||||
	return string(b) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func Bytes(s string) []byte { | 
				
			||||
	return []byte(s) | 
				
			||||
} | 
				
			||||
@ -0,0 +1,20 @@ | 
				
			||||
// +build !appengine
 | 
				
			||||
 | 
				
			||||
package internal | 
				
			||||
 | 
				
			||||
import "unsafe" | 
				
			||||
 | 
				
			||||
// String converts byte slice to string.
 | 
				
			||||
func String(b []byte) string { | 
				
			||||
	return *(*string)(unsafe.Pointer(&b)) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Bytes converts string to byte slice.
 | 
				
			||||
func Bytes(s string) []byte { | 
				
			||||
	return *(*[]byte)(unsafe.Pointer( | 
				
			||||
		&struct { | 
				
			||||
			string | 
				
			||||
			Cap int | 
				
			||||
		}{s, len(s)}, | 
				
			||||
	)) | 
				
			||||
} | 
				
			||||
@ -0,0 +1,73 @@ | 
				
			||||
package internal | 
				
			||||
 | 
				
			||||
import ( | 
				
			||||
	"context" | 
				
			||||
	"time" | 
				
			||||
 | 
				
			||||
	"github.com/go-redis/redis/v8/internal/proto" | 
				
			||||
	"github.com/go-redis/redis/v8/internal/util" | 
				
			||||
	"go.opentelemetry.io/otel" | 
				
			||||
	"go.opentelemetry.io/otel/trace" | 
				
			||||
) | 
				
			||||
 | 
				
			||||
func Sleep(ctx context.Context, dur time.Duration) error { | 
				
			||||
	return WithSpan(ctx, "time.Sleep", func(ctx context.Context, span trace.Span) error { | 
				
			||||
		t := time.NewTimer(dur) | 
				
			||||
		defer t.Stop() | 
				
			||||
 | 
				
			||||
		select { | 
				
			||||
		case <-t.C: | 
				
			||||
			return nil | 
				
			||||
		case <-ctx.Done(): | 
				
			||||
			return ctx.Err() | 
				
			||||
		} | 
				
			||||
	}) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func ToLower(s string) string { | 
				
			||||
	if isLower(s) { | 
				
			||||
		return s | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	b := make([]byte, len(s)) | 
				
			||||
	for i := range b { | 
				
			||||
		c := s[i] | 
				
			||||
		if c >= 'A' && c <= 'Z' { | 
				
			||||
			c += 'a' - 'A' | 
				
			||||
		} | 
				
			||||
		b[i] = c | 
				
			||||
	} | 
				
			||||
	return util.BytesToString(b) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func isLower(s string) bool { | 
				
			||||
	for i := 0; i < len(s); i++ { | 
				
			||||
		c := s[i] | 
				
			||||
		if c >= 'A' && c <= 'Z' { | 
				
			||||
			return false | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
	return true | 
				
			||||
} | 
				
			||||
 | 
				
			||||
//------------------------------------------------------------------------------
 | 
				
			||||
 | 
				
			||||
var tracer = otel.Tracer("github.com/go-redis/redis") | 
				
			||||
 | 
				
			||||
func WithSpan(ctx context.Context, name string, fn func(context.Context, trace.Span) error) error { | 
				
			||||
	if span := trace.SpanFromContext(ctx); !span.IsRecording() { | 
				
			||||
		return fn(ctx, span) | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	ctx, span := tracer.Start(ctx, name) | 
				
			||||
	defer span.End() | 
				
			||||
 | 
				
			||||
	return fn(ctx, span) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func RecordError(ctx context.Context, span trace.Span, err error) error { | 
				
			||||
	if err != proto.Nil { | 
				
			||||
		span.RecordError(err) | 
				
			||||
	} | 
				
			||||
	return err | 
				
			||||
} | 
				
			||||
						
							
								
									
								
							
							
								
									330
								
							
							vendor/github.com/go-redis/redis/v7/redis.go → vendor/github.com/go-redis/redis/v8/redis.go
							
								generated
							
							
								vendored
							
						
						
					
					
								
									330
								
							
							vendor/github.com/go-redis/redis/v7/redis.go → vendor/github.com/go-redis/redis/v8/redis.go
							
								generated
							
							
								vendored
							
						
						
							
								
									
								
							
							
								
									349
								
							
							vendor/github.com/go-redis/redis/v7/ring.go → vendor/github.com/go-redis/redis/v8/ring.go
							
								generated
							
							
								vendored
							
						
						
					
					
								
									349
								
							
							vendor/github.com/go-redis/redis/v7/ring.go → vendor/github.com/go-redis/redis/v8/ring.go
							
								generated
							
							
								vendored
							
						@ -0,0 +1,65 @@ | 
				
			||||
package redis | 
				
			||||
 | 
				
			||||
import ( | 
				
			||||
	"context" | 
				
			||||
	"crypto/sha1" | 
				
			||||
	"encoding/hex" | 
				
			||||
	"io" | 
				
			||||
	"strings" | 
				
			||||
) | 
				
			||||
 | 
				
			||||
type Scripter interface { | 
				
			||||
	Eval(ctx context.Context, script string, keys []string, args ...interface{}) *Cmd | 
				
			||||
	EvalSha(ctx context.Context, sha1 string, keys []string, args ...interface{}) *Cmd | 
				
			||||
	ScriptExists(ctx context.Context, hashes ...string) *BoolSliceCmd | 
				
			||||
	ScriptLoad(ctx context.Context, script string) *StringCmd | 
				
			||||
} | 
				
			||||
 | 
				
			||||
var ( | 
				
			||||
	_ Scripter = (*Client)(nil) | 
				
			||||
	_ Scripter = (*Ring)(nil) | 
				
			||||
	_ Scripter = (*ClusterClient)(nil) | 
				
			||||
) | 
				
			||||
 | 
				
			||||
type Script struct { | 
				
			||||
	src, hash string | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func NewScript(src string) *Script { | 
				
			||||
	h := sha1.New() | 
				
			||||
	_, _ = io.WriteString(h, src) | 
				
			||||
	return &Script{ | 
				
			||||
		src:  src, | 
				
			||||
		hash: hex.EncodeToString(h.Sum(nil)), | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (s *Script) Hash() string { | 
				
			||||
	return s.hash | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (s *Script) Load(ctx context.Context, c Scripter) *StringCmd { | 
				
			||||
	return c.ScriptLoad(ctx, s.src) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (s *Script) Exists(ctx context.Context, c Scripter) *BoolSliceCmd { | 
				
			||||
	return c.ScriptExists(ctx, s.hash) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (s *Script) Eval(ctx context.Context, c Scripter, keys []string, args ...interface{}) *Cmd { | 
				
			||||
	return c.Eval(ctx, s.src, keys, args...) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (s *Script) EvalSha(ctx context.Context, c Scripter, keys []string, args ...interface{}) *Cmd { | 
				
			||||
	return c.EvalSha(ctx, s.hash, keys, args...) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Run optimistically uses EVALSHA to run the script. If script does not exist
 | 
				
			||||
// it is retried using EVAL.
 | 
				
			||||
func (s *Script) Run(ctx context.Context, c Scripter, keys []string, args ...interface{}) *Cmd { | 
				
			||||
	r := s.EvalSha(ctx, c, keys, args...) | 
				
			||||
	if err := r.Err(); err != nil && strings.HasPrefix(err.Error(), "NOSCRIPT ") { | 
				
			||||
		return s.Eval(ctx, c, keys, args...) | 
				
			||||
	} | 
				
			||||
	return r | 
				
			||||
} | 
				
			||||
@ -0,0 +1,738 @@ | 
				
			||||
package redis | 
				
			||||
 | 
				
			||||
import ( | 
				
			||||
	"context" | 
				
			||||
	"crypto/tls" | 
				
			||||
	"errors" | 
				
			||||
	"net" | 
				
			||||
	"strings" | 
				
			||||
	"sync" | 
				
			||||
	"time" | 
				
			||||
 | 
				
			||||
	"github.com/go-redis/redis/v8/internal" | 
				
			||||
	"github.com/go-redis/redis/v8/internal/pool" | 
				
			||||
	"github.com/go-redis/redis/v8/internal/rand" | 
				
			||||
) | 
				
			||||
 | 
				
			||||
//------------------------------------------------------------------------------
 | 
				
			||||
 | 
				
			||||
// FailoverOptions are used to configure a failover client and should
 | 
				
			||||
// be passed to NewFailoverClient.
 | 
				
			||||
type FailoverOptions struct { | 
				
			||||
	// The master name.
 | 
				
			||||
	MasterName string | 
				
			||||
	// A seed list of host:port addresses of sentinel nodes.
 | 
				
			||||
	SentinelAddrs []string | 
				
			||||
	// Sentinel password from "requirepass <password>" (if enabled) in Sentinel configuration
 | 
				
			||||
	SentinelPassword string | 
				
			||||
 | 
				
			||||
	// Allows routing read-only commands to the closest master or slave node.
 | 
				
			||||
	// This option only works with NewFailoverClusterClient.
 | 
				
			||||
	RouteByLatency bool | 
				
			||||
	// Allows routing read-only commands to the random master or slave node.
 | 
				
			||||
	// This option only works with NewFailoverClusterClient.
 | 
				
			||||
	RouteRandomly bool | 
				
			||||
 | 
				
			||||
	// Route all commands to slave read-only nodes.
 | 
				
			||||
	SlaveOnly bool | 
				
			||||
 | 
				
			||||
	// Following options are copied from Options struct.
 | 
				
			||||
 | 
				
			||||
	Dialer    func(ctx context.Context, network, addr string) (net.Conn, error) | 
				
			||||
	OnConnect func(ctx context.Context, cn *Conn) error | 
				
			||||
 | 
				
			||||
	Username string | 
				
			||||
	Password string | 
				
			||||
	DB       int | 
				
			||||
 | 
				
			||||
	MaxRetries      int | 
				
			||||
	MinRetryBackoff time.Duration | 
				
			||||
	MaxRetryBackoff time.Duration | 
				
			||||
 | 
				
			||||
	DialTimeout  time.Duration | 
				
			||||
	ReadTimeout  time.Duration | 
				
			||||
	WriteTimeout time.Duration | 
				
			||||
 | 
				
			||||
	PoolSize           int | 
				
			||||
	MinIdleConns       int | 
				
			||||
	MaxConnAge         time.Duration | 
				
			||||
	PoolTimeout        time.Duration | 
				
			||||
	IdleTimeout        time.Duration | 
				
			||||
	IdleCheckFrequency time.Duration | 
				
			||||
 | 
				
			||||
	TLSConfig *tls.Config | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (opt *FailoverOptions) clientOptions() *Options { | 
				
			||||
	return &Options{ | 
				
			||||
		Addr: "FailoverClient", | 
				
			||||
 | 
				
			||||
		Dialer:    opt.Dialer, | 
				
			||||
		OnConnect: opt.OnConnect, | 
				
			||||
 | 
				
			||||
		DB:       opt.DB, | 
				
			||||
		Username: opt.Username, | 
				
			||||
		Password: opt.Password, | 
				
			||||
 | 
				
			||||
		MaxRetries:      opt.MaxRetries, | 
				
			||||
		MinRetryBackoff: opt.MinRetryBackoff, | 
				
			||||
		MaxRetryBackoff: opt.MaxRetryBackoff, | 
				
			||||
 | 
				
			||||
		DialTimeout:  opt.DialTimeout, | 
				
			||||
		ReadTimeout:  opt.ReadTimeout, | 
				
			||||
		WriteTimeout: opt.WriteTimeout, | 
				
			||||
 | 
				
			||||
		PoolSize:           opt.PoolSize, | 
				
			||||
		PoolTimeout:        opt.PoolTimeout, | 
				
			||||
		IdleTimeout:        opt.IdleTimeout, | 
				
			||||
		IdleCheckFrequency: opt.IdleCheckFrequency, | 
				
			||||
		MinIdleConns:       opt.MinIdleConns, | 
				
			||||
		MaxConnAge:         opt.MaxConnAge, | 
				
			||||
 | 
				
			||||
		TLSConfig: opt.TLSConfig, | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (opt *FailoverOptions) sentinelOptions(addr string) *Options { | 
				
			||||
	return &Options{ | 
				
			||||
		Addr: addr, | 
				
			||||
 | 
				
			||||
		Dialer:    opt.Dialer, | 
				
			||||
		OnConnect: opt.OnConnect, | 
				
			||||
 | 
				
			||||
		DB:       0, | 
				
			||||
		Password: opt.SentinelPassword, | 
				
			||||
 | 
				
			||||
		MaxRetries:      opt.MaxRetries, | 
				
			||||
		MinRetryBackoff: opt.MinRetryBackoff, | 
				
			||||
		MaxRetryBackoff: opt.MaxRetryBackoff, | 
				
			||||
 | 
				
			||||
		DialTimeout:  opt.DialTimeout, | 
				
			||||
		ReadTimeout:  opt.ReadTimeout, | 
				
			||||
		WriteTimeout: opt.WriteTimeout, | 
				
			||||
 | 
				
			||||
		PoolSize:           opt.PoolSize, | 
				
			||||
		PoolTimeout:        opt.PoolTimeout, | 
				
			||||
		IdleTimeout:        opt.IdleTimeout, | 
				
			||||
		IdleCheckFrequency: opt.IdleCheckFrequency, | 
				
			||||
		MinIdleConns:       opt.MinIdleConns, | 
				
			||||
		MaxConnAge:         opt.MaxConnAge, | 
				
			||||
 | 
				
			||||
		TLSConfig: opt.TLSConfig, | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (opt *FailoverOptions) clusterOptions() *ClusterOptions { | 
				
			||||
	return &ClusterOptions{ | 
				
			||||
		Dialer:    opt.Dialer, | 
				
			||||
		OnConnect: opt.OnConnect, | 
				
			||||
 | 
				
			||||
		Username: opt.Username, | 
				
			||||
		Password: opt.Password, | 
				
			||||
 | 
				
			||||
		MaxRedirects: opt.MaxRetries, | 
				
			||||
 | 
				
			||||
		RouteByLatency: opt.RouteByLatency, | 
				
			||||
		RouteRandomly:  opt.RouteRandomly, | 
				
			||||
 | 
				
			||||
		MinRetryBackoff: opt.MinRetryBackoff, | 
				
			||||
		MaxRetryBackoff: opt.MaxRetryBackoff, | 
				
			||||
 | 
				
			||||
		DialTimeout:  opt.DialTimeout, | 
				
			||||
		ReadTimeout:  opt.ReadTimeout, | 
				
			||||
		WriteTimeout: opt.WriteTimeout, | 
				
			||||
 | 
				
			||||
		PoolSize:           opt.PoolSize, | 
				
			||||
		PoolTimeout:        opt.PoolTimeout, | 
				
			||||
		IdleTimeout:        opt.IdleTimeout, | 
				
			||||
		IdleCheckFrequency: opt.IdleCheckFrequency, | 
				
			||||
		MinIdleConns:       opt.MinIdleConns, | 
				
			||||
		MaxConnAge:         opt.MaxConnAge, | 
				
			||||
 | 
				
			||||
		TLSConfig: opt.TLSConfig, | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// NewFailoverClient returns a Redis client that uses Redis Sentinel
 | 
				
			||||
// for automatic failover. It's safe for concurrent use by multiple
 | 
				
			||||
// goroutines.
 | 
				
			||||
func NewFailoverClient(failoverOpt *FailoverOptions) *Client { | 
				
			||||
	if failoverOpt.RouteByLatency { | 
				
			||||
		panic("to route commands by latency, use NewFailoverClusterClient") | 
				
			||||
	} | 
				
			||||
	if failoverOpt.RouteRandomly { | 
				
			||||
		panic("to route commands randomly, use NewFailoverClusterClient") | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	sentinelAddrs := make([]string, len(failoverOpt.SentinelAddrs)) | 
				
			||||
	copy(sentinelAddrs, failoverOpt.SentinelAddrs) | 
				
			||||
 | 
				
			||||
	failover := &sentinelFailover{ | 
				
			||||
		opt:           failoverOpt, | 
				
			||||
		sentinelAddrs: sentinelAddrs, | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	opt := failoverOpt.clientOptions() | 
				
			||||
	opt.Dialer = masterSlaveDialer(failover) | 
				
			||||
	opt.init() | 
				
			||||
 | 
				
			||||
	connPool := newConnPool(opt) | 
				
			||||
 | 
				
			||||
	failover.mu.Lock() | 
				
			||||
	failover.onFailover = func(ctx context.Context, addr string) { | 
				
			||||
		_ = connPool.Filter(func(cn *pool.Conn) bool { | 
				
			||||
			return cn.RemoteAddr().String() != addr | 
				
			||||
		}) | 
				
			||||
	} | 
				
			||||
	failover.mu.Unlock() | 
				
			||||
 | 
				
			||||
	c := Client{ | 
				
			||||
		baseClient: newBaseClient(opt, connPool), | 
				
			||||
		ctx:        context.Background(), | 
				
			||||
	} | 
				
			||||
	c.cmdable = c.Process | 
				
			||||
	c.onClose = failover.Close | 
				
			||||
 | 
				
			||||
	return &c | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func masterSlaveDialer( | 
				
			||||
	failover *sentinelFailover, | 
				
			||||
) func(ctx context.Context, network, addr string) (net.Conn, error) { | 
				
			||||
	return func(ctx context.Context, network, _ string) (net.Conn, error) { | 
				
			||||
		var addr string | 
				
			||||
		var err error | 
				
			||||
 | 
				
			||||
		if failover.opt.SlaveOnly { | 
				
			||||
			addr, err = failover.RandomSlaveAddr(ctx) | 
				
			||||
		} else { | 
				
			||||
			addr, err = failover.MasterAddr(ctx) | 
				
			||||
			if err == nil { | 
				
			||||
				failover.trySwitchMaster(ctx, addr) | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
 | 
				
			||||
		if err != nil { | 
				
			||||
			return nil, err | 
				
			||||
		} | 
				
			||||
		if failover.opt.Dialer != nil { | 
				
			||||
			return failover.opt.Dialer(ctx, network, addr) | 
				
			||||
		} | 
				
			||||
		return net.DialTimeout("tcp", addr, failover.opt.DialTimeout) | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
//------------------------------------------------------------------------------
 | 
				
			||||
 | 
				
			||||
// SentinelClient is a client for a Redis Sentinel.
 | 
				
			||||
type SentinelClient struct { | 
				
			||||
	*baseClient | 
				
			||||
	hooks | 
				
			||||
	ctx context.Context | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func NewSentinelClient(opt *Options) *SentinelClient { | 
				
			||||
	opt.init() | 
				
			||||
	c := &SentinelClient{ | 
				
			||||
		baseClient: &baseClient{ | 
				
			||||
			opt:      opt, | 
				
			||||
			connPool: newConnPool(opt), | 
				
			||||
		}, | 
				
			||||
		ctx: context.Background(), | 
				
			||||
	} | 
				
			||||
	return c | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (c *SentinelClient) Context() context.Context { | 
				
			||||
	return c.ctx | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (c *SentinelClient) WithContext(ctx context.Context) *SentinelClient { | 
				
			||||
	if ctx == nil { | 
				
			||||
		panic("nil context") | 
				
			||||
	} | 
				
			||||
	clone := *c | 
				
			||||
	clone.ctx = ctx | 
				
			||||
	return &clone | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (c *SentinelClient) Process(ctx context.Context, cmd Cmder) error { | 
				
			||||
	return c.hooks.process(ctx, cmd, c.baseClient.process) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (c *SentinelClient) pubSub() *PubSub { | 
				
			||||
	pubsub := &PubSub{ | 
				
			||||
		opt: c.opt, | 
				
			||||
 | 
				
			||||
		newConn: func(ctx context.Context, channels []string) (*pool.Conn, error) { | 
				
			||||
			return c.newConn(ctx) | 
				
			||||
		}, | 
				
			||||
		closeConn: c.connPool.CloseConn, | 
				
			||||
	} | 
				
			||||
	pubsub.init() | 
				
			||||
	return pubsub | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Ping is used to test if a connection is still alive, or to
 | 
				
			||||
// measure latency.
 | 
				
			||||
func (c *SentinelClient) Ping(ctx context.Context) *StringCmd { | 
				
			||||
	cmd := NewStringCmd(ctx, "ping") | 
				
			||||
	_ = c.Process(ctx, cmd) | 
				
			||||
	return cmd | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Subscribe subscribes the client to the specified channels.
 | 
				
			||||
// Channels can be omitted to create empty subscription.
 | 
				
			||||
func (c *SentinelClient) Subscribe(ctx context.Context, channels ...string) *PubSub { | 
				
			||||
	pubsub := c.pubSub() | 
				
			||||
	if len(channels) > 0 { | 
				
			||||
		_ = pubsub.Subscribe(ctx, channels...) | 
				
			||||
	} | 
				
			||||
	return pubsub | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// PSubscribe subscribes the client to the given patterns.
 | 
				
			||||
// Patterns can be omitted to create empty subscription.
 | 
				
			||||
func (c *SentinelClient) PSubscribe(ctx context.Context, channels ...string) *PubSub { | 
				
			||||
	pubsub := c.pubSub() | 
				
			||||
	if len(channels) > 0 { | 
				
			||||
		_ = pubsub.PSubscribe(ctx, channels...) | 
				
			||||
	} | 
				
			||||
	return pubsub | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (c *SentinelClient) GetMasterAddrByName(ctx context.Context, name string) *StringSliceCmd { | 
				
			||||
	cmd := NewStringSliceCmd(ctx, "sentinel", "get-master-addr-by-name", name) | 
				
			||||
	_ = c.Process(ctx, cmd) | 
				
			||||
	return cmd | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (c *SentinelClient) Sentinels(ctx context.Context, name string) *SliceCmd { | 
				
			||||
	cmd := NewSliceCmd(ctx, "sentinel", "sentinels", name) | 
				
			||||
	_ = c.Process(ctx, cmd) | 
				
			||||
	return cmd | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Failover forces a failover as if the master was not reachable, and without
 | 
				
			||||
// asking for agreement to other Sentinels.
 | 
				
			||||
func (c *SentinelClient) Failover(ctx context.Context, name string) *StatusCmd { | 
				
			||||
	cmd := NewStatusCmd(ctx, "sentinel", "failover", name) | 
				
			||||
	_ = c.Process(ctx, cmd) | 
				
			||||
	return cmd | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Reset resets all the masters with matching name. The pattern argument is a
 | 
				
			||||
// glob-style pattern. The reset process clears any previous state in a master
 | 
				
			||||
// (including a failover in progress), and removes every slave and sentinel
 | 
				
			||||
// already discovered and associated with the master.
 | 
				
			||||
func (c *SentinelClient) Reset(ctx context.Context, pattern string) *IntCmd { | 
				
			||||
	cmd := NewIntCmd(ctx, "sentinel", "reset", pattern) | 
				
			||||
	_ = c.Process(ctx, cmd) | 
				
			||||
	return cmd | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// FlushConfig forces Sentinel to rewrite its configuration on disk, including
 | 
				
			||||
// the current Sentinel state.
 | 
				
			||||
func (c *SentinelClient) FlushConfig(ctx context.Context) *StatusCmd { | 
				
			||||
	cmd := NewStatusCmd(ctx, "sentinel", "flushconfig") | 
				
			||||
	_ = c.Process(ctx, cmd) | 
				
			||||
	return cmd | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Master shows the state and info of the specified master.
 | 
				
			||||
func (c *SentinelClient) Master(ctx context.Context, name string) *StringStringMapCmd { | 
				
			||||
	cmd := NewStringStringMapCmd(ctx, "sentinel", "master", name) | 
				
			||||
	_ = c.Process(ctx, cmd) | 
				
			||||
	return cmd | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Masters shows a list of monitored masters and their state.
 | 
				
			||||
func (c *SentinelClient) Masters(ctx context.Context) *SliceCmd { | 
				
			||||
	cmd := NewSliceCmd(ctx, "sentinel", "masters") | 
				
			||||
	_ = c.Process(ctx, cmd) | 
				
			||||
	return cmd | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Slaves shows a list of slaves for the specified master and their state.
 | 
				
			||||
func (c *SentinelClient) Slaves(ctx context.Context, name string) *SliceCmd { | 
				
			||||
	cmd := NewSliceCmd(ctx, "sentinel", "slaves", name) | 
				
			||||
	_ = c.Process(ctx, cmd) | 
				
			||||
	return cmd | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// CkQuorum checks if the current Sentinel configuration is able to reach the
 | 
				
			||||
// quorum needed to failover a master, and the majority needed to authorize the
 | 
				
			||||
// failover. This command should be used in monitoring systems to check if a
 | 
				
			||||
// Sentinel deployment is ok.
 | 
				
			||||
func (c *SentinelClient) CkQuorum(ctx context.Context, name string) *StringCmd { | 
				
			||||
	cmd := NewStringCmd(ctx, "sentinel", "ckquorum", name) | 
				
			||||
	_ = c.Process(ctx, cmd) | 
				
			||||
	return cmd | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Monitor tells the Sentinel to start monitoring a new master with the specified
 | 
				
			||||
// name, ip, port, and quorum.
 | 
				
			||||
func (c *SentinelClient) Monitor(ctx context.Context, name, ip, port, quorum string) *StringCmd { | 
				
			||||
	cmd := NewStringCmd(ctx, "sentinel", "monitor", name, ip, port, quorum) | 
				
			||||
	_ = c.Process(ctx, cmd) | 
				
			||||
	return cmd | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Set is used in order to change configuration parameters of a specific master.
 | 
				
			||||
func (c *SentinelClient) Set(ctx context.Context, name, option, value string) *StringCmd { | 
				
			||||
	cmd := NewStringCmd(ctx, "sentinel", "set", name, option, value) | 
				
			||||
	_ = c.Process(ctx, cmd) | 
				
			||||
	return cmd | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Remove is used in order to remove the specified master: the master will no
 | 
				
			||||
// longer be monitored, and will totally be removed from the internal state of
 | 
				
			||||
// the Sentinel.
 | 
				
			||||
func (c *SentinelClient) Remove(ctx context.Context, name string) *StringCmd { | 
				
			||||
	cmd := NewStringCmd(ctx, "sentinel", "remove", name) | 
				
			||||
	_ = c.Process(ctx, cmd) | 
				
			||||
	return cmd | 
				
			||||
} | 
				
			||||
 | 
				
			||||
//------------------------------------------------------------------------------
 | 
				
			||||
 | 
				
			||||
type sentinelFailover struct { | 
				
			||||
	opt *FailoverOptions | 
				
			||||
 | 
				
			||||
	sentinelAddrs []string | 
				
			||||
 | 
				
			||||
	onFailover func(ctx context.Context, addr string) | 
				
			||||
	onUpdate   func(ctx context.Context) | 
				
			||||
 | 
				
			||||
	mu          sync.RWMutex | 
				
			||||
	_masterAddr string | 
				
			||||
	sentinel    *SentinelClient | 
				
			||||
	pubsub      *PubSub | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (c *sentinelFailover) Close() error { | 
				
			||||
	c.mu.Lock() | 
				
			||||
	defer c.mu.Unlock() | 
				
			||||
	if c.sentinel != nil { | 
				
			||||
		return c.closeSentinel() | 
				
			||||
	} | 
				
			||||
	return nil | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (c *sentinelFailover) closeSentinel() error { | 
				
			||||
	firstErr := c.pubsub.Close() | 
				
			||||
	c.pubsub = nil | 
				
			||||
 | 
				
			||||
	err := c.sentinel.Close() | 
				
			||||
	if err != nil && firstErr == nil { | 
				
			||||
		firstErr = err | 
				
			||||
	} | 
				
			||||
	c.sentinel = nil | 
				
			||||
 | 
				
			||||
	return firstErr | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (c *sentinelFailover) RandomSlaveAddr(ctx context.Context) (string, error) { | 
				
			||||
	addresses, err := c.slaveAddrs(ctx) | 
				
			||||
	if err != nil { | 
				
			||||
		return "", err | 
				
			||||
	} | 
				
			||||
	if len(addresses) == 0 { | 
				
			||||
		return c.MasterAddr(ctx) | 
				
			||||
	} | 
				
			||||
	return addresses[rand.Intn(len(addresses))], nil | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (c *sentinelFailover) MasterAddr(ctx context.Context) (string, error) { | 
				
			||||
	c.mu.RLock() | 
				
			||||
	sentinel := c.sentinel | 
				
			||||
	c.mu.RUnlock() | 
				
			||||
 | 
				
			||||
	if sentinel != nil { | 
				
			||||
		addr := c.getMasterAddr(ctx, sentinel) | 
				
			||||
		if addr != "" { | 
				
			||||
			return addr, nil | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	c.mu.Lock() | 
				
			||||
	defer c.mu.Unlock() | 
				
			||||
 | 
				
			||||
	if c.sentinel != nil { | 
				
			||||
		addr := c.getMasterAddr(ctx, c.sentinel) | 
				
			||||
		if addr != "" { | 
				
			||||
			return addr, nil | 
				
			||||
		} | 
				
			||||
		_ = c.closeSentinel() | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	for i, sentinelAddr := range c.sentinelAddrs { | 
				
			||||
		sentinel := NewSentinelClient(c.opt.sentinelOptions(sentinelAddr)) | 
				
			||||
 | 
				
			||||
		masterAddr, err := sentinel.GetMasterAddrByName(ctx, c.opt.MasterName).Result() | 
				
			||||
		if err != nil { | 
				
			||||
			internal.Logger.Printf(ctx, "sentinel: GetMasterAddrByName master=%q failed: %s", | 
				
			||||
				c.opt.MasterName, err) | 
				
			||||
			_ = sentinel.Close() | 
				
			||||
			continue | 
				
			||||
		} | 
				
			||||
 | 
				
			||||
		// Push working sentinel to the top.
 | 
				
			||||
		c.sentinelAddrs[0], c.sentinelAddrs[i] = c.sentinelAddrs[i], c.sentinelAddrs[0] | 
				
			||||
		c.setSentinel(ctx, sentinel) | 
				
			||||
 | 
				
			||||
		addr := net.JoinHostPort(masterAddr[0], masterAddr[1]) | 
				
			||||
		return addr, nil | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	return "", errors.New("redis: all sentinels specified in configuration are unreachable") | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (c *sentinelFailover) slaveAddrs(ctx context.Context) ([]string, error) { | 
				
			||||
	c.mu.RLock() | 
				
			||||
	sentinel := c.sentinel | 
				
			||||
	c.mu.RUnlock() | 
				
			||||
 | 
				
			||||
	if sentinel != nil { | 
				
			||||
		addrs := c.getSlaveAddrs(ctx, sentinel) | 
				
			||||
		if len(addrs) > 0 { | 
				
			||||
			return addrs, nil | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	c.mu.Lock() | 
				
			||||
	defer c.mu.Unlock() | 
				
			||||
 | 
				
			||||
	if c.sentinel != nil { | 
				
			||||
		addrs := c.getSlaveAddrs(ctx, c.sentinel) | 
				
			||||
		if len(addrs) > 0 { | 
				
			||||
			return addrs, nil | 
				
			||||
		} | 
				
			||||
		_ = c.closeSentinel() | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	for i, sentinelAddr := range c.sentinelAddrs { | 
				
			||||
		sentinel := NewSentinelClient(c.opt.sentinelOptions(sentinelAddr)) | 
				
			||||
 | 
				
			||||
		slaves, err := sentinel.Slaves(ctx, c.opt.MasterName).Result() | 
				
			||||
		if err != nil { | 
				
			||||
			internal.Logger.Printf(ctx, "sentinel: Slaves master=%q failed: %s", | 
				
			||||
				c.opt.MasterName, err) | 
				
			||||
			_ = sentinel.Close() | 
				
			||||
			continue | 
				
			||||
		} | 
				
			||||
 | 
				
			||||
		// Push working sentinel to the top.
 | 
				
			||||
		c.sentinelAddrs[0], c.sentinelAddrs[i] = c.sentinelAddrs[i], c.sentinelAddrs[0] | 
				
			||||
		c.setSentinel(ctx, sentinel) | 
				
			||||
 | 
				
			||||
		addrs := parseSlaveAddrs(slaves) | 
				
			||||
		return addrs, nil | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	return []string{}, errors.New("redis: all sentinels specified in configuration are unreachable") | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (c *sentinelFailover) getMasterAddr(ctx context.Context, sentinel *SentinelClient) string { | 
				
			||||
	addr, err := sentinel.GetMasterAddrByName(ctx, c.opt.MasterName).Result() | 
				
			||||
	if err != nil { | 
				
			||||
		internal.Logger.Printf(ctx, "sentinel: GetMasterAddrByName name=%q failed: %s", | 
				
			||||
			c.opt.MasterName, err) | 
				
			||||
		return "" | 
				
			||||
	} | 
				
			||||
	return net.JoinHostPort(addr[0], addr[1]) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (c *sentinelFailover) getSlaveAddrs(ctx context.Context, sentinel *SentinelClient) []string { | 
				
			||||
	addrs, err := sentinel.Slaves(ctx, c.opt.MasterName).Result() | 
				
			||||
	if err != nil { | 
				
			||||
		internal.Logger.Printf(ctx, "sentinel: Slaves name=%q failed: %s", | 
				
			||||
			c.opt.MasterName, err) | 
				
			||||
		return []string{} | 
				
			||||
	} | 
				
			||||
	return parseSlaveAddrs(addrs) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func parseSlaveAddrs(addrs []interface{}) []string { | 
				
			||||
	nodes := make([]string, 0, len(addrs)) | 
				
			||||
 | 
				
			||||
	for _, node := range addrs { | 
				
			||||
		ip := "" | 
				
			||||
		port := "" | 
				
			||||
		flags := []string{} | 
				
			||||
		lastkey := "" | 
				
			||||
		isDown := false | 
				
			||||
 | 
				
			||||
		for _, key := range node.([]interface{}) { | 
				
			||||
			switch lastkey { | 
				
			||||
			case "ip": | 
				
			||||
				ip = key.(string) | 
				
			||||
			case "port": | 
				
			||||
				port = key.(string) | 
				
			||||
			case "flags": | 
				
			||||
				flags = strings.Split(key.(string), ",") | 
				
			||||
			} | 
				
			||||
			lastkey = key.(string) | 
				
			||||
		} | 
				
			||||
 | 
				
			||||
		for _, flag := range flags { | 
				
			||||
			switch flag { | 
				
			||||
			case "s_down", "o_down", "disconnected": | 
				
			||||
				isDown = true | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
 | 
				
			||||
		if !isDown { | 
				
			||||
			nodes = append(nodes, net.JoinHostPort(ip, port)) | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	return nodes | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (c *sentinelFailover) trySwitchMaster(ctx context.Context, addr string) { | 
				
			||||
	c.mu.RLock() | 
				
			||||
	currentAddr := c._masterAddr | 
				
			||||
	c.mu.RUnlock() | 
				
			||||
 | 
				
			||||
	if addr == currentAddr { | 
				
			||||
		return | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	c.mu.Lock() | 
				
			||||
	defer c.mu.Unlock() | 
				
			||||
 | 
				
			||||
	if addr == c._masterAddr { | 
				
			||||
		return | 
				
			||||
	} | 
				
			||||
	c._masterAddr = addr | 
				
			||||
 | 
				
			||||
	internal.Logger.Printf(ctx, "sentinel: new master=%q addr=%q", | 
				
			||||
		c.opt.MasterName, addr) | 
				
			||||
	if c.onFailover != nil { | 
				
			||||
		c.onFailover(ctx, addr) | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (c *sentinelFailover) setSentinel(ctx context.Context, sentinel *SentinelClient) { | 
				
			||||
	if c.sentinel != nil { | 
				
			||||
		panic("not reached") | 
				
			||||
	} | 
				
			||||
	c.sentinel = sentinel | 
				
			||||
	c.discoverSentinels(ctx) | 
				
			||||
 | 
				
			||||
	c.pubsub = sentinel.Subscribe(ctx, "+switch-master", "+slave-reconf-done") | 
				
			||||
	go c.listen(c.pubsub) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (c *sentinelFailover) discoverSentinels(ctx context.Context) { | 
				
			||||
	sentinels, err := c.sentinel.Sentinels(ctx, c.opt.MasterName).Result() | 
				
			||||
	if err != nil { | 
				
			||||
		internal.Logger.Printf(ctx, "sentinel: Sentinels master=%q failed: %s", c.opt.MasterName, err) | 
				
			||||
		return | 
				
			||||
	} | 
				
			||||
	for _, sentinel := range sentinels { | 
				
			||||
		vals := sentinel.([]interface{}) | 
				
			||||
		for i := 0; i < len(vals); i += 2 { | 
				
			||||
			key := vals[i].(string) | 
				
			||||
			if key == "name" { | 
				
			||||
				sentinelAddr := vals[i+1].(string) | 
				
			||||
				if !contains(c.sentinelAddrs, sentinelAddr) { | 
				
			||||
					internal.Logger.Printf(ctx, "sentinel: discovered new sentinel=%q for master=%q", | 
				
			||||
						sentinelAddr, c.opt.MasterName) | 
				
			||||
					c.sentinelAddrs = append(c.sentinelAddrs, sentinelAddr) | 
				
			||||
				} | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (c *sentinelFailover) listen(pubsub *PubSub) { | 
				
			||||
	ctx := context.TODO() | 
				
			||||
 | 
				
			||||
	if c.onUpdate != nil { | 
				
			||||
		c.onUpdate(ctx) | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	ch := pubsub.Channel() | 
				
			||||
	for msg := range ch { | 
				
			||||
		if msg.Channel == "+switch-master" { | 
				
			||||
			parts := strings.Split(msg.Payload, " ") | 
				
			||||
			if parts[0] != c.opt.MasterName { | 
				
			||||
				internal.Logger.Printf(pubsub.getContext(), "sentinel: ignore addr for master=%q", parts[0]) | 
				
			||||
				continue | 
				
			||||
			} | 
				
			||||
			addr := net.JoinHostPort(parts[3], parts[4]) | 
				
			||||
			c.trySwitchMaster(pubsub.getContext(), addr) | 
				
			||||
		} | 
				
			||||
 | 
				
			||||
		if c.onUpdate != nil { | 
				
			||||
			c.onUpdate(ctx) | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func contains(slice []string, str string) bool { | 
				
			||||
	for _, s := range slice { | 
				
			||||
		if s == str { | 
				
			||||
			return true | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
	return false | 
				
			||||
} | 
				
			||||
 | 
				
			||||
//------------------------------------------------------------------------------
 | 
				
			||||
 | 
				
			||||
// NewFailoverClusterClient returns a client that supports routing read-only commands
 | 
				
			||||
// to a slave node.
 | 
				
			||||
func NewFailoverClusterClient(failoverOpt *FailoverOptions) *ClusterClient { | 
				
			||||
	sentinelAddrs := make([]string, len(failoverOpt.SentinelAddrs)) | 
				
			||||
	copy(sentinelAddrs, failoverOpt.SentinelAddrs) | 
				
			||||
 | 
				
			||||
	failover := &sentinelFailover{ | 
				
			||||
		opt:           failoverOpt, | 
				
			||||
		sentinelAddrs: sentinelAddrs, | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	opt := failoverOpt.clusterOptions() | 
				
			||||
	opt.ClusterSlots = func(ctx context.Context) ([]ClusterSlot, error) { | 
				
			||||
		masterAddr, err := failover.MasterAddr(ctx) | 
				
			||||
		if err != nil { | 
				
			||||
			return nil, err | 
				
			||||
		} | 
				
			||||
 | 
				
			||||
		nodes := []ClusterNode{{ | 
				
			||||
			Addr: masterAddr, | 
				
			||||
		}} | 
				
			||||
 | 
				
			||||
		slaveAddrs, err := failover.slaveAddrs(ctx) | 
				
			||||
		if err != nil { | 
				
			||||
			return nil, err | 
				
			||||
		} | 
				
			||||
 | 
				
			||||
		for _, slaveAddr := range slaveAddrs { | 
				
			||||
			nodes = append(nodes, ClusterNode{ | 
				
			||||
				Addr: slaveAddr, | 
				
			||||
			}) | 
				
			||||
		} | 
				
			||||
 | 
				
			||||
		slots := []ClusterSlot{ | 
				
			||||
			{ | 
				
			||||
				Start: 0, | 
				
			||||
				End:   16383, | 
				
			||||
				Nodes: nodes, | 
				
			||||
			}, | 
				
			||||
		} | 
				
			||||
		return slots, nil | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	c := NewClusterClient(opt) | 
				
			||||
 | 
				
			||||
	failover.mu.Lock() | 
				
			||||
	failover.onUpdate = func(ctx context.Context) { | 
				
			||||
		c.ReloadState(ctx) | 
				
			||||
	} | 
				
			||||
	failover.mu.Unlock() | 
				
			||||
 | 
				
			||||
	return c | 
				
			||||
} | 
				
			||||
						
							
								
									
								
							
							
								
									51
								
							
							vendor/github.com/go-redis/redis/v7/tx.go → vendor/github.com/go-redis/redis/v8/tx.go
							
								generated
							
							
								vendored
							
						
						
					
					
								
									51
								
							
							vendor/github.com/go-redis/redis/v7/tx.go → vendor/github.com/go-redis/redis/v8/tx.go
							
								generated
							
							
								vendored
							
						@ -0,0 +1,23 @@ | 
				
			||||
.DS_Store | 
				
			||||
Thumbs.db | 
				
			||||
 | 
				
			||||
.tools/ | 
				
			||||
.idea/ | 
				
			||||
.vscode/ | 
				
			||||
*.iml | 
				
			||||
*.so | 
				
			||||
coverage.* | 
				
			||||
 | 
				
			||||
gen/ | 
				
			||||
 | 
				
			||||
/example/grpc/client/client | 
				
			||||
/example/grpc/server/server | 
				
			||||
/example/http/client/client | 
				
			||||
/example/http/server/server | 
				
			||||
/example/jaeger/jaeger | 
				
			||||
/example/namedtracer/namedtracer | 
				
			||||
/example/opencensus/opencensus | 
				
			||||
/example/prometheus/prometheus | 
				
			||||
/example/prom-collector/prom-collector | 
				
			||||
/example/zipkin/zipkin | 
				
			||||
/example/otel-collector/otel-collector | 
				
			||||
@ -0,0 +1,3 @@ | 
				
			||||
[submodule "opentelemetry-proto"] | 
				
			||||
	path = exporters/otlp/internal/opentelemetry-proto | 
				
			||||
	url = https://github.com/open-telemetry/opentelemetry-proto | 
				
			||||
@ -0,0 +1,32 @@ | 
				
			||||
# See https://github.com/golangci/golangci-lint#config-file | 
				
			||||
run: | 
				
			||||
  issues-exit-code: 1 #Default | 
				
			||||
  tests: true #Default | 
				
			||||
 | 
				
			||||
linters: | 
				
			||||
  enable: | 
				
			||||
    - misspell | 
				
			||||
    - goimports | 
				
			||||
    - golint | 
				
			||||
    - gofmt | 
				
			||||
 | 
				
			||||
issues: | 
				
			||||
  exclude-rules: | 
				
			||||
    # helpers in tests often (rightfully) pass a *testing.T as their first argument | 
				
			||||
    - path: _test\.go | 
				
			||||
      text: "context.Context should be the first parameter of a function" | 
				
			||||
      linters: | 
				
			||||
        - golint | 
				
			||||
    # Yes, they are, but it's okay in a test | 
				
			||||
    - path: _test\.go | 
				
			||||
      text: "exported func.*returns unexported type.*which can be annoying to use" | 
				
			||||
      linters: | 
				
			||||
        - golint | 
				
			||||
 | 
				
			||||
linters-settings: | 
				
			||||
  misspell: | 
				
			||||
    locale: US | 
				
			||||
    ignore-words: | 
				
			||||
      - cancelled | 
				
			||||
  goimports: | 
				
			||||
    local-prefixes: go.opentelemetry.io | 
				
			||||
									
										
											File diff suppressed because it is too large
											Load Diff
										
									
								
							
						@ -0,0 +1,17 @@ | 
				
			||||
##################################################### | 
				
			||||
# | 
				
			||||
# List of approvers for this repository | 
				
			||||
# | 
				
			||||
##################################################### | 
				
			||||
# | 
				
			||||
# Learn about membership in OpenTelemetry community: | 
				
			||||
#  https://github.com/open-telemetry/community/blob/master/community-membership.md | 
				
			||||
# | 
				
			||||
# | 
				
			||||
# Learn about CODEOWNERS file format: | 
				
			||||
#  https://help.github.com/en/articles/about-code-owners | 
				
			||||
# | 
				
			||||
 | 
				
			||||
* @jmacd @lizthegrey @MrAlias @Aneurysm9 @evantorrie @XSAM @dashpole | 
				
			||||
 | 
				
			||||
CODEOWNERS @MrAlias @Aneurysm9 | 
				
			||||
@ -0,0 +1,374 @@ | 
				
			||||
# Contributing to opentelemetry-go | 
				
			||||
 | 
				
			||||
The Go special interest group (SIG) meets regularly. See the | 
				
			||||
OpenTelemetry | 
				
			||||
[community](https://github.com/open-telemetry/community#golang-sdk) | 
				
			||||
repo for information on this and other language SIGs. | 
				
			||||
 | 
				
			||||
See the [public meeting | 
				
			||||
notes](https://docs.google.com/document/d/1A63zSWX0x2CyCK_LoNhmQC4rqhLpYXJzXbEPDUQ2n6w/edit#heading=h.9tngw7jdwd6b) | 
				
			||||
for a summary description of past meetings. To request edit access, | 
				
			||||
join the meeting or get in touch on | 
				
			||||
[Gitter](https://gitter.im/open-telemetry/opentelemetry-go). | 
				
			||||
 | 
				
			||||
## Development | 
				
			||||
 | 
				
			||||
You can view and edit the source code by cloning this repository: | 
				
			||||
 | 
				
			||||
```bash | 
				
			||||
git clone https://github.com/open-telemetry/opentelemetry-go.git | 
				
			||||
``` | 
				
			||||
 | 
				
			||||
Run `make test` to run the tests instead of `go test`.  | 
				
			||||
 | 
				
			||||
There are some generated files checked into the repo. To make sure | 
				
			||||
that the generated files are up-to-date, run `make` (or `make | 
				
			||||
precommit` - the `precommit` target is the default). | 
				
			||||
 | 
				
			||||
The `precommit` target also fixes the formatting of the code and | 
				
			||||
checks the status of the go module files. | 
				
			||||
 | 
				
			||||
If after running `make precommit` the output of `git status` contains | 
				
			||||
`nothing to commit, working tree clean` then it means that everything | 
				
			||||
is up-to-date and properly formatted. | 
				
			||||
 | 
				
			||||
## Pull Requests | 
				
			||||
 | 
				
			||||
### How to Send Pull Requests | 
				
			||||
 | 
				
			||||
Everyone is welcome to contribute code to `opentelemetry-go` via | 
				
			||||
GitHub pull requests (PRs). | 
				
			||||
 | 
				
			||||
To create a new PR, fork the project in GitHub and clone the upstream | 
				
			||||
repo: | 
				
			||||
 | 
				
			||||
```sh | 
				
			||||
$ go get -d go.opentelemetry.io/otel | 
				
			||||
``` | 
				
			||||
 | 
				
			||||
(This may print some warning about "build constraints exclude all Go | 
				
			||||
files", just ignore it.) | 
				
			||||
 | 
				
			||||
This will put the project in `${GOPATH}/src/go.opentelemetry.io/otel`. You | 
				
			||||
can alternatively use `git` directly with: | 
				
			||||
 | 
				
			||||
```sh | 
				
			||||
$ git clone https://github.com/open-telemetry/opentelemetry-go | 
				
			||||
``` | 
				
			||||
 | 
				
			||||
(Note that `git clone` is *not* using the `go.opentelemetry.io/otel` name - | 
				
			||||
that name is a kind of a redirector to GitHub that `go get` can | 
				
			||||
understand, but `git` does not.) | 
				
			||||
 | 
				
			||||
This would put the project in the `opentelemetry-go` directory in | 
				
			||||
current working directory. | 
				
			||||
 | 
				
			||||
Enter the newly created directory and add your fork as a new remote: | 
				
			||||
 | 
				
			||||
```sh | 
				
			||||
$ git remote add <YOUR_FORK> git@github.com:<YOUR_GITHUB_USERNAME>/opentelemetry-go | 
				
			||||
``` | 
				
			||||
 | 
				
			||||
Check out a new branch, make modifications, run linters and tests, update | 
				
			||||
`CHANGELOG.md`, and push the branch to your fork: | 
				
			||||
 | 
				
			||||
```sh | 
				
			||||
$ git checkout -b <YOUR_BRANCH_NAME> | 
				
			||||
# edit files | 
				
			||||
# update changelog | 
				
			||||
$ make precommit | 
				
			||||
$ git add -p | 
				
			||||
$ git commit | 
				
			||||
$ git push <YOUR_FORK> <YOUR_BRANCH_NAME> | 
				
			||||
``` | 
				
			||||
 | 
				
			||||
Open a pull request against the main `opentelemetry-go` repo. Be sure to add the pull | 
				
			||||
request ID to the entry you added to `CHANGELOG.md`. | 
				
			||||
 | 
				
			||||
### How to Receive Comments | 
				
			||||
 | 
				
			||||
* If the PR is not ready for review, please put `[WIP]` in the title, | 
				
			||||
  tag it as `work-in-progress`, or mark it as | 
				
			||||
  [`draft`](https://github.blog/2019-02-14-introducing-draft-pull-requests/). | 
				
			||||
* Make sure CLA is signed and CI is clear. | 
				
			||||
 | 
				
			||||
### How to Get PRs Merged | 
				
			||||
 | 
				
			||||
A PR is considered to be **ready to merge** when: | 
				
			||||
 | 
				
			||||
* It has received two approvals from Collaborators/Maintainers (at | 
				
			||||
  different companies). This is not enforced through technical means | 
				
			||||
  and a PR may be **ready to merge** with a single approval if the change | 
				
			||||
  and its approach have been discussed and consensus reached. | 
				
			||||
* Major feedbacks are resolved. | 
				
			||||
* It has been open for review for at least one working day. This gives | 
				
			||||
  people reasonable time to review. | 
				
			||||
* Trivial changes (typo, cosmetic, doc, etc.) do not have to wait for | 
				
			||||
  one day and may be merged with a single Maintainer's approval. | 
				
			||||
* `CHANGELOG.md` has been updated to reflect what has been | 
				
			||||
  added, changed, removed, or fixed. | 
				
			||||
* Urgent fix can take exception as long as it has been actively | 
				
			||||
  communicated. | 
				
			||||
 | 
				
			||||
Any Maintainer can merge the PR once it is **ready to merge**. | 
				
			||||
 | 
				
			||||
## Design Choices | 
				
			||||
 | 
				
			||||
As with other OpenTelemetry clients, opentelemetry-go follows the | 
				
			||||
[opentelemetry-specification](https://github.com/open-telemetry/opentelemetry-specification). | 
				
			||||
 | 
				
			||||
It's especially valuable to read through the [library | 
				
			||||
guidelines](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/library-guidelines.md). | 
				
			||||
 | 
				
			||||
### Focus on Capabilities, Not Structure Compliance | 
				
			||||
 | 
				
			||||
OpenTelemetry is an evolving specification, one where the desires and | 
				
			||||
use cases are clear, but the method to satisfy those uses cases are | 
				
			||||
not. | 
				
			||||
 | 
				
			||||
As such, Contributions should provide functionality and behavior that | 
				
			||||
conforms to the specification, but the interface and structure is | 
				
			||||
flexible. | 
				
			||||
 | 
				
			||||
It is preferable to have contributions follow the idioms of the | 
				
			||||
language rather than conform to specific API names or argument | 
				
			||||
patterns in the spec. | 
				
			||||
 | 
				
			||||
For a deeper discussion, see: | 
				
			||||
https://github.com/open-telemetry/opentelemetry-specification/issues/165 | 
				
			||||
 | 
				
			||||
## Style Guide | 
				
			||||
 | 
				
			||||
One of the primary goals of this project is that it is actually used by | 
				
			||||
developers. With this goal in mind the project strives to build | 
				
			||||
user-friendly and idiomatic Go code adhering to the Go community's best | 
				
			||||
practices. | 
				
			||||
 | 
				
			||||
For a non-comprehensive but foundational overview of these best practices | 
				
			||||
the [Effective Go](https://golang.org/doc/effective_go.html) documentation | 
				
			||||
is an excellent starting place. | 
				
			||||
 | 
				
			||||
As a convenience for developers building this project the `make precommit` | 
				
			||||
will format, lint, validate, and in some cases fix the changes you plan to | 
				
			||||
submit. This check will need to pass for your changes to be able to be | 
				
			||||
merged. | 
				
			||||
 | 
				
			||||
In addition to idiomatic Go, the project has adopted certain standards for | 
				
			||||
implementations of common patterns. These standards should be followed as a | 
				
			||||
default, and if they are not followed documentation needs to be included as | 
				
			||||
to the reasons why. | 
				
			||||
 | 
				
			||||
### Configuration | 
				
			||||
 | 
				
			||||
When creating an instantiation function for a complex `struct` it is useful | 
				
			||||
to allow variable number of options to be applied. However, the strong type | 
				
			||||
system of Go restricts the function design options. There are a few ways to | 
				
			||||
solve this problem, but we have landed on the following design. | 
				
			||||
 | 
				
			||||
#### `config` | 
				
			||||
 | 
				
			||||
Configuration should be held in a `struct` named `config`, or prefixed with | 
				
			||||
specific type name this Configuration applies to if there are multiple | 
				
			||||
`config` in the package. This `struct` must contain configuration options. | 
				
			||||
 | 
				
			||||
```go | 
				
			||||
// config contains configuration options for a thing. | 
				
			||||
type config struct { | 
				
			||||
    // options ... | 
				
			||||
} | 
				
			||||
``` | 
				
			||||
 | 
				
			||||
In general the `config` `struct` will not need to be used externally to the | 
				
			||||
package and should be unexported. If, however, it is expected that the user | 
				
			||||
will likely want to build custom options for the configuration, the `config` | 
				
			||||
should be exported. Please, include in the documentation for the `config` | 
				
			||||
how the user can extend the configuration. | 
				
			||||
 | 
				
			||||
It is important that `config` are not shared across package boundaries. | 
				
			||||
Meaning a `config` from one package should not be directly used by another. | 
				
			||||
 | 
				
			||||
Optionally, it is common to include a `newConfig` function (with the same | 
				
			||||
naming scheme). This function wraps any defaults setting and looping over | 
				
			||||
all options to create a configured `config`. | 
				
			||||
 | 
				
			||||
```go | 
				
			||||
// newConfig returns an appropriately configured config. | 
				
			||||
func newConfig([]Option) config { | 
				
			||||
    // Set default values for config. | 
				
			||||
    config := config{/* […] */} | 
				
			||||
    for _, option := range options { | 
				
			||||
        option.Apply(&config) | 
				
			||||
    } | 
				
			||||
    // Preform any validation here. | 
				
			||||
    return config | 
				
			||||
} | 
				
			||||
``` | 
				
			||||
 | 
				
			||||
If validation of the `config` options is also preformed this can return an | 
				
			||||
error as well that is expected to be handled by the instantiation function | 
				
			||||
or propagated to the user. | 
				
			||||
 | 
				
			||||
Given the design goal of not having the user need to work with the `config`, | 
				
			||||
the `newConfig` function should also be unexported. | 
				
			||||
 | 
				
			||||
#### `Option` | 
				
			||||
 | 
				
			||||
To set the value of the options a `config` contains, a corresponding | 
				
			||||
`Option` interface type should be used. | 
				
			||||
 | 
				
			||||
```go | 
				
			||||
type Option interface { | 
				
			||||
  Apply(*config) | 
				
			||||
} | 
				
			||||
``` | 
				
			||||
 | 
				
			||||
The name of the interface should be prefixed in the same way the | 
				
			||||
corresponding `config` is (if at all). | 
				
			||||
 | 
				
			||||
#### Options | 
				
			||||
 | 
				
			||||
All user configurable options for a `config` must have a related unexported | 
				
			||||
implementation of the `Option` interface and an exported configuration | 
				
			||||
function that wraps this implementation. | 
				
			||||
 | 
				
			||||
The wrapping function name should be prefixed with `With*` (or in the | 
				
			||||
special case of a boolean options `Without*`) and should have the following | 
				
			||||
function signature. | 
				
			||||
 | 
				
			||||
```go | 
				
			||||
func With*(…) Option { … } | 
				
			||||
``` | 
				
			||||
 | 
				
			||||
##### `bool` Options | 
				
			||||
 | 
				
			||||
```go | 
				
			||||
type defaultFalseOption bool | 
				
			||||
 | 
				
			||||
func (o defaultFalseOption) Apply(c *config) { | 
				
			||||
    c.Bool = bool(o) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// WithOption sets a T* to have an option included. | 
				
			||||
func WithOption() Option { | 
				
			||||
    return defaultFalseOption(true) | 
				
			||||
} | 
				
			||||
``` | 
				
			||||
 | 
				
			||||
```go | 
				
			||||
type defaultTrueOption bool | 
				
			||||
 | 
				
			||||
func (o defaultTrueOption) Apply(c *config) { | 
				
			||||
    c.Bool = bool(o) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// WithoutOption sets a T* to have Bool option excluded. | 
				
			||||
func WithoutOption() Option { | 
				
			||||
    return defaultTrueOption(false) | 
				
			||||
} | 
				
			||||
```` | 
				
			||||
 | 
				
			||||
##### Declared Type Options | 
				
			||||
 | 
				
			||||
```go | 
				
			||||
type myTypeOption struct { | 
				
			||||
    MyType MyType | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (o myTypeOption) Apply(c *config) { | 
				
			||||
    c.MyType = o.MyType | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// WithMyType sets T* to have include MyType. | 
				
			||||
func WithMyType(t MyType) Option { | 
				
			||||
    return myTypeOption{t} | 
				
			||||
} | 
				
			||||
``` | 
				
			||||
 | 
				
			||||
#### Instantiation | 
				
			||||
 | 
				
			||||
Using this configuration pattern to configure instantiation with a `New*` | 
				
			||||
function. | 
				
			||||
 | 
				
			||||
```go | 
				
			||||
func NewT*(options ...Option) T* {…} | 
				
			||||
``` | 
				
			||||
 | 
				
			||||
Any required parameters can be declared before the variadic `options`. | 
				
			||||
 | 
				
			||||
#### Dealing with Overlap | 
				
			||||
 | 
				
			||||
Sometimes there are multiple complex `struct` that share common | 
				
			||||
configuration and also have distinct configuration. To avoid repeated | 
				
			||||
portions of `config`s, a common `config` can be used with the union of | 
				
			||||
options being handled with the `Option` interface. | 
				
			||||
 | 
				
			||||
For example. | 
				
			||||
 | 
				
			||||
```go | 
				
			||||
// config holds options for all animals. | 
				
			||||
type config struct { | 
				
			||||
	Weight      float64 | 
				
			||||
	Color       string | 
				
			||||
	MaxAltitude float64 | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// DogOption apply Dog specific options. | 
				
			||||
type DogOption interface { | 
				
			||||
	ApplyDog(*config) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// BirdOption apply Bird specific options. | 
				
			||||
type BirdOption interface { | 
				
			||||
	ApplyBird(*config) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Option apply options for all animals. | 
				
			||||
type Option interface { | 
				
			||||
	BirdOption | 
				
			||||
	DogOption | 
				
			||||
} | 
				
			||||
 | 
				
			||||
type weightOption float64 | 
				
			||||
func (o weightOption) ApplyDog(c *config)  { c.Weight = float64(o) } | 
				
			||||
func (o weightOption) ApplyBird(c *config) { c.Weight = float64(o) } | 
				
			||||
func WithWeight(w float64) Option          { return weightOption(w) } | 
				
			||||
 | 
				
			||||
type furColorOption string | 
				
			||||
func (o furColorOption) ApplyDog(c *config) { c.Color = string(o) } | 
				
			||||
func WithFurColor(c string) DogOption       { return furColorOption(c) } | 
				
			||||
 | 
				
			||||
type maxAltitudeOption float64 | 
				
			||||
func (o maxAltitudeOption) ApplyBird(c *config) { c.MaxAltitude = float64(o) } | 
				
			||||
func WithMaxAltitude(a float64) BirdOption      { return maxAltitudeOption(a) } | 
				
			||||
 | 
				
			||||
func NewDog(name string, o ...DogOption) Dog    {…} | 
				
			||||
func NewBird(name string, o ...BirdOption) Bird {…} | 
				
			||||
``` | 
				
			||||
 | 
				
			||||
### Interface Type | 
				
			||||
 | 
				
			||||
To allow other developers to better comprehend the code, it is important | 
				
			||||
to ensure it is sufficiently documented. One simple measure that contributes | 
				
			||||
to this aim is self-documenting by naming method parameters. Therefore, | 
				
			||||
where appropriate, methods of every exported interface type should have | 
				
			||||
their parameters appropriately named. | 
				
			||||
 | 
				
			||||
## Approvers and Maintainers | 
				
			||||
 | 
				
			||||
Approvers: | 
				
			||||
 | 
				
			||||
- [Liz Fong-Jones](https://github.com/lizthegrey), Honeycomb | 
				
			||||
- [Evan Torrie](https://github.com/evantorrie), Verizon Media | 
				
			||||
- [Josh MacDonald](https://github.com/jmacd), LightStep | 
				
			||||
- [Sam Xie](https://github.com/XSAM) | 
				
			||||
- [David Ashpole](https://github.com/dashpole), Google | 
				
			||||
 | 
				
			||||
Maintainers: | 
				
			||||
 | 
				
			||||
- [Anthony Mirabella](https://github.com/Aneurysm9), Centene | 
				
			||||
- [Tyler Yahn](https://github.com/MrAlias), New Relic | 
				
			||||
 | 
				
			||||
### Become an Approver or a Maintainer | 
				
			||||
 | 
				
			||||
See the [community membership document in OpenTelemetry community | 
				
			||||
repo](https://github.com/open-telemetry/community/blob/master/community-membership.md). | 
				
			||||
@ -0,0 +1,201 @@ | 
				
			||||
                                 Apache License | 
				
			||||
                           Version 2.0, January 2004 | 
				
			||||
                        http://www.apache.org/licenses/ | 
				
			||||
 | 
				
			||||
   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | 
				
			||||
 | 
				
			||||
   1. Definitions. | 
				
			||||
 | 
				
			||||
      "License" shall mean the terms and conditions for use, reproduction, | 
				
			||||
      and distribution as defined by Sections 1 through 9 of this document. | 
				
			||||
 | 
				
			||||
      "Licensor" shall mean the copyright owner or entity authorized by | 
				
			||||
      the copyright owner that is granting the License. | 
				
			||||
 | 
				
			||||
      "Legal Entity" shall mean the union of the acting entity and all | 
				
			||||
      other entities that control, are controlled by, or are under common | 
				
			||||
      control with that entity. For the purposes of this definition, | 
				
			||||
      "control" means (i) the power, direct or indirect, to cause the | 
				
			||||
      direction or management of such entity, whether by contract or | 
				
			||||
      otherwise, or (ii) ownership of fifty percent (50%) or more of the | 
				
			||||
      outstanding shares, or (iii) beneficial ownership of such entity. | 
				
			||||
 | 
				
			||||
      "You" (or "Your") shall mean an individual or Legal Entity | 
				
			||||
      exercising permissions granted by this License. | 
				
			||||
 | 
				
			||||
      "Source" form shall mean the preferred form for making modifications, | 
				
			||||
      including but not limited to software source code, documentation | 
				
			||||
      source, and configuration files. | 
				
			||||
 | 
				
			||||
      "Object" form shall mean any form resulting from mechanical | 
				
			||||
      transformation or translation of a Source form, including but | 
				
			||||
      not limited to compiled object code, generated documentation, | 
				
			||||
      and conversions to other media types. | 
				
			||||
 | 
				
			||||
      "Work" shall mean the work of authorship, whether in Source or | 
				
			||||
      Object form, made available under the License, as indicated by a | 
				
			||||
      copyright notice that is included in or attached to the work | 
				
			||||
      (an example is provided in the Appendix below). | 
				
			||||
 | 
				
			||||
      "Derivative Works" shall mean any work, whether in Source or Object | 
				
			||||
      form, that is based on (or derived from) the Work and for which the | 
				
			||||
      editorial revisions, annotations, elaborations, or other modifications | 
				
			||||
      represent, as a whole, an original work of authorship. For the purposes | 
				
			||||
      of this License, Derivative Works shall not include works that remain | 
				
			||||
      separable from, or merely link (or bind by name) to the interfaces of, | 
				
			||||
      the Work and Derivative Works thereof. | 
				
			||||
 | 
				
			||||
      "Contribution" shall mean any work of authorship, including | 
				
			||||
      the original version of the Work and any modifications or additions | 
				
			||||
      to that Work or Derivative Works thereof, that is intentionally | 
				
			||||
      submitted to Licensor for inclusion in the Work by the copyright owner | 
				
			||||
      or by an individual or Legal Entity authorized to submit on behalf of | 
				
			||||
      the copyright owner. For the purposes of this definition, "submitted" | 
				
			||||
      means any form of electronic, verbal, or written communication sent | 
				
			||||
      to the Licensor or its representatives, including but not limited to | 
				
			||||
      communication on electronic mailing lists, source code control systems, | 
				
			||||
      and issue tracking systems that are managed by, or on behalf of, the | 
				
			||||
      Licensor for the purpose of discussing and improving the Work, but | 
				
			||||
      excluding communication that is conspicuously marked or otherwise | 
				
			||||
      designated in writing by the copyright owner as "Not a Contribution." | 
				
			||||
 | 
				
			||||
      "Contributor" shall mean Licensor and any individual or Legal Entity | 
				
			||||
      on behalf of whom a Contribution has been received by Licensor and | 
				
			||||
      subsequently incorporated within the Work. | 
				
			||||
 | 
				
			||||
   2. Grant of Copyright License. Subject to the terms and conditions of | 
				
			||||
      this License, each Contributor hereby grants to You a perpetual, | 
				
			||||
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable | 
				
			||||
      copyright license to reproduce, prepare Derivative Works of, | 
				
			||||
      publicly display, publicly perform, sublicense, and distribute the | 
				
			||||
      Work and such Derivative Works in Source or Object form. | 
				
			||||
 | 
				
			||||
   3. Grant of Patent License. Subject to the terms and conditions of | 
				
			||||
      this License, each Contributor hereby grants to You a perpetual, | 
				
			||||
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable | 
				
			||||
      (except as stated in this section) patent license to make, have made, | 
				
			||||
      use, offer to sell, sell, import, and otherwise transfer the Work, | 
				
			||||
      where such license applies only to those patent claims licensable | 
				
			||||
      by such Contributor that are necessarily infringed by their | 
				
			||||
      Contribution(s) alone or by combination of their Contribution(s) | 
				
			||||
      with the Work to which such Contribution(s) was submitted. If You | 
				
			||||
      institute patent litigation against any entity (including a | 
				
			||||
      cross-claim or counterclaim in a lawsuit) alleging that the Work | 
				
			||||
      or a Contribution incorporated within the Work constitutes direct | 
				
			||||
      or contributory patent infringement, then any patent licenses | 
				
			||||
      granted to You under this License for that Work shall terminate | 
				
			||||
      as of the date such litigation is filed. | 
				
			||||
 | 
				
			||||
   4. Redistribution. You may reproduce and distribute copies of the | 
				
			||||
      Work or Derivative Works thereof in any medium, with or without | 
				
			||||
      modifications, and in Source or Object form, provided that You | 
				
			||||
      meet the following conditions: | 
				
			||||
 | 
				
			||||
      (a) You must give any other recipients of the Work or | 
				
			||||
          Derivative Works a copy of this License; and | 
				
			||||
 | 
				
			||||
      (b) You must cause any modified files to carry prominent notices | 
				
			||||
          stating that You changed the files; and | 
				
			||||
 | 
				
			||||
      (c) You must retain, in the Source form of any Derivative Works | 
				
			||||
          that You distribute, all copyright, patent, trademark, and | 
				
			||||
          attribution notices from the Source form of the Work, | 
				
			||||
          excluding those notices that do not pertain to any part of | 
				
			||||
          the Derivative Works; and | 
				
			||||
 | 
				
			||||
      (d) If the Work includes a "NOTICE" text file as part of its | 
				
			||||
          distribution, then any Derivative Works that You distribute must | 
				
			||||
          include a readable copy of the attribution notices contained | 
				
			||||
          within such NOTICE file, excluding those notices that do not | 
				
			||||
          pertain to any part of the Derivative Works, in at least one | 
				
			||||
          of the following places: within a NOTICE text file distributed | 
				
			||||
          as part of the Derivative Works; within the Source form or | 
				
			||||
          documentation, if provided along with the Derivative Works; or, | 
				
			||||
          within a display generated by the Derivative Works, if and | 
				
			||||
          wherever such third-party notices normally appear. The contents | 
				
			||||
          of the NOTICE file are for informational purposes only and | 
				
			||||
          do not modify the License. You may add Your own attribution | 
				
			||||
          notices within Derivative Works that You distribute, alongside | 
				
			||||
          or as an addendum to the NOTICE text from the Work, provided | 
				
			||||
          that such additional attribution notices cannot be construed | 
				
			||||
          as modifying the License. | 
				
			||||
 | 
				
			||||
      You may add Your own copyright statement to Your modifications and | 
				
			||||
      may provide additional or different license terms and conditions | 
				
			||||
      for use, reproduction, or distribution of Your modifications, or | 
				
			||||
      for any such Derivative Works as a whole, provided Your use, | 
				
			||||
      reproduction, and distribution of the Work otherwise complies with | 
				
			||||
      the conditions stated in this License. | 
				
			||||
 | 
				
			||||
   5. Submission of Contributions. Unless You explicitly state otherwise, | 
				
			||||
      any Contribution intentionally submitted for inclusion in the Work | 
				
			||||
      by You to the Licensor shall be under the terms and conditions of | 
				
			||||
      this License, without any additional terms or conditions. | 
				
			||||
      Notwithstanding the above, nothing herein shall supersede or modify | 
				
			||||
      the terms of any separate license agreement you may have executed | 
				
			||||
      with Licensor regarding such Contributions. | 
				
			||||
 | 
				
			||||
   6. Trademarks. This License does not grant permission to use the trade | 
				
			||||
      names, trademarks, service marks, or product names of the Licensor, | 
				
			||||
      except as required for reasonable and customary use in describing the | 
				
			||||
      origin of the Work and reproducing the content of the NOTICE file. | 
				
			||||
 | 
				
			||||
   7. Disclaimer of Warranty. Unless required by applicable law or | 
				
			||||
      agreed to in writing, Licensor provides the Work (and each | 
				
			||||
      Contributor provides its Contributions) on an "AS IS" BASIS, | 
				
			||||
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | 
				
			||||
      implied, including, without limitation, any warranties or conditions | 
				
			||||
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | 
				
			||||
      PARTICULAR PURPOSE. You are solely responsible for determining the | 
				
			||||
      appropriateness of using or redistributing the Work and assume any | 
				
			||||
      risks associated with Your exercise of permissions under this License. | 
				
			||||
 | 
				
			||||
   8. Limitation of Liability. In no event and under no legal theory, | 
				
			||||
      whether in tort (including negligence), contract, or otherwise, | 
				
			||||
      unless required by applicable law (such as deliberate and grossly | 
				
			||||
      negligent acts) or agreed to in writing, shall any Contributor be | 
				
			||||
      liable to You for damages, including any direct, indirect, special, | 
				
			||||
      incidental, or consequential damages of any character arising as a | 
				
			||||
      result of this License or out of the use or inability to use the | 
				
			||||
      Work (including but not limited to damages for loss of goodwill, | 
				
			||||
      work stoppage, computer failure or malfunction, or any and all | 
				
			||||
      other commercial damages or losses), even if such Contributor | 
				
			||||
      has been advised of the possibility of such damages. | 
				
			||||
 | 
				
			||||
   9. Accepting Warranty or Additional Liability. While redistributing | 
				
			||||
      the Work or Derivative Works thereof, You may choose to offer, | 
				
			||||
      and charge a fee for, acceptance of support, warranty, indemnity, | 
				
			||||
      or other liability obligations and/or rights consistent with this | 
				
			||||
      License. However, in accepting such obligations, You may act only | 
				
			||||
      on Your own behalf and on Your sole responsibility, not on behalf | 
				
			||||
      of any other Contributor, and only if You agree to indemnify, | 
				
			||||
      defend, and hold each Contributor harmless for any liability | 
				
			||||
      incurred by, or claims asserted against, such Contributor by reason | 
				
			||||
      of your accepting any such warranty or additional liability. | 
				
			||||
 | 
				
			||||
   END OF TERMS AND CONDITIONS | 
				
			||||
 | 
				
			||||
   APPENDIX: How to apply the Apache License to your work. | 
				
			||||
 | 
				
			||||
      To apply the Apache License to your work, attach the following | 
				
			||||
      boilerplate notice, with the fields enclosed by brackets "[]" | 
				
			||||
      replaced with your own identifying information. (Don't include | 
				
			||||
      the brackets!)  The text should be enclosed in the appropriate | 
				
			||||
      comment syntax for the file format. We also recommend that a | 
				
			||||
      file or class name and description of purpose be included on the | 
				
			||||
      same "printed page" as the copyright notice for easier | 
				
			||||
      identification within third-party archives. | 
				
			||||
 | 
				
			||||
   Copyright [yyyy] [name of copyright owner] | 
				
			||||
 | 
				
			||||
   Licensed under the Apache License, Version 2.0 (the "License"); | 
				
			||||
   you may not use this file except in compliance with the License. | 
				
			||||
   You may obtain a copy of the License at | 
				
			||||
 | 
				
			||||
       http://www.apache.org/licenses/LICENSE-2.0 | 
				
			||||
 | 
				
			||||
   Unless required by applicable law or agreed to in writing, software | 
				
			||||
   distributed under the License is distributed on an "AS IS" BASIS, | 
				
			||||
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
				
			||||
   See the License for the specific language governing permissions and | 
				
			||||
   limitations under the License. | 
				
			||||
@ -0,0 +1,177 @@ | 
				
			||||
# Copyright The OpenTelemetry 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.
 | 
				
			||||
 | 
				
			||||
EXAMPLES := $(shell ./get_main_pkgs.sh ./example)
 | 
				
			||||
TOOLS_MOD_DIR := ./internal/tools
 | 
				
			||||
 | 
				
			||||
# All source code and documents. Used in spell check.
 | 
				
			||||
ALL_DOCS := $(shell find . -name '*.md' -type f | sort)
 | 
				
			||||
# All directories with go.mod files related to opentelemetry library. Used for building, testing and linting.
 | 
				
			||||
ALL_GO_MOD_DIRS := $(filter-out $(TOOLS_MOD_DIR), $(shell find . -type f -name 'go.mod' -exec dirname {} \; | egrep -v '^./example' | sort)) $(shell find ./example -type f -name 'go.mod' -exec dirname {} \; | sort)
 | 
				
			||||
ALL_COVERAGE_MOD_DIRS := $(shell find . -type f -name 'go.mod' -exec dirname {} \; | egrep -v '^./example|^$(TOOLS_MOD_DIR)' | sort)
 | 
				
			||||
 | 
				
			||||
# Mac OS Catalina 10.5.x doesn't support 386. Hence skip 386 test
 | 
				
			||||
SKIP_386_TEST = false
 | 
				
			||||
UNAME_S := $(shell uname -s)
 | 
				
			||||
ifeq ($(UNAME_S),Darwin) | 
				
			||||
	SW_VERS := $(shell sw_vers -productVersion)
 | 
				
			||||
	ifeq ($(shell echo $(SW_VERS) | egrep '^(10.1[5-9]|1[1-9]|[2-9])'), $(SW_VERS))
 | 
				
			||||
		SKIP_386_TEST = true
 | 
				
			||||
	endif
 | 
				
			||||
endif | 
				
			||||
 | 
				
			||||
GOTEST_MIN = go test -timeout 30s
 | 
				
			||||
GOTEST = $(GOTEST_MIN) -race
 | 
				
			||||
GOTEST_WITH_COVERAGE = $(GOTEST) -coverprofile=coverage.out -covermode=atomic -coverpkg=./...
 | 
				
			||||
 | 
				
			||||
.DEFAULT_GOAL := precommit
 | 
				
			||||
 | 
				
			||||
.PHONY: precommit | 
				
			||||
 | 
				
			||||
TOOLS_DIR := $(abspath ./.tools)
 | 
				
			||||
 | 
				
			||||
$(TOOLS_DIR)/golangci-lint: $(TOOLS_MOD_DIR)/go.mod $(TOOLS_MOD_DIR)/go.sum $(TOOLS_MOD_DIR)/tools.go | 
				
			||||
	cd $(TOOLS_MOD_DIR) && \
 | 
				
			||||
	go build -o $(TOOLS_DIR)/golangci-lint github.com/golangci/golangci-lint/cmd/golangci-lint
 | 
				
			||||
 | 
				
			||||
$(TOOLS_DIR)/misspell: $(TOOLS_MOD_DIR)/go.mod $(TOOLS_MOD_DIR)/go.sum $(TOOLS_MOD_DIR)/tools.go | 
				
			||||
	cd $(TOOLS_MOD_DIR) && \
 | 
				
			||||
	go build -o $(TOOLS_DIR)/misspell github.com/client9/misspell/cmd/misspell
 | 
				
			||||
 | 
				
			||||
$(TOOLS_DIR)/stringer: $(TOOLS_MOD_DIR)/go.mod $(TOOLS_MOD_DIR)/go.sum $(TOOLS_MOD_DIR)/tools.go | 
				
			||||
	cd $(TOOLS_MOD_DIR) && \
 | 
				
			||||
	go build -o $(TOOLS_DIR)/stringer golang.org/x/tools/cmd/stringer
 | 
				
			||||
 | 
				
			||||
$(TOOLS_DIR)/gojq: $(TOOLS_MOD_DIR)/go.mod $(TOOLS_MOD_DIR)/go.sum $(TOOLS_MOD_DIR)/tools.go | 
				
			||||
	cd $(TOOLS_MOD_DIR) && \
 | 
				
			||||
	go build -o $(TOOLS_DIR)/gojq github.com/itchyny/gojq/cmd/gojq
 | 
				
			||||
 | 
				
			||||
precommit: dependabot-check license-check generate build lint examples test-benchmarks test | 
				
			||||
 | 
				
			||||
.PHONY: test-with-coverage | 
				
			||||
test-with-coverage: | 
				
			||||
	set -e; \
 | 
				
			||||
	printf "" > coverage.txt; \
 | 
				
			||||
	for dir in $(ALL_COVERAGE_MOD_DIRS); do \
 | 
				
			||||
	  echo "go test ./... + coverage in $${dir}"; \
 | 
				
			||||
	  (cd "$${dir}" && \
 | 
				
			||||
	 	$(GOTEST_WITH_COVERAGE) ./... && \
 | 
				
			||||
		go tool cover -html=coverage.out -o coverage.html); \
 | 
				
			||||
      [ -f "$${dir}/coverage.out" ] && cat "$${dir}/coverage.out" >> coverage.txt; \
 | 
				
			||||
	done; \
 | 
				
			||||
	sed -i.bak -e '2,$$ { /^mode: /d; }' coverage.txt
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
.PHONY: ci | 
				
			||||
ci: precommit check-clean-work-tree test-with-coverage test-386 | 
				
			||||
 | 
				
			||||
.PHONY: check-clean-work-tree | 
				
			||||
check-clean-work-tree: | 
				
			||||
	@if ! git diff --quiet; then \
 | 
				
			||||
	  echo; \
 | 
				
			||||
	  echo 'Working tree is not clean, did you forget to run "make precommit"?'; \
 | 
				
			||||
	  echo; \
 | 
				
			||||
	  git status; \
 | 
				
			||||
	  exit 1; \
 | 
				
			||||
	fi
 | 
				
			||||
 | 
				
			||||
.PHONY: build | 
				
			||||
build: | 
				
			||||
	# TODO: Fix this on windows.
 | 
				
			||||
	set -e; for dir in $(ALL_GO_MOD_DIRS); do \
 | 
				
			||||
	  echo "compiling all packages in $${dir}"; \
 | 
				
			||||
	  (cd "$${dir}" && \
 | 
				
			||||
	    go build ./... && \
 | 
				
			||||
	    go test -run xxxxxMatchNothingxxxxx ./... >/dev/null); \
 | 
				
			||||
	done
 | 
				
			||||
 | 
				
			||||
.PHONY: test | 
				
			||||
test: | 
				
			||||
	set -e; for dir in $(ALL_GO_MOD_DIRS); do \
 | 
				
			||||
	  echo "go test ./... + race in $${dir}"; \
 | 
				
			||||
	  (cd "$${dir}" && \
 | 
				
			||||
	    $(GOTEST) ./...); \
 | 
				
			||||
	done
 | 
				
			||||
 | 
				
			||||
.PHONY: test-386 | 
				
			||||
test-386: | 
				
			||||
	if [ $(SKIP_386_TEST) = true ] ; then \
 | 
				
			||||
	  echo "skipping the test for GOARCH 386 as it is not supported on the current OS"; \
 | 
				
			||||
	else \
 | 
				
			||||
	  set -e; for dir in $(ALL_GO_MOD_DIRS); do \
 | 
				
			||||
	    echo "go test ./... GOARCH 386 in $${dir}"; \
 | 
				
			||||
	    (cd "$${dir}" && \
 | 
				
			||||
	      GOARCH=386 $(GOTEST_MIN) ./...); \
 | 
				
			||||
	  done; \
 | 
				
			||||
	fi
 | 
				
			||||
 | 
				
			||||
.PHONY: examples | 
				
			||||
examples: | 
				
			||||
	@set -e; for ex in $(EXAMPLES); do \
 | 
				
			||||
	  echo "Building $${ex}"; \
 | 
				
			||||
	  (cd "$${ex}" && \
 | 
				
			||||
	   go build .); \
 | 
				
			||||
	done
 | 
				
			||||
 | 
				
			||||
.PHONY: test-benchmarks | 
				
			||||
test-benchmarks: | 
				
			||||
	@set -e; for dir in $(ALL_GO_MOD_DIRS); do \
 | 
				
			||||
	  echo "test benchmarks in $${dir}"; \
 | 
				
			||||
	  (cd "$${dir}" && go test -test.benchtime=1ms -run=NONE -bench=. ./...) > /dev/null; \
 | 
				
			||||
	done
 | 
				
			||||
 | 
				
			||||
.PHONY: lint | 
				
			||||
lint: $(TOOLS_DIR)/golangci-lint $(TOOLS_DIR)/misspell | 
				
			||||
	set -e; for dir in $(ALL_GO_MOD_DIRS); do \
 | 
				
			||||
	  echo "golangci-lint in $${dir}"; \
 | 
				
			||||
	  (cd "$${dir}" && \
 | 
				
			||||
	    $(TOOLS_DIR)/golangci-lint run --fix && \
 | 
				
			||||
	    $(TOOLS_DIR)/golangci-lint run); \
 | 
				
			||||
	done
 | 
				
			||||
	$(TOOLS_DIR)/misspell -w $(ALL_DOCS)
 | 
				
			||||
	set -e; for dir in $(ALL_GO_MOD_DIRS) $(TOOLS_MOD_DIR); do \
 | 
				
			||||
	  echo "go mod tidy in $${dir}"; \
 | 
				
			||||
	  (cd "$${dir}" && \
 | 
				
			||||
	    go mod tidy); \
 | 
				
			||||
	done
 | 
				
			||||
 | 
				
			||||
generate: $(TOOLS_DIR)/stringer | 
				
			||||
	set -e; for dir in $(ALL_GO_MOD_DIRS); do \
 | 
				
			||||
	  echo "running generators in $${dir}"; \
 | 
				
			||||
	  (cd "$${dir}" && \
 | 
				
			||||
	    PATH="$(TOOLS_DIR):$${PATH}" go generate ./...); \
 | 
				
			||||
	done
 | 
				
			||||
 | 
				
			||||
.PHONY: license-check | 
				
			||||
license-check: | 
				
			||||
	@licRes=$$(for f in $$(find . -type f \( -iname '*.go' -o -iname '*.sh' \) ! -path './vendor/*' ! -path './exporters/otlp/internal/opentelemetry-proto/*') ; do \
 | 
				
			||||
	           awk '/Copyright The OpenTelemetry Authors|generated|GENERATED/ && NR<=3 { found=1; next } END { if (!found) print FILENAME }' $$f; \
 | 
				
			||||
	   done); \
 | 
				
			||||
	   if [ -n "$${licRes}" ]; then \
 | 
				
			||||
	           echo "license header checking failed:"; echo "$${licRes}"; \
 | 
				
			||||
	           exit 1; \
 | 
				
			||||
	   fi
 | 
				
			||||
 | 
				
			||||
.PHONY: dependabot-check | 
				
			||||
dependabot-check: | 
				
			||||
	@result=$$( \
 | 
				
			||||
		for f in $$( find . -type f -name go.mod -exec dirname {} \; | sed 's/^.\/\?/\//' ); \
 | 
				
			||||
			do grep -q "$$f" .github/dependabot.yml \
 | 
				
			||||
			|| echo "$$f"; \
 | 
				
			||||
		done; \
 | 
				
			||||
	); \
 | 
				
			||||
	if [ -n "$$result" ]; then \
 | 
				
			||||
		echo "missing go.mod dependabot check:"; echo "$$result"; \
 | 
				
			||||
		exit 1; \
 | 
				
			||||
	fi
 | 
				
			||||
@ -0,0 +1,129 @@ | 
				
			||||
#   -*- mode: makefile; -*- | 
				
			||||
# Copyright The OpenTelemetry 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. | 
				
			||||
# | 
				
			||||
# This Makefile.proto has rules to generate go code for otlp | 
				
			||||
# exporter. It does it by copying the proto files from | 
				
			||||
# `exporters/otlp/internal/opentelemetry-proto` (which is a | 
				
			||||
# submodule that needs to be checked out) into `gen/proto`, changing | 
				
			||||
# the go_package option to a valid string, generating the go files and | 
				
			||||
# finally copying the files into the module. The files are not | 
				
			||||
# generated in place, because protoc generates a too-deep directory | 
				
			||||
# structure. | 
				
			||||
# | 
				
			||||
# Currently, all the generated code is in | 
				
			||||
# `exporters/otlp/internal/opentelemetry-proto-gen`. | 
				
			||||
# | 
				
			||||
# Prereqs: wget (for downloading the zip file with protoc binary), | 
				
			||||
# unzip (for unpacking the archive), rsync (for copying back the | 
				
			||||
# generated files). | 
				
			||||
 | 
				
			||||
PROTOC_VERSION := 3.14.0 | 
				
			||||
 | 
				
			||||
TOOLS_DIR                       := $(abspath ./.tools) | 
				
			||||
TOOLS_MOD_DIR                   := ./internal/tools | 
				
			||||
PROTOBUF_VERSION                := v1 | 
				
			||||
OTEL_PROTO_SUBMODULE            := exporters/otlp/internal/opentelemetry-proto | 
				
			||||
GEN_TEMP_DIR                    := gen | 
				
			||||
SUBMODULE_PROTO_FILES           := $(wildcard $(OTEL_PROTO_SUBMODULE)/opentelemetry/proto/*/$(PROTOBUF_VERSION)/*.proto) $(wildcard $(OTEL_PROTO_SUBMODULE)/opentelemetry/proto/collector/*/$(PROTOBUF_VERSION)/*.proto) | 
				
			||||
 | 
				
			||||
ifeq ($(strip $(SUBMODULE_PROTO_FILES)),) | 
				
			||||
$(error Submodule at $(OTEL_PROTO_SUBMODULE) is not checked out, use "git submodule update --init") | 
				
			||||
endif | 
				
			||||
 | 
				
			||||
PROTOBUF_GEN_DIR   := exporters/otlp/internal/opentelemetry-proto-gen | 
				
			||||
PROTOBUF_TEMP_DIR  := $(GEN_TEMP_DIR)/pb-go | 
				
			||||
PROTO_SOURCE_DIR   := $(GEN_TEMP_DIR)/proto | 
				
			||||
SOURCE_PROTO_FILES := $(subst $(OTEL_PROTO_SUBMODULE),$(PROTO_SOURCE_DIR),$(SUBMODULE_PROTO_FILES)) | 
				
			||||
 | 
				
			||||
.DEFAULT_GOAL := protobuf | 
				
			||||
 | 
				
			||||
UNAME_S := $(shell uname -s) | 
				
			||||
UNAME_M := $(shell uname -m) | 
				
			||||
 | 
				
			||||
ifeq ($(UNAME_S),Linux) | 
				
			||||
 | 
				
			||||
PROTOC_OS := linux | 
				
			||||
PROTOC_ARCH := $(UNAME_M) | 
				
			||||
 | 
				
			||||
else ifeq ($(UNAME_S),Darwin) | 
				
			||||
 | 
				
			||||
PROTOC_OS := osx | 
				
			||||
PROTOC_ARCH := x86_64 | 
				
			||||
 | 
				
			||||
endif | 
				
			||||
 | 
				
			||||
PROTOC_ZIP_URL := https://github.com/protocolbuffers/protobuf/releases/download/v$(PROTOC_VERSION)/protoc-$(PROTOC_VERSION)-$(PROTOC_OS)-$(PROTOC_ARCH).zip | 
				
			||||
 | 
				
			||||
$(TOOLS_DIR)/PROTOC_$(PROTOC_VERSION): | 
				
			||||
	@rm -f "$(TOOLS_DIR)"/PROTOC_* && \ | 
				
			||||
	touch "$@" | 
				
			||||
 | 
				
			||||
# Depend on a versioned file (like PROTOC_3.14.0), so when version | 
				
			||||
# gets bumped, we will depend on a nonexistent file and thus download | 
				
			||||
# a newer version. | 
				
			||||
$(TOOLS_DIR)/protoc/bin/protoc: $(TOOLS_DIR)/PROTOC_$(PROTOC_VERSION) | 
				
			||||
	echo "Fetching protoc $(PROTOC_VERSION)" && \ | 
				
			||||
	rm -rf $(TOOLS_DIR)/protoc && \ | 
				
			||||
	wget -O $(TOOLS_DIR)/protoc.zip $(PROTOC_ZIP_URL) && \ | 
				
			||||
	unzip $(TOOLS_DIR)/protoc.zip -d $(TOOLS_DIR)/protoc-tmp && \ | 
				
			||||
	rm $(TOOLS_DIR)/protoc.zip && \ | 
				
			||||
	touch $(TOOLS_DIR)/protoc-tmp/bin/protoc && \ | 
				
			||||
	mv $(TOOLS_DIR)/protoc-tmp $(TOOLS_DIR)/protoc | 
				
			||||
 | 
				
			||||
$(TOOLS_DIR)/protoc-gen-gogofast: $(TOOLS_MOD_DIR)/go.mod $(TOOLS_MOD_DIR)/go.sum $(TOOLS_MOD_DIR)/tools.go | 
				
			||||
	cd $(TOOLS_MOD_DIR) && \ | 
				
			||||
	go build -o $(TOOLS_DIR)/protoc-gen-gogofast github.com/gogo/protobuf/protoc-gen-gogofast && \ | 
				
			||||
	go mod tidy | 
				
			||||
 | 
				
			||||
# Return a sed expression for replacing the go_package option in proto | 
				
			||||
# file with a one that's valid for us. | 
				
			||||
# | 
				
			||||
# Example: $(call get-sed-expr,$(PROTOBUF_GEN_DIR)) | 
				
			||||
define get-sed-expr | 
				
			||||
's,go_package = "github.com/open-telemetry/opentelemetry-proto/gen/go,go_package = "go.opentelemetry.io/otel/$(1),' | 
				
			||||
endef | 
				
			||||
 | 
				
			||||
.PHONY: protobuf | 
				
			||||
protobuf: protobuf-source gen-protobuf copy-protobufs | 
				
			||||
 | 
				
			||||
.PHONY: protobuf-source | 
				
			||||
protobuf-source: $(SOURCE_PROTO_FILES) | 
				
			||||
 | 
				
			||||
# This copies proto files from submodule into $(PROTO_SOURCE_DIR), | 
				
			||||
# thus satisfying the $(SOURCE_PROTO_FILES) prerequisite. The copies | 
				
			||||
# have their package name replaced by go.opentelemetry.io/otel. | 
				
			||||
$(PROTO_SOURCE_DIR)/%.proto: $(OTEL_PROTO_SUBMODULE)/%.proto | 
				
			||||
	@ \ | 
				
			||||
	mkdir -p $(@D); \ | 
				
			||||
	sed -e $(call get-sed-expr,$(PROTOBUF_GEN_DIR)) "$<" >"$@.tmp"; \ | 
				
			||||
	mv "$@.tmp" "$@" | 
				
			||||
 | 
				
			||||
.PHONY: gen-protobuf | 
				
			||||
gen-protobuf: $(SOURCE_PROTO_FILES) $(TOOLS_DIR)/protoc-gen-gogofast $(TOOLS_DIR)/protoc/bin/protoc | 
				
			||||
	@ \ | 
				
			||||
	mkdir -p "$(PROTOBUF_TEMP_DIR)"; \ | 
				
			||||
	set -e; for f in $^; do \ | 
				
			||||
	  if [[ "$${f}" == $(TOOLS_DIR)/* ]]; then continue; fi; \ | 
				
			||||
	  echo "protoc $${f#"$(PROTO_SOURCE_DIR)/"}"; \ | 
				
			||||
	  PATH="$(TOOLS_DIR):$${PATH}" $(TOOLS_DIR)/protoc/bin/protoc --proto_path="$(PROTO_SOURCE_DIR)" --gogofast_out="plugins=grpc:$(PROTOBUF_TEMP_DIR)" "$${f}"; \ | 
				
			||||
	done | 
				
			||||
 | 
				
			||||
.PHONY: copy-protobufs | 
				
			||||
copy-protobufs: | 
				
			||||
	@rsync -a $(PROTOBUF_TEMP_DIR)/go.opentelemetry.io/otel/exporters . | 
				
			||||
 | 
				
			||||
.PHONY: clean | 
				
			||||
clean: | 
				
			||||
	rm -rf $(GEN_TEMP_DIR) | 
				
			||||
@ -0,0 +1,71 @@ | 
				
			||||
# OpenTelemetry-Go | 
				
			||||
 | 
				
			||||
[](https://github.com/open-telemetry/opentelemetry-go/actions?query=workflow%3Aci+branch%3Amaster) | 
				
			||||
[](https://pkg.go.dev/go.opentelemetry.io/otel) | 
				
			||||
[](https://goreportcard.com/report/go.opentelemetry.io/otel) | 
				
			||||
[](https://gitter.im/open-telemetry/opentelemetry-go?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) | 
				
			||||
 | 
				
			||||
The Go [OpenTelemetry](https://opentelemetry.io/) implementation. | 
				
			||||
 | 
				
			||||
## Project Status | 
				
			||||
 | 
				
			||||
**Warning**: this project is currently in a pre-GA phase. Backwards | 
				
			||||
incompatible changes may be introduced in subsequent minor version releases as | 
				
			||||
we work to track the evolving OpenTelemetry specification and user feedback. | 
				
			||||
 | 
				
			||||
Our progress towards a GA release candidate is tracked in [this project | 
				
			||||
board](https://github.com/orgs/open-telemetry/projects/5). This release | 
				
			||||
candidate will follow semantic versioning and will be released with a major | 
				
			||||
version greater than zero. | 
				
			||||
 | 
				
			||||
Progress and status specific to this repository is tracked in our local | 
				
			||||
[project boards](https://github.com/open-telemetry/opentelemetry-go/projects) | 
				
			||||
and | 
				
			||||
[milestones](https://github.com/open-telemetry/opentelemetry-go/milestones). | 
				
			||||
 | 
				
			||||
Project versioning information and stability guarantees can be found in the | 
				
			||||
[versioning documentation](./VERSIONING.md). | 
				
			||||
 | 
				
			||||
## Getting Started | 
				
			||||
 | 
				
			||||
You can find a getting started guide on [opentelemetry.io](https://opentelemetry.io/docs/go/getting-started/). | 
				
			||||
 | 
				
			||||
OpenTelemetry's goal is to provide a single set of APIs to capture distributed | 
				
			||||
traces and metrics from your application and send them to an observability | 
				
			||||
platform. This project allows you to do just that for applications written in | 
				
			||||
Go. There are two steps to this process: instrument your application, and | 
				
			||||
configure an exporter. | 
				
			||||
 | 
				
			||||
### Instrumentation | 
				
			||||
 | 
				
			||||
To start capturing distributed traces and metric events from your application | 
				
			||||
it first needs to be instrumented. The easiest way to do this is by using an | 
				
			||||
instrumentation library for your code. Be sure to check out [the officially | 
				
			||||
supported instrumentation | 
				
			||||
libraries](https://github.com/open-telemetry/opentelemetry-go-contrib/tree/master/instrumentation). | 
				
			||||
 | 
				
			||||
If you need to extend the telemetry an instrumentation library provides or want | 
				
			||||
to build your own instrumentation for your application directly you will need | 
				
			||||
to use the | 
				
			||||
[go.opentelemetry.io/otel/api](https://pkg.go.dev/go.opentelemetry.io/otel/api) | 
				
			||||
package. The included [examples](./example/) are a good way to see some | 
				
			||||
practical uses of this process. | 
				
			||||
 | 
				
			||||
### Export | 
				
			||||
 | 
				
			||||
Now that your application is instrumented to collect telemetry, it needs an | 
				
			||||
export pipeline to send that telemetry to an observability platform. | 
				
			||||
 | 
				
			||||
You can find officially supported exporters [here](./exporters/) and in the | 
				
			||||
companion [contrib | 
				
			||||
repository](https://github.com/open-telemetry/opentelemetry-go-contrib/tree/master/exporters/metric). | 
				
			||||
Additionally, there are many vendor specific or 3rd party exporters for | 
				
			||||
OpenTelemetry. These exporters are broken down by | 
				
			||||
[trace](https://pkg.go.dev/go.opentelemetry.io/otel/sdk/export/trace?tab=importedby) | 
				
			||||
and | 
				
			||||
[metric](https://pkg.go.dev/go.opentelemetry.io/otel/sdk/export/metric?tab=importedby) | 
				
			||||
support. | 
				
			||||
 | 
				
			||||
## Contributing | 
				
			||||
 | 
				
			||||
See the [contributing documentation](CONTRIBUTING.md). | 
				
			||||
@ -0,0 +1,81 @@ | 
				
			||||
# Release Process | 
				
			||||
 | 
				
			||||
## Pre-Release | 
				
			||||
 | 
				
			||||
Update go.mod for submodules to depend on the new release which will happen in the next step. | 
				
			||||
 | 
				
			||||
1. Run the pre-release script. It creates a branch `pre_release_<new tag>` that will contain all release changes. | 
				
			||||
 | 
				
			||||
    ``` | 
				
			||||
    ./pre_release.sh -t <new tag> | 
				
			||||
    ``` | 
				
			||||
 | 
				
			||||
2. Verify the changes. | 
				
			||||
 | 
				
			||||
    ``` | 
				
			||||
    git diff master | 
				
			||||
    ``` | 
				
			||||
 | 
				
			||||
    This should have changed the version for all modules to be `<new tag>`. | 
				
			||||
 | 
				
			||||
3. Update the [Changelog](./CHANGELOG.md). | 
				
			||||
   - Make sure all relevant changes for this release are included and are in language that non-contributors to the project can understand. | 
				
			||||
       To verify this, you can look directly at the commits since the `<last tag>`. | 
				
			||||
 | 
				
			||||
       ``` | 
				
			||||
       git --no-pager log --pretty=oneline "<last tag>..HEAD" | 
				
			||||
       ``` | 
				
			||||
 | 
				
			||||
   - Move all the `Unreleased` changes into a new section following the title scheme (`[<new tag>] - <date of release>`). | 
				
			||||
   - Update all the appropriate links at the bottom. | 
				
			||||
 | 
				
			||||
4. Push the changes to upstream and create a Pull Request on GitHub. | 
				
			||||
    Be sure to include the curated changes from the [Changelog](./CHANGELOG.md) in the description. | 
				
			||||
 | 
				
			||||
 | 
				
			||||
## Tag | 
				
			||||
 | 
				
			||||
Once the Pull Request with all the version changes has been approved and merged it is time to tag the merged commit. | 
				
			||||
 | 
				
			||||
***IMPORTANT***: It is critical you use the same tag that you used in the Pre-Release step! | 
				
			||||
Failure to do so will leave things in a broken state. | 
				
			||||
 | 
				
			||||
***IMPORTANT***: [There is currently no way to remove an incorrectly tagged version of a Go module](https://github.com/golang/go/issues/34189). | 
				
			||||
It is critical you make sure the version you push upstream is correct. | 
				
			||||
[Failure to do so will lead to minor emergencies and tough to work around](https://github.com/open-telemetry/opentelemetry-go/issues/331). | 
				
			||||
 | 
				
			||||
1. Run the tag.sh script using the `<commit-hash>` of the commit on the master branch for the merged Pull Request. | 
				
			||||
 | 
				
			||||
    ``` | 
				
			||||
    ./tag.sh <new tag> <commit-hash> | 
				
			||||
    ``` | 
				
			||||
 | 
				
			||||
2. Push tags to the upstream remote (not your fork: `github.com/open-telemetry/opentelemetry-go.git`). | 
				
			||||
    Make sure you push all sub-modules as well. | 
				
			||||
 | 
				
			||||
    ``` | 
				
			||||
    git push upstream <new tag> | 
				
			||||
    git push upstream <submodules-path/new tag> | 
				
			||||
    ... | 
				
			||||
    ``` | 
				
			||||
 | 
				
			||||
## Release | 
				
			||||
 | 
				
			||||
Finally create a Release for the new `<new tag>` on GitHub. | 
				
			||||
The release body should include all the release notes from the Changelog for this release. | 
				
			||||
Additionally, the `tag.sh` script generates commit logs since last release which can be used to supplement the release notes. | 
				
			||||
 | 
				
			||||
## Verify Examples | 
				
			||||
 | 
				
			||||
After releasing verify that examples build outside of the repository. | 
				
			||||
 | 
				
			||||
``` | 
				
			||||
./verify_examples.sh | 
				
			||||
``` | 
				
			||||
 | 
				
			||||
The script copies examples into a different directory removes any `replace` declarations in `go.mod` and builds them. | 
				
			||||
This ensures they build with the published release, not the local copy. | 
				
			||||
 | 
				
			||||
## Contrib Repository | 
				
			||||
 | 
				
			||||
Once verified be sure to [make a release for the `contrib` repository](https://github.com/open-telemetry/opentelemetry-go-contrib/blob/master/RELEASING.md) that uses this release. | 
				
			||||
@ -0,0 +1,217 @@ | 
				
			||||
# Versioning | 
				
			||||
 | 
				
			||||
This document describes the versioning policy for this repository. This policy | 
				
			||||
is designed so the following goals can be achieved. | 
				
			||||
 | 
				
			||||
**Users are provided a codebase of value that is stable and secure.** | 
				
			||||
 | 
				
			||||
## Policy | 
				
			||||
 | 
				
			||||
* Versioning of this project will be idiomatic of a Go project using [Go | 
				
			||||
  modules](https://github.com/golang/go/wiki/Modules). | 
				
			||||
  * [Semantic import | 
				
			||||
    versioning](https://github.com/golang/go/wiki/Modules#semantic-import-versioning) | 
				
			||||
    will be used. | 
				
			||||
    * Versions will comply with [semver 2.0](https://semver.org/spec/v2.0.0.html). | 
				
			||||
    * If a module is version `v2` or higher, the major version of the module | 
				
			||||
      must be included as a `/vN` at the end of the module paths used in | 
				
			||||
      `go.mod` files (e.g., `module go.opentelemetry.io/otel/v2`, `require | 
				
			||||
      go.opentelemetry.io/otel/v2 v2.0.1`) and in the package import path | 
				
			||||
      (e.g., `import "go.opentelemetry.io/otel/v2/trace"`). This includes the | 
				
			||||
      paths used in `go get` commands (e.g., `go get | 
				
			||||
      go.opentelemetry.io/otel/v2@v2.0.1`.  Note there is both a `/v2` and a | 
				
			||||
      `@v2.0.1` in that example. One way to think about it is that the module | 
				
			||||
      name now includes the `/v2`, so include `/v2` whenever you are using the | 
				
			||||
      module name). | 
				
			||||
    * If a module is version `v0` or `v1`, do not include the major version in | 
				
			||||
      either the module path or the import path. | 
				
			||||
  * Modules will be used to encapsulate signals and components. | 
				
			||||
    * Experimental modules still under active development will be versioned at | 
				
			||||
      `v0` to imply the stability guarantee defined by | 
				
			||||
      [semver](https://semver.org/spec/v2.0.0.html#spec-item-4). | 
				
			||||
 | 
				
			||||
      > Major version zero (0.y.z) is for initial development. Anything MAY | 
				
			||||
      > change at any time. The public API SHOULD NOT be considered stable. | 
				
			||||
 | 
				
			||||
    * Mature modules for which we guarantee a stable public API will be versioned | 
				
			||||
      with a major version greater than `v0`. | 
				
			||||
      * The decision to make a module stable will be made on a case-by-case | 
				
			||||
        basis by the maintainers of this project. | 
				
			||||
    * Experimental modules will start their versioning at `v0.0.0` and will | 
				
			||||
      increment their minor version when backwards incompatible changes are | 
				
			||||
      released and increment their patch version when backwards compatible | 
				
			||||
      changes are released. | 
				
			||||
    * All stable modules that use the same major version number will use the | 
				
			||||
      same entire version number. | 
				
			||||
      * Stable modules may be released with an incremented minor or patch | 
				
			||||
        version even though that module has not been changed, but rather so | 
				
			||||
        that it will remain at the same version as other stable modules that | 
				
			||||
        did undergo change. | 
				
			||||
      * When an experimental module becomes stable a new stable module version | 
				
			||||
        will be released and will include this now stable module. The new | 
				
			||||
        stable module version will be an increment of the minor version number | 
				
			||||
        and will be applied to all existing stable modules as well as the newly | 
				
			||||
        stable module being released. | 
				
			||||
* Versioning of the associated [contrib | 
				
			||||
  repository](https://github.com/open-telemetry/opentelemetry-go-contrib) of | 
				
			||||
  this project will be idiomatic of a Go project using [Go | 
				
			||||
  modules](https://github.com/golang/go/wiki/Modules). | 
				
			||||
  * [Semantic import | 
				
			||||
    versioning](https://github.com/golang/go/wiki/Modules#semantic-import-versioning) | 
				
			||||
    will be used. | 
				
			||||
    * Versions will comply with [semver 2.0](https://semver.org/spec/v2.0.0.html). | 
				
			||||
    * If a module is version `v2` or higher, the | 
				
			||||
      major version of the module must be included as a `/vN` at the end of the | 
				
			||||
      module paths used in `go.mod` files (e.g., `module | 
				
			||||
      go.opentelemetry.io/contrib/instrumentation/host/v2`, `require | 
				
			||||
      go.opentelemetry.io/contrib/instrumentation/host/v2 v2.0.1`) and in the | 
				
			||||
      package import path (e.g., `import | 
				
			||||
      "go.opentelemetry.io/contrib/instrumentation/host/v2"`). This includes | 
				
			||||
      the paths used in `go get` commands (e.g., `go get | 
				
			||||
      go.opentelemetry.io/contrib/instrumentation/host/v2@v2.0.1`.  Note there | 
				
			||||
      is both a `/v2` and a `@v2.0.1` in that example. One way to think about | 
				
			||||
      it is that the module name now includes the `/v2`, so include `/v2` | 
				
			||||
      whenever you are using the module name). | 
				
			||||
    * If a module is version `v0` or `v1`, do not include the major version | 
				
			||||
      in either the module path or the import path. | 
				
			||||
  * In addition to public APIs, telemetry produced by stable instrumentation | 
				
			||||
    will remain stable and backwards compatible. This is to avoid breaking | 
				
			||||
    alerts and dashboard. | 
				
			||||
  * Modules will be used to encapsulate instrumentation, detectors, exporters, | 
				
			||||
    propagators, and any other independent sets of related components. | 
				
			||||
    * Experimental modules still under active development will be versioned at | 
				
			||||
      `v0` to imply the stability guarantee defined by | 
				
			||||
      [semver](https://semver.org/spec/v2.0.0.html#spec-item-4). | 
				
			||||
 | 
				
			||||
      > Major version zero (0.y.z) is for initial development. Anything MAY | 
				
			||||
      > change at any time. The public API SHOULD NOT be considered stable. | 
				
			||||
 | 
				
			||||
    * Mature modules for which we guarantee a stable public API and telemetry will | 
				
			||||
      be versioned with a major version greater than `v0`. | 
				
			||||
    * Experimental modules will start their versioning at `v0.0.0` and will | 
				
			||||
      increment their minor version when backwards incompatible changes are | 
				
			||||
      released and increment their patch version when backwards compatible | 
				
			||||
      changes are released. | 
				
			||||
    * Stable contrib modules cannot depend on experimental modules from this | 
				
			||||
      project. | 
				
			||||
    * All stable contrib modules of the same major version with this project | 
				
			||||
      will use the same entire version as this project. | 
				
			||||
      * Stable modules may be released with an incremented minor or patch | 
				
			||||
        version even though that module's code has not been changed. Instead | 
				
			||||
        the only change that will have been included is to have updated that | 
				
			||||
        modules dependency on this project's stable APIs. | 
				
			||||
      * When an experimental module in contrib becomes stable a new stable | 
				
			||||
        module version will be released and will include this now stable | 
				
			||||
        module. The new stable module version will be an increment of the minor | 
				
			||||
        version number and will be applied to all existing stable contrib | 
				
			||||
        modules, this project's modules, and the newly stable module being | 
				
			||||
        released. | 
				
			||||
  * Contrib modules will be kept up to date with this project's releases. | 
				
			||||
    * Due to the dependency contrib modules will implicitly have on this | 
				
			||||
      project's modules the release of stable contrib modules to match the | 
				
			||||
      released version number will be staggered after this project's release. | 
				
			||||
      There is no explicit time guarantee for how long after this projects | 
				
			||||
      release the contrib release will be. Effort should be made to keep them | 
				
			||||
      as close in time as possible. | 
				
			||||
    * No additional stable release in this project can be made until the | 
				
			||||
      contrib repository has a matching stable release. | 
				
			||||
    * No release can be made in the contrib repository after this project's | 
				
			||||
      stable release except for a stable release of the contrib repository. | 
				
			||||
* GitHub releases will be made for all releases. | 
				
			||||
* Go modules will be made available at Go package mirrors. | 
				
			||||
 | 
				
			||||
## Example Versioning Lifecycle | 
				
			||||
 | 
				
			||||
To better understand the implementation of the above policy the following | 
				
			||||
example is provided. This project is simplified to include only the following | 
				
			||||
modules and their versions: | 
				
			||||
 | 
				
			||||
* `otel`: `v0.14.0` | 
				
			||||
* `otel/trace`: `v0.14.0` | 
				
			||||
* `otel/metric`: `v0.14.0` | 
				
			||||
* `otel/baggage`: `v0.14.0` | 
				
			||||
* `otel/sdk/trace`: `v0.14.0` | 
				
			||||
* `otel/sdk/metric`: `v0.14.0` | 
				
			||||
 | 
				
			||||
These modules have been developed to a point where the `otel/trace`, | 
				
			||||
`otel/baggage`, and `otel/sdk/trace` modules have reached a point that they | 
				
			||||
should be considered for a stable release. The `otel/metric` and | 
				
			||||
`otel/sdk/metric` are still under active development and the `otel` module | 
				
			||||
depends on both `otel/trace` and `otel/metric`. | 
				
			||||
 | 
				
			||||
The `otel` package is refactored to remove its dependencies on `otel/metric` so | 
				
			||||
it can be released as stable as well. With that done the following release | 
				
			||||
candidates are made: | 
				
			||||
 | 
				
			||||
* `otel`: `v1.0.0-rc.1` | 
				
			||||
* `otel/trace`: `v1.0.0-rc.1` | 
				
			||||
* `otel/baggage`: `v1.0.0-rc.1` | 
				
			||||
* `otel/sdk/trace`: `v1.0.0-rc.1` | 
				
			||||
 | 
				
			||||
The `otel/metric` and `otel/sdk/metric` modules remain at `v0.14.0`. | 
				
			||||
 | 
				
			||||
A few minor issues are discovered in the `otel/trace` package. These issues are | 
				
			||||
resolved with some minor, but backwards incompatible, changes and are released | 
				
			||||
as a second release candidate: | 
				
			||||
 | 
				
			||||
* `otel`: `v1.0.0-rc.2` | 
				
			||||
* `otel/trace`: `v1.0.0-rc.2` | 
				
			||||
* `otel/baggage`: `v1.0.0-rc.2` | 
				
			||||
* `otel/sdk/trace`: `v1.0.0-rc.2` | 
				
			||||
 | 
				
			||||
Notice that all module version numbers are incremented to adhere to our | 
				
			||||
versioning policy. | 
				
			||||
 | 
				
			||||
After these release candidates have been evaluated to satisfaction, they are | 
				
			||||
released as version `v1.0.0`. | 
				
			||||
 | 
				
			||||
* `otel`: `v1.0.0` | 
				
			||||
* `otel/trace`: `v1.0.0` | 
				
			||||
* `otel/baggage`: `v1.0.0` | 
				
			||||
* `otel/sdk/trace`: `v1.0.0` | 
				
			||||
 | 
				
			||||
Since both the `go` utility and the Go module system support [the semantic | 
				
			||||
versioning definition of | 
				
			||||
precedence](https://semver.org/spec/v2.0.0.html#spec-item-11), this release | 
				
			||||
will correctly be interpreted as the successor to the previous release | 
				
			||||
candidates. | 
				
			||||
 | 
				
			||||
Active development of this project continues. The `otel/metric` module now has | 
				
			||||
backwards incompatible changes to its API that need to be released and the | 
				
			||||
`otel/baggage` module has a minor bug fix that needs to be released. The | 
				
			||||
following release is made: | 
				
			||||
 | 
				
			||||
* `otel`: `v1.0.1` | 
				
			||||
* `otel/trace`: `v1.0.1` | 
				
			||||
* `otel/metric`: `v0.15.0` | 
				
			||||
* `otel/baggage`: `v1.0.1` | 
				
			||||
* `otel/sdk/trace`: `v1.0.1` | 
				
			||||
* `otel/sdk/metric`: `v0.15.0` | 
				
			||||
 | 
				
			||||
Notice that, again, all stable module versions are incremented in unison and | 
				
			||||
the `otel/sdk/metric` package, which depends on the `otel/metric` package, also | 
				
			||||
bumped its version. This bump of the `otel/sdk/metric` package makes sense | 
				
			||||
given their coupling, though it is not explicitly required by our versioning | 
				
			||||
policy. | 
				
			||||
 | 
				
			||||
As we progress, the `otel/metric` and `otel/sdk/metric` packages have reached a | 
				
			||||
point where they should be evaluated for stability. The `otel` module is | 
				
			||||
reintegrated with the `otel/metric` package and the following release is made: | 
				
			||||
 | 
				
			||||
* `otel`: `v1.1.0-rc.1` | 
				
			||||
* `otel/trace`: `v1.1.0-rc.1` | 
				
			||||
* `otel/metric`: `v1.1.0-rc.1` | 
				
			||||
* `otel/baggage`: `v1.1.0-rc.1` | 
				
			||||
* `otel/sdk/trace`: `v1.1.0-rc.1` | 
				
			||||
* `otel/sdk/metric`: `v1.1.0-rc.1` | 
				
			||||
 | 
				
			||||
All the modules are evaluated and determined to a viable stable release. They | 
				
			||||
are then released as version `v1.1.0` (the minor version is incremented to | 
				
			||||
indicate the addition of new signal). | 
				
			||||
 | 
				
			||||
* `otel`: `v1.1.0` | 
				
			||||
* `otel/trace`: `v1.1.0` | 
				
			||||
* `otel/metric`: `v1.1.0` | 
				
			||||
* `otel/baggage`: `v1.1.0` | 
				
			||||
* `otel/sdk/trace`: `v1.1.0` | 
				
			||||
* `otel/sdk/metric`: `v1.1.0` | 
				
			||||
@ -0,0 +1,106 @@ | 
				
			||||
// Copyright The OpenTelemetry Authors
 | 
				
			||||
//
 | 
				
			||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||
// you may not use this file except in compliance with the License.
 | 
				
			||||
// You may obtain a copy of the License at
 | 
				
			||||
//
 | 
				
			||||
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||
//
 | 
				
			||||
// Unless required by applicable law or agreed to in writing, software
 | 
				
			||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||
// See the License for the specific language governing permissions and
 | 
				
			||||
// limitations under the License.
 | 
				
			||||
 | 
				
			||||
package codes // import "go.opentelemetry.io/otel/codes"
 | 
				
			||||
 | 
				
			||||
import ( | 
				
			||||
	"encoding/json" | 
				
			||||
	"fmt" | 
				
			||||
	"strconv" | 
				
			||||
) | 
				
			||||
 | 
				
			||||
const ( | 
				
			||||
	// Unset is the default status code.
 | 
				
			||||
	Unset Code = 0 | 
				
			||||
	// Error indicates the operation contains an error.
 | 
				
			||||
	Error Code = 1 | 
				
			||||
	// Ok indicates operation has been validated by an Application developers
 | 
				
			||||
	// or Operator to have completed successfully, or contain no error.
 | 
				
			||||
	Ok Code = 2 | 
				
			||||
 | 
				
			||||
	maxCode = 3 | 
				
			||||
) | 
				
			||||
 | 
				
			||||
// Code is an 32-bit representation of a status state.
 | 
				
			||||
type Code uint32 | 
				
			||||
 | 
				
			||||
var codeToStr = map[Code]string{ | 
				
			||||
	Unset: "Unset", | 
				
			||||
	Error: "Error", | 
				
			||||
	Ok:    "Ok", | 
				
			||||
} | 
				
			||||
 | 
				
			||||
var strToCode = map[string]Code{ | 
				
			||||
	`"Unset"`: Unset, | 
				
			||||
	`"Error"`: Error, | 
				
			||||
	`"Ok"`:    Ok, | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// String returns the Code as a string.
 | 
				
			||||
func (c Code) String() string { | 
				
			||||
	return codeToStr[c] | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// UnmarshalJSON unmarshals b into the Code.
 | 
				
			||||
//
 | 
				
			||||
// This is based on the functionality in the gRPC codes package:
 | 
				
			||||
// https://github.com/grpc/grpc-go/blob/bb64fee312b46ebee26be43364a7a966033521b1/codes/codes.go#L218-L244
 | 
				
			||||
func (c *Code) UnmarshalJSON(b []byte) error { | 
				
			||||
	// From json.Unmarshaler: By convention, to approximate the behavior of
 | 
				
			||||
	// Unmarshal itself, Unmarshalers implement UnmarshalJSON([]byte("null")) as
 | 
				
			||||
	// a no-op.
 | 
				
			||||
	if string(b) == "null" { | 
				
			||||
		return nil | 
				
			||||
	} | 
				
			||||
	if c == nil { | 
				
			||||
		return fmt.Errorf("nil receiver passed to UnmarshalJSON") | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	var x interface{} | 
				
			||||
	if err := json.Unmarshal(b, &x); err != nil { | 
				
			||||
		return err | 
				
			||||
	} | 
				
			||||
	switch x.(type) { | 
				
			||||
	case string: | 
				
			||||
		if jc, ok := strToCode[string(b)]; ok { | 
				
			||||
			*c = jc | 
				
			||||
			return nil | 
				
			||||
		} | 
				
			||||
		return fmt.Errorf("invalid code: %q", string(b)) | 
				
			||||
	case float64: | 
				
			||||
		if ci, err := strconv.ParseUint(string(b), 10, 32); err == nil { | 
				
			||||
			if ci >= maxCode { | 
				
			||||
				return fmt.Errorf("invalid code: %q", ci) | 
				
			||||
			} | 
				
			||||
 | 
				
			||||
			*c = Code(ci) | 
				
			||||
			return nil | 
				
			||||
		} | 
				
			||||
		return fmt.Errorf("invalid code: %q", string(b)) | 
				
			||||
	default: | 
				
			||||
		return fmt.Errorf("invalid code: %q", string(b)) | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// MarshalJSON returns c as the JSON encoding of c.
 | 
				
			||||
func (c *Code) MarshalJSON() ([]byte, error) { | 
				
			||||
	if c == nil { | 
				
			||||
		return []byte("null"), nil | 
				
			||||
	} | 
				
			||||
	str, ok := codeToStr[*c] | 
				
			||||
	if !ok { | 
				
			||||
		return nil, fmt.Errorf("invalid code: %d", *c) | 
				
			||||
	} | 
				
			||||
	return []byte(fmt.Sprintf("%q", str)), nil | 
				
			||||
} | 
				
			||||
@ -0,0 +1,25 @@ | 
				
			||||
// Copyright The OpenTelemetry Authors
 | 
				
			||||
//
 | 
				
			||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||
// you may not use this file except in compliance with the License.
 | 
				
			||||
// You may obtain a copy of the License at
 | 
				
			||||
//
 | 
				
			||||
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||
//
 | 
				
			||||
// Unless required by applicable law or agreed to in writing, software
 | 
				
			||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||
// See the License for the specific language governing permissions and
 | 
				
			||||
// limitations under the License.
 | 
				
			||||
 | 
				
			||||
/* | 
				
			||||
Package codes defines the canonical error codes used by OpenTelemetry. | 
				
			||||
 | 
				
			||||
This package is currently in a pre-GA phase. Backwards incompatible changes | 
				
			||||
may be introduced in subsequent minor version releases as we work to track | 
				
			||||
the evolving OpenTelemetry specification and user feedback. | 
				
			||||
 | 
				
			||||
It conforms to [the OpenTelemetry | 
				
			||||
specification](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/api.md#statuscanonicalcode).
 | 
				
			||||
*/ | 
				
			||||
package codes // import "go.opentelemetry.io/otel/codes"
 | 
				
			||||
@ -0,0 +1,38 @@ | 
				
			||||
// Copyright The OpenTelemetry Authors
 | 
				
			||||
//
 | 
				
			||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||
// you may not use this file except in compliance with the License.
 | 
				
			||||
// You may obtain a copy of the License at
 | 
				
			||||
//
 | 
				
			||||
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||
//
 | 
				
			||||
// Unless required by applicable law or agreed to in writing, software
 | 
				
			||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||
// See the License for the specific language governing permissions and
 | 
				
			||||
// limitations under the License.
 | 
				
			||||
 | 
				
			||||
/* | 
				
			||||
Package otel provides global access to the OpenTelemetry API. The subpackages of | 
				
			||||
the otel package provide an implementation of the OpenTelemetry API. | 
				
			||||
 | 
				
			||||
This package is currently in a pre-GA phase. Backwards incompatible changes | 
				
			||||
may be introduced in subsequent minor version releases as we work to track the | 
				
			||||
evolving OpenTelemetry specification and user feedback. | 
				
			||||
 | 
				
			||||
The provided API is used to instrument code and measure data about that code's | 
				
			||||
performance and operation. The measured data, by default, is not processed or | 
				
			||||
transmitted anywhere. An implementation of the OpenTelemetry SDK, like the | 
				
			||||
default SDK implementation (go.opentelemetry.io/otel/sdk), and associated | 
				
			||||
exporters are used to process and transport this data. | 
				
			||||
 | 
				
			||||
To read the getting started guide, see https://opentelemetry.io/docs/go/getting-started/.
 | 
				
			||||
 | 
				
			||||
To read more about tracing, see go.opentelemetry.io/otel/trace. | 
				
			||||
 | 
				
			||||
To read more about metrics, see go.opentelemetry.io/otel/metric. | 
				
			||||
 | 
				
			||||
To read more about propagation, see go.opentelemetry.io/otel/propagation and | 
				
			||||
go.opentelemetry.io/otel/baggage. | 
				
			||||
*/ | 
				
			||||
package otel // import "go.opentelemetry.io/otel"
 | 
				
			||||
@ -0,0 +1,22 @@ | 
				
			||||
// Copyright The OpenTelemetry Authors
 | 
				
			||||
//
 | 
				
			||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||
// you may not use this file except in compliance with the License.
 | 
				
			||||
// You may obtain a copy of the License at
 | 
				
			||||
//
 | 
				
			||||
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||
//
 | 
				
			||||
// Unless required by applicable law or agreed to in writing, software
 | 
				
			||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||
// See the License for the specific language governing permissions and
 | 
				
			||||
// limitations under the License.
 | 
				
			||||
 | 
				
			||||
package otel // import "go.opentelemetry.io/otel"
 | 
				
			||||
 | 
				
			||||
// ErrorHandler handles irremediable events.
 | 
				
			||||
type ErrorHandler interface { | 
				
			||||
	// Handle handles any error deemed irremediable by an OpenTelemetry
 | 
				
			||||
	// component.
 | 
				
			||||
	Handle(error) | 
				
			||||
} | 
				
			||||
@ -0,0 +1,41 @@ | 
				
			||||
#!/usr/bin/env bash | 
				
			||||
 | 
				
			||||
# Copyright The OpenTelemetry 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. | 
				
			||||
 | 
				
			||||
set -euo pipefail | 
				
			||||
 | 
				
			||||
top_dir='.' | 
				
			||||
if [[ $# -gt 0 ]]; then | 
				
			||||
    top_dir="${1}" | 
				
			||||
fi | 
				
			||||
 | 
				
			||||
p=$(pwd) | 
				
			||||
mod_dirs=() | 
				
			||||
 | 
				
			||||
# Note `mapfile` does not exist in older bash versions: | 
				
			||||
# https://stackoverflow.com/questions/41475261/need-alternative-to-readarray-mapfile-for-script-on-older-version-of-bash | 
				
			||||
 | 
				
			||||
while IFS= read -r line; do | 
				
			||||
    mod_dirs+=("$line") | 
				
			||||
done < <(find "${top_dir}" -type f -name 'go.mod' -exec dirname {} \; | sort) | 
				
			||||
 | 
				
			||||
for mod_dir in "${mod_dirs[@]}"; do | 
				
			||||
    cd "${mod_dir}" | 
				
			||||
 | 
				
			||||
    while IFS= read -r line; do | 
				
			||||
        echo ".${line#${p}}" | 
				
			||||
    done < <(go list --find -f '{{.Name}}|{{.Dir}}' ./... | grep '^main|' | cut -f 2- -d '|') | 
				
			||||
    cd "${p}" | 
				
			||||
done | 
				
			||||
@ -0,0 +1,8 @@ | 
				
			||||
module go.opentelemetry.io/otel | 
				
			||||
 | 
				
			||||
go 1.14 | 
				
			||||
 | 
				
			||||
require ( | 
				
			||||
	github.com/google/go-cmp v0.5.4 | 
				
			||||
	github.com/stretchr/testify v1.6.1 | 
				
			||||
) | 
				
			||||
@ -0,0 +1,15 @@ | 
				
			||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= | 
				
			||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | 
				
			||||
github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= | 
				
			||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | 
				
			||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | 
				
			||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | 
				
			||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | 
				
			||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= | 
				
			||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | 
				
			||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= | 
				
			||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | 
				
			||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= | 
				
			||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | 
				
			||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= | 
				
			||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | 
				
			||||
@ -0,0 +1,89 @@ | 
				
			||||
// Copyright The OpenTelemetry Authors
 | 
				
			||||
//
 | 
				
			||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||
// you may not use this file except in compliance with the License.
 | 
				
			||||
// You may obtain a copy of the License at
 | 
				
			||||
//
 | 
				
			||||
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||
//
 | 
				
			||||
// Unless required by applicable law or agreed to in writing, software
 | 
				
			||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||
// See the License for the specific language governing permissions and
 | 
				
			||||
// limitations under the License.
 | 
				
			||||
 | 
				
			||||
package otel // import "go.opentelemetry.io/otel"
 | 
				
			||||
 | 
				
			||||
import ( | 
				
			||||
	"log" | 
				
			||||
	"os" | 
				
			||||
	"sync" | 
				
			||||
	"sync/atomic" | 
				
			||||
) | 
				
			||||
 | 
				
			||||
var ( | 
				
			||||
	// globalErrorHandler provides an ErrorHandler that can be used
 | 
				
			||||
	// throughout an OpenTelemetry instrumented project. When a user
 | 
				
			||||
	// specified ErrorHandler is registered (`SetErrorHandler`) all calls to
 | 
				
			||||
	// `Handle` and will be delegated to the registered ErrorHandler.
 | 
				
			||||
	globalErrorHandler = &loggingErrorHandler{ | 
				
			||||
		l: log.New(os.Stderr, "", log.LstdFlags), | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	// delegateErrorHandlerOnce ensures that a user provided ErrorHandler is
 | 
				
			||||
	// only ever registered once.
 | 
				
			||||
	delegateErrorHandlerOnce sync.Once | 
				
			||||
 | 
				
			||||
	// Comiple time check that loggingErrorHandler implements ErrorHandler.
 | 
				
			||||
	_ ErrorHandler = (*loggingErrorHandler)(nil) | 
				
			||||
) | 
				
			||||
 | 
				
			||||
// loggingErrorHandler logs all errors to STDERR.
 | 
				
			||||
type loggingErrorHandler struct { | 
				
			||||
	delegate atomic.Value | 
				
			||||
 | 
				
			||||
	l *log.Logger | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// setDelegate sets the ErrorHandler delegate if one is not already set.
 | 
				
			||||
func (h *loggingErrorHandler) setDelegate(d ErrorHandler) { | 
				
			||||
	if h.delegate.Load() != nil { | 
				
			||||
		// Delegate already registered
 | 
				
			||||
		return | 
				
			||||
	} | 
				
			||||
	h.delegate.Store(d) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Handle implements ErrorHandler.
 | 
				
			||||
func (h *loggingErrorHandler) Handle(err error) { | 
				
			||||
	if d := h.delegate.Load(); d != nil { | 
				
			||||
		d.(ErrorHandler).Handle(err) | 
				
			||||
		return | 
				
			||||
	} | 
				
			||||
	h.l.Print(err) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// GetErrorHandler returns the global ErrorHandler instance. If no ErrorHandler
 | 
				
			||||
// instance has been set (`SetErrorHandler`), the default ErrorHandler which
 | 
				
			||||
// logs errors to STDERR is returned.
 | 
				
			||||
func GetErrorHandler() ErrorHandler { | 
				
			||||
	return globalErrorHandler | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// SetErrorHandler sets the global ErrorHandler to be h.
 | 
				
			||||
func SetErrorHandler(h ErrorHandler) { | 
				
			||||
	delegateErrorHandlerOnce.Do(func() { | 
				
			||||
		current := GetErrorHandler() | 
				
			||||
		if current == h { | 
				
			||||
			return | 
				
			||||
		} | 
				
			||||
		if internalHandler, ok := current.(*loggingErrorHandler); ok { | 
				
			||||
			internalHandler.setDelegate(h) | 
				
			||||
		} | 
				
			||||
	}) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Handle is a convience function for ErrorHandler().Handle(err)
 | 
				
			||||
func Handle(err error) { | 
				
			||||
	GetErrorHandler().Handle(err) | 
				
			||||
} | 
				
			||||
@ -0,0 +1,338 @@ | 
				
			||||
// Copyright The OpenTelemetry Authors
 | 
				
			||||
//
 | 
				
			||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||
// you may not use this file except in compliance with the License.
 | 
				
			||||
// You may obtain a copy of the License at
 | 
				
			||||
//
 | 
				
			||||
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||
//
 | 
				
			||||
// Unless required by applicable law or agreed to in writing, software
 | 
				
			||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||
// See the License for the specific language governing permissions and
 | 
				
			||||
// limitations under the License.
 | 
				
			||||
 | 
				
			||||
// Package baggage provides types and functions to manage W3C Baggage.
 | 
				
			||||
package baggage | 
				
			||||
 | 
				
			||||
import ( | 
				
			||||
	"context" | 
				
			||||
 | 
				
			||||
	"go.opentelemetry.io/otel/label" | 
				
			||||
) | 
				
			||||
 | 
				
			||||
type rawMap map[label.Key]label.Value | 
				
			||||
type keySet map[label.Key]struct{} | 
				
			||||
 | 
				
			||||
// Map is an immutable storage for correlations.
 | 
				
			||||
type Map struct { | 
				
			||||
	m rawMap | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// MapUpdate contains information about correlation changes to be
 | 
				
			||||
// made.
 | 
				
			||||
type MapUpdate struct { | 
				
			||||
	// DropSingleK contains a single key to be dropped from
 | 
				
			||||
	// correlations. Use this to avoid an overhead of a slice
 | 
				
			||||
	// allocation if there is only one key to drop.
 | 
				
			||||
	DropSingleK label.Key | 
				
			||||
	// DropMultiK contains all the keys to be dropped from
 | 
				
			||||
	// correlations.
 | 
				
			||||
	DropMultiK []label.Key | 
				
			||||
 | 
				
			||||
	// SingleKV contains a single key-value pair to be added to
 | 
				
			||||
	// correlations. Use this to avoid an overhead of a slice
 | 
				
			||||
	// allocation if there is only one key-value pair to add.
 | 
				
			||||
	SingleKV label.KeyValue | 
				
			||||
	// MultiKV contains all the key-value pairs to be added to
 | 
				
			||||
	// correlations.
 | 
				
			||||
	MultiKV []label.KeyValue | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func newMap(raw rawMap) Map { | 
				
			||||
	return Map{ | 
				
			||||
		m: raw, | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// NewEmptyMap creates an empty correlations map.
 | 
				
			||||
func NewEmptyMap() Map { | 
				
			||||
	return newMap(nil) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// NewMap creates a map with the contents of the update applied. In
 | 
				
			||||
// this function, having an update with DropSingleK or DropMultiK
 | 
				
			||||
// makes no sense - those fields are effectively ignored.
 | 
				
			||||
func NewMap(update MapUpdate) Map { | 
				
			||||
	return NewEmptyMap().Apply(update) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Apply creates a copy of the map with the contents of the update
 | 
				
			||||
// applied. Apply will first drop the keys from DropSingleK and
 | 
				
			||||
// DropMultiK, then add key-value pairs from SingleKV and MultiKV.
 | 
				
			||||
func (m Map) Apply(update MapUpdate) Map { | 
				
			||||
	delSet, addSet := getModificationSets(update) | 
				
			||||
	mapSize := getNewMapSize(m.m, delSet, addSet) | 
				
			||||
 | 
				
			||||
	r := make(rawMap, mapSize) | 
				
			||||
	for k, v := range m.m { | 
				
			||||
		// do not copy items we want to drop
 | 
				
			||||
		if _, ok := delSet[k]; ok { | 
				
			||||
			continue | 
				
			||||
		} | 
				
			||||
		// do not copy items we would overwrite
 | 
				
			||||
		if _, ok := addSet[k]; ok { | 
				
			||||
			continue | 
				
			||||
		} | 
				
			||||
		r[k] = v | 
				
			||||
	} | 
				
			||||
	if update.SingleKV.Key.Defined() { | 
				
			||||
		r[update.SingleKV.Key] = update.SingleKV.Value | 
				
			||||
	} | 
				
			||||
	for _, kv := range update.MultiKV { | 
				
			||||
		r[kv.Key] = kv.Value | 
				
			||||
	} | 
				
			||||
	if len(r) == 0 { | 
				
			||||
		r = nil | 
				
			||||
	} | 
				
			||||
	return newMap(r) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func getModificationSets(update MapUpdate) (delSet, addSet keySet) { | 
				
			||||
	deletionsCount := len(update.DropMultiK) | 
				
			||||
	if update.DropSingleK.Defined() { | 
				
			||||
		deletionsCount++ | 
				
			||||
	} | 
				
			||||
	if deletionsCount > 0 { | 
				
			||||
		delSet = make(map[label.Key]struct{}, deletionsCount) | 
				
			||||
		for _, k := range update.DropMultiK { | 
				
			||||
			delSet[k] = struct{}{} | 
				
			||||
		} | 
				
			||||
		if update.DropSingleK.Defined() { | 
				
			||||
			delSet[update.DropSingleK] = struct{}{} | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	additionsCount := len(update.MultiKV) | 
				
			||||
	if update.SingleKV.Key.Defined() { | 
				
			||||
		additionsCount++ | 
				
			||||
	} | 
				
			||||
	if additionsCount > 0 { | 
				
			||||
		addSet = make(map[label.Key]struct{}, additionsCount) | 
				
			||||
		for _, k := range update.MultiKV { | 
				
			||||
			addSet[k.Key] = struct{}{} | 
				
			||||
		} | 
				
			||||
		if update.SingleKV.Key.Defined() { | 
				
			||||
			addSet[update.SingleKV.Key] = struct{}{} | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	return | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func getNewMapSize(m rawMap, delSet, addSet keySet) int { | 
				
			||||
	mapSizeDiff := 0 | 
				
			||||
	for k := range addSet { | 
				
			||||
		if _, ok := m[k]; !ok { | 
				
			||||
			mapSizeDiff++ | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
	for k := range delSet { | 
				
			||||
		if _, ok := m[k]; ok { | 
				
			||||
			if _, inAddSet := addSet[k]; !inAddSet { | 
				
			||||
				mapSizeDiff-- | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
	return len(m) + mapSizeDiff | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Value gets a value from correlations map and returns a boolean
 | 
				
			||||
// value indicating whether the key exist in the map.
 | 
				
			||||
func (m Map) Value(k label.Key) (label.Value, bool) { | 
				
			||||
	value, ok := m.m[k] | 
				
			||||
	return value, ok | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// HasValue returns a boolean value indicating whether the key exist
 | 
				
			||||
// in the map.
 | 
				
			||||
func (m Map) HasValue(k label.Key) bool { | 
				
			||||
	_, has := m.Value(k) | 
				
			||||
	return has | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Len returns a length of the map.
 | 
				
			||||
func (m Map) Len() int { | 
				
			||||
	return len(m.m) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Foreach calls a passed callback once on each key-value pair until
 | 
				
			||||
// all the key-value pairs of the map were iterated or the callback
 | 
				
			||||
// returns false, whichever happens first.
 | 
				
			||||
func (m Map) Foreach(f func(label.KeyValue) bool) { | 
				
			||||
	for k, v := range m.m { | 
				
			||||
		if !f(label.KeyValue{ | 
				
			||||
			Key:   k, | 
				
			||||
			Value: v, | 
				
			||||
		}) { | 
				
			||||
			return | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
type correlationsType struct{} | 
				
			||||
 | 
				
			||||
// SetHookFunc describes a type of a callback that is called when
 | 
				
			||||
// storing baggage in the context.
 | 
				
			||||
type SetHookFunc func(context.Context) context.Context | 
				
			||||
 | 
				
			||||
// GetHookFunc describes a type of a callback that is called when
 | 
				
			||||
// getting baggage from the context.
 | 
				
			||||
type GetHookFunc func(context.Context, Map) Map | 
				
			||||
 | 
				
			||||
// value under this key is either of type Map or correlationsData
 | 
				
			||||
var correlationsKey = &correlationsType{} | 
				
			||||
 | 
				
			||||
type correlationsData struct { | 
				
			||||
	m       Map | 
				
			||||
	setHook SetHookFunc | 
				
			||||
	getHook GetHookFunc | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (d correlationsData) isHookless() bool { | 
				
			||||
	return d.setHook == nil && d.getHook == nil | 
				
			||||
} | 
				
			||||
 | 
				
			||||
type hookKind int | 
				
			||||
 | 
				
			||||
const ( | 
				
			||||
	hookKindSet hookKind = iota | 
				
			||||
	hookKindGet | 
				
			||||
) | 
				
			||||
 | 
				
			||||
func (d *correlationsData) overrideHook(kind hookKind, setHook SetHookFunc, getHook GetHookFunc) { | 
				
			||||
	switch kind { | 
				
			||||
	case hookKindSet: | 
				
			||||
		d.setHook = setHook | 
				
			||||
	case hookKindGet: | 
				
			||||
		d.getHook = getHook | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// ContextWithSetHook installs a hook function that will be invoked
 | 
				
			||||
// every time ContextWithMap is called. To avoid unnecessary callback
 | 
				
			||||
// invocations (recursive or not), the callback can temporarily clear
 | 
				
			||||
// the hooks from the context with the ContextWithNoHooks function.
 | 
				
			||||
//
 | 
				
			||||
// Note that NewContext also calls ContextWithMap, so the hook will be
 | 
				
			||||
// invoked.
 | 
				
			||||
//
 | 
				
			||||
// Passing nil SetHookFunc creates a context with no set hook to call.
 | 
				
			||||
//
 | 
				
			||||
// This function should not be used by applications or libraries. It
 | 
				
			||||
// is mostly for interoperation with other observability APIs.
 | 
				
			||||
func ContextWithSetHook(ctx context.Context, hook SetHookFunc) context.Context { | 
				
			||||
	return contextWithHook(ctx, hookKindSet, hook, nil) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// ContextWithGetHook installs a hook function that will be invoked
 | 
				
			||||
// every time MapFromContext is called. To avoid unnecessary callback
 | 
				
			||||
// invocations (recursive or not), the callback can temporarily clear
 | 
				
			||||
// the hooks from the context with the ContextWithNoHooks function.
 | 
				
			||||
//
 | 
				
			||||
// Note that NewContext also calls MapFromContext, so the hook will be
 | 
				
			||||
// invoked.
 | 
				
			||||
//
 | 
				
			||||
// Passing nil GetHookFunc creates a context with no get hook to call.
 | 
				
			||||
//
 | 
				
			||||
// This function should not be used by applications or libraries. It
 | 
				
			||||
// is mostly for interoperation with other observability APIs.
 | 
				
			||||
func ContextWithGetHook(ctx context.Context, hook GetHookFunc) context.Context { | 
				
			||||
	return contextWithHook(ctx, hookKindGet, nil, hook) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func contextWithHook(ctx context.Context, kind hookKind, setHook SetHookFunc, getHook GetHookFunc) context.Context { | 
				
			||||
	switch v := ctx.Value(correlationsKey).(type) { | 
				
			||||
	case correlationsData: | 
				
			||||
		v.overrideHook(kind, setHook, getHook) | 
				
			||||
		if v.isHookless() { | 
				
			||||
			return context.WithValue(ctx, correlationsKey, v.m) | 
				
			||||
		} | 
				
			||||
		return context.WithValue(ctx, correlationsKey, v) | 
				
			||||
	case Map: | 
				
			||||
		return contextWithOneHookAndMap(ctx, kind, setHook, getHook, v) | 
				
			||||
	default: | 
				
			||||
		m := NewEmptyMap() | 
				
			||||
		return contextWithOneHookAndMap(ctx, kind, setHook, getHook, m) | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func contextWithOneHookAndMap(ctx context.Context, kind hookKind, setHook SetHookFunc, getHook GetHookFunc, m Map) context.Context { | 
				
			||||
	d := correlationsData{m: m} | 
				
			||||
	d.overrideHook(kind, setHook, getHook) | 
				
			||||
	if d.isHookless() { | 
				
			||||
		return ctx | 
				
			||||
	} | 
				
			||||
	return context.WithValue(ctx, correlationsKey, d) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// ContextWithNoHooks creates a context with all the hooks
 | 
				
			||||
// disabled. Also returns old set and get hooks. This function can be
 | 
				
			||||
// used to temporarily clear the context from hooks and then reinstate
 | 
				
			||||
// them by calling ContextWithSetHook and ContextWithGetHook functions
 | 
				
			||||
// passing the hooks returned by this function.
 | 
				
			||||
//
 | 
				
			||||
// This function should not be used by applications or libraries. It
 | 
				
			||||
// is mostly for interoperation with other observability APIs.
 | 
				
			||||
func ContextWithNoHooks(ctx context.Context) (context.Context, SetHookFunc, GetHookFunc) { | 
				
			||||
	switch v := ctx.Value(correlationsKey).(type) { | 
				
			||||
	case correlationsData: | 
				
			||||
		return context.WithValue(ctx, correlationsKey, v.m), v.setHook, v.getHook | 
				
			||||
	default: | 
				
			||||
		return ctx, nil, nil | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// ContextWithMap returns a context with the Map entered into it.
 | 
				
			||||
func ContextWithMap(ctx context.Context, m Map) context.Context { | 
				
			||||
	switch v := ctx.Value(correlationsKey).(type) { | 
				
			||||
	case correlationsData: | 
				
			||||
		v.m = m | 
				
			||||
		ctx = context.WithValue(ctx, correlationsKey, v) | 
				
			||||
		if v.setHook != nil { | 
				
			||||
			ctx = v.setHook(ctx) | 
				
			||||
		} | 
				
			||||
		return ctx | 
				
			||||
	default: | 
				
			||||
		return context.WithValue(ctx, correlationsKey, m) | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// ContextWithNoCorrelationData returns a context stripped of correlation
 | 
				
			||||
// data.
 | 
				
			||||
func ContextWithNoCorrelationData(ctx context.Context) context.Context { | 
				
			||||
	return context.WithValue(ctx, correlationsKey, nil) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// NewContext returns a context with the map from passed context
 | 
				
			||||
// updated with the passed key-value pairs.
 | 
				
			||||
func NewContext(ctx context.Context, keyvalues ...label.KeyValue) context.Context { | 
				
			||||
	return ContextWithMap(ctx, MapFromContext(ctx).Apply(MapUpdate{ | 
				
			||||
		MultiKV: keyvalues, | 
				
			||||
	})) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// MapFromContext gets the current Map from a Context.
 | 
				
			||||
func MapFromContext(ctx context.Context) Map { | 
				
			||||
	switch v := ctx.Value(correlationsKey).(type) { | 
				
			||||
	case correlationsData: | 
				
			||||
		if v.getHook != nil { | 
				
			||||
			return v.getHook(ctx, v.m) | 
				
			||||
		} | 
				
			||||
		return v.m | 
				
			||||
	case Map: | 
				
			||||
		return v | 
				
			||||
	default: | 
				
			||||
		return NewEmptyMap() | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
@ -0,0 +1,348 @@ | 
				
			||||
// Copyright The OpenTelemetry Authors
 | 
				
			||||
//
 | 
				
			||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||
// you may not use this file except in compliance with the License.
 | 
				
			||||
// You may obtain a copy of the License at
 | 
				
			||||
//
 | 
				
			||||
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||
//
 | 
				
			||||
// Unless required by applicable law or agreed to in writing, software
 | 
				
			||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||
// See the License for the specific language governing permissions and
 | 
				
			||||
// limitations under the License.
 | 
				
			||||
 | 
				
			||||
package global | 
				
			||||
 | 
				
			||||
import ( | 
				
			||||
	"context" | 
				
			||||
	"sync" | 
				
			||||
	"sync/atomic" | 
				
			||||
	"unsafe" | 
				
			||||
 | 
				
			||||
	"go.opentelemetry.io/otel/label" | 
				
			||||
	"go.opentelemetry.io/otel/metric" | 
				
			||||
	"go.opentelemetry.io/otel/metric/number" | 
				
			||||
	"go.opentelemetry.io/otel/metric/registry" | 
				
			||||
) | 
				
			||||
 | 
				
			||||
// This file contains the forwarding implementation of MeterProvider used as
 | 
				
			||||
// the default global instance.  Metric events using instruments provided by
 | 
				
			||||
// this implementation are no-ops until the first Meter implementation is set
 | 
				
			||||
// as the global provider.
 | 
				
			||||
//
 | 
				
			||||
// The implementation here uses Mutexes to maintain a list of active Meters in
 | 
				
			||||
// the MeterProvider and Instruments in each Meter, under the assumption that
 | 
				
			||||
// these interfaces are not performance-critical.
 | 
				
			||||
//
 | 
				
			||||
// We have the invariant that setDelegate() will be called before a new
 | 
				
			||||
// MeterProvider implementation is registered as the global provider.  Mutexes
 | 
				
			||||
// in the MeterProvider and Meters ensure that each instrument has a delegate
 | 
				
			||||
// before the global provider is set.
 | 
				
			||||
//
 | 
				
			||||
// Bound instrument operations are implemented by delegating to the
 | 
				
			||||
// instrument after it is registered, with a sync.Once initializer to
 | 
				
			||||
// protect against races with Release().
 | 
				
			||||
//
 | 
				
			||||
// Metric uniqueness checking is implemented by calling the exported
 | 
				
			||||
// methods of the api/metric/registry package.
 | 
				
			||||
 | 
				
			||||
type meterKey struct { | 
				
			||||
	Name, Version string | 
				
			||||
} | 
				
			||||
 | 
				
			||||
type meterProvider struct { | 
				
			||||
	delegate metric.MeterProvider | 
				
			||||
 | 
				
			||||
	// lock protects `delegate` and `meters`.
 | 
				
			||||
	lock sync.Mutex | 
				
			||||
 | 
				
			||||
	// meters maintains a unique entry for every named Meter
 | 
				
			||||
	// that has been registered through the global instance.
 | 
				
			||||
	meters map[meterKey]*meterEntry | 
				
			||||
} | 
				
			||||
 | 
				
			||||
type meterImpl struct { | 
				
			||||
	delegate unsafe.Pointer // (*metric.MeterImpl)
 | 
				
			||||
 | 
				
			||||
	lock       sync.Mutex | 
				
			||||
	syncInsts  []*syncImpl | 
				
			||||
	asyncInsts []*asyncImpl | 
				
			||||
} | 
				
			||||
 | 
				
			||||
type meterEntry struct { | 
				
			||||
	unique metric.MeterImpl | 
				
			||||
	impl   meterImpl | 
				
			||||
} | 
				
			||||
 | 
				
			||||
type instrument struct { | 
				
			||||
	descriptor metric.Descriptor | 
				
			||||
} | 
				
			||||
 | 
				
			||||
type syncImpl struct { | 
				
			||||
	delegate unsafe.Pointer // (*metric.SyncImpl)
 | 
				
			||||
 | 
				
			||||
	instrument | 
				
			||||
} | 
				
			||||
 | 
				
			||||
type asyncImpl struct { | 
				
			||||
	delegate unsafe.Pointer // (*metric.AsyncImpl)
 | 
				
			||||
 | 
				
			||||
	instrument | 
				
			||||
 | 
				
			||||
	runner metric.AsyncRunner | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// SyncImpler is implemented by all of the sync metric
 | 
				
			||||
// instruments.
 | 
				
			||||
type SyncImpler interface { | 
				
			||||
	SyncImpl() metric.SyncImpl | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// AsyncImpler is implemented by all of the async
 | 
				
			||||
// metric instruments.
 | 
				
			||||
type AsyncImpler interface { | 
				
			||||
	AsyncImpl() metric.AsyncImpl | 
				
			||||
} | 
				
			||||
 | 
				
			||||
type syncHandle struct { | 
				
			||||
	delegate unsafe.Pointer // (*metric.BoundInstrumentImpl)
 | 
				
			||||
 | 
				
			||||
	inst   *syncImpl | 
				
			||||
	labels []label.KeyValue | 
				
			||||
 | 
				
			||||
	initialize sync.Once | 
				
			||||
} | 
				
			||||
 | 
				
			||||
var _ metric.MeterProvider = &meterProvider{} | 
				
			||||
var _ metric.MeterImpl = &meterImpl{} | 
				
			||||
var _ metric.InstrumentImpl = &syncImpl{} | 
				
			||||
var _ metric.BoundSyncImpl = &syncHandle{} | 
				
			||||
var _ metric.AsyncImpl = &asyncImpl{} | 
				
			||||
 | 
				
			||||
func (inst *instrument) Descriptor() metric.Descriptor { | 
				
			||||
	return inst.descriptor | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// MeterProvider interface and delegation
 | 
				
			||||
 | 
				
			||||
func newMeterProvider() *meterProvider { | 
				
			||||
	return &meterProvider{ | 
				
			||||
		meters: map[meterKey]*meterEntry{}, | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (p *meterProvider) setDelegate(provider metric.MeterProvider) { | 
				
			||||
	p.lock.Lock() | 
				
			||||
	defer p.lock.Unlock() | 
				
			||||
 | 
				
			||||
	p.delegate = provider | 
				
			||||
	for key, entry := range p.meters { | 
				
			||||
		entry.impl.setDelegate(key.Name, key.Version, provider) | 
				
			||||
	} | 
				
			||||
	p.meters = nil | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (p *meterProvider) Meter(instrumentationName string, opts ...metric.MeterOption) metric.Meter { | 
				
			||||
	p.lock.Lock() | 
				
			||||
	defer p.lock.Unlock() | 
				
			||||
 | 
				
			||||
	if p.delegate != nil { | 
				
			||||
		return p.delegate.Meter(instrumentationName, opts...) | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	key := meterKey{ | 
				
			||||
		Name:    instrumentationName, | 
				
			||||
		Version: metric.NewMeterConfig(opts...).InstrumentationVersion, | 
				
			||||
	} | 
				
			||||
	entry, ok := p.meters[key] | 
				
			||||
	if !ok { | 
				
			||||
		entry = &meterEntry{} | 
				
			||||
		entry.unique = registry.NewUniqueInstrumentMeterImpl(&entry.impl) | 
				
			||||
		p.meters[key] = entry | 
				
			||||
 | 
				
			||||
	} | 
				
			||||
	return metric.WrapMeterImpl(entry.unique, key.Name, metric.WithInstrumentationVersion(key.Version)) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Meter interface and delegation
 | 
				
			||||
 | 
				
			||||
func (m *meterImpl) setDelegate(name, version string, provider metric.MeterProvider) { | 
				
			||||
	m.lock.Lock() | 
				
			||||
	defer m.lock.Unlock() | 
				
			||||
 | 
				
			||||
	d := new(metric.MeterImpl) | 
				
			||||
	*d = provider.Meter(name, metric.WithInstrumentationVersion(version)).MeterImpl() | 
				
			||||
	m.delegate = unsafe.Pointer(d) | 
				
			||||
 | 
				
			||||
	for _, inst := range m.syncInsts { | 
				
			||||
		inst.setDelegate(*d) | 
				
			||||
	} | 
				
			||||
	m.syncInsts = nil | 
				
			||||
	for _, obs := range m.asyncInsts { | 
				
			||||
		obs.setDelegate(*d) | 
				
			||||
	} | 
				
			||||
	m.asyncInsts = nil | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (m *meterImpl) NewSyncInstrument(desc metric.Descriptor) (metric.SyncImpl, error) { | 
				
			||||
	m.lock.Lock() | 
				
			||||
	defer m.lock.Unlock() | 
				
			||||
 | 
				
			||||
	if meterPtr := (*metric.MeterImpl)(atomic.LoadPointer(&m.delegate)); meterPtr != nil { | 
				
			||||
		return (*meterPtr).NewSyncInstrument(desc) | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	inst := &syncImpl{ | 
				
			||||
		instrument: instrument{ | 
				
			||||
			descriptor: desc, | 
				
			||||
		}, | 
				
			||||
	} | 
				
			||||
	m.syncInsts = append(m.syncInsts, inst) | 
				
			||||
	return inst, nil | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Synchronous delegation
 | 
				
			||||
 | 
				
			||||
func (inst *syncImpl) setDelegate(d metric.MeterImpl) { | 
				
			||||
	implPtr := new(metric.SyncImpl) | 
				
			||||
 | 
				
			||||
	var err error | 
				
			||||
	*implPtr, err = d.NewSyncInstrument(inst.descriptor) | 
				
			||||
 | 
				
			||||
	if err != nil { | 
				
			||||
		// TODO: There is no standard way to deliver this error to the user.
 | 
				
			||||
		// See https://github.com/open-telemetry/opentelemetry-go/issues/514
 | 
				
			||||
		// Note that the default SDK will not generate any errors yet, this is
 | 
				
			||||
		// only for added safety.
 | 
				
			||||
		panic(err) | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	atomic.StorePointer(&inst.delegate, unsafe.Pointer(implPtr)) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (inst *syncImpl) Implementation() interface{} { | 
				
			||||
	if implPtr := (*metric.SyncImpl)(atomic.LoadPointer(&inst.delegate)); implPtr != nil { | 
				
			||||
		return (*implPtr).Implementation() | 
				
			||||
	} | 
				
			||||
	return inst | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (inst *syncImpl) Bind(labels []label.KeyValue) metric.BoundSyncImpl { | 
				
			||||
	if implPtr := (*metric.SyncImpl)(atomic.LoadPointer(&inst.delegate)); implPtr != nil { | 
				
			||||
		return (*implPtr).Bind(labels) | 
				
			||||
	} | 
				
			||||
	return &syncHandle{ | 
				
			||||
		inst:   inst, | 
				
			||||
		labels: labels, | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (bound *syncHandle) Unbind() { | 
				
			||||
	bound.initialize.Do(func() {}) | 
				
			||||
 | 
				
			||||
	implPtr := (*metric.BoundSyncImpl)(atomic.LoadPointer(&bound.delegate)) | 
				
			||||
 | 
				
			||||
	if implPtr == nil { | 
				
			||||
		return | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	(*implPtr).Unbind() | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Async delegation
 | 
				
			||||
 | 
				
			||||
func (m *meterImpl) NewAsyncInstrument( | 
				
			||||
	desc metric.Descriptor, | 
				
			||||
	runner metric.AsyncRunner, | 
				
			||||
) (metric.AsyncImpl, error) { | 
				
			||||
 | 
				
			||||
	m.lock.Lock() | 
				
			||||
	defer m.lock.Unlock() | 
				
			||||
 | 
				
			||||
	if meterPtr := (*metric.MeterImpl)(atomic.LoadPointer(&m.delegate)); meterPtr != nil { | 
				
			||||
		return (*meterPtr).NewAsyncInstrument(desc, runner) | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	inst := &asyncImpl{ | 
				
			||||
		instrument: instrument{ | 
				
			||||
			descriptor: desc, | 
				
			||||
		}, | 
				
			||||
		runner: runner, | 
				
			||||
	} | 
				
			||||
	m.asyncInsts = append(m.asyncInsts, inst) | 
				
			||||
	return inst, nil | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (obs *asyncImpl) Implementation() interface{} { | 
				
			||||
	if implPtr := (*metric.AsyncImpl)(atomic.LoadPointer(&obs.delegate)); implPtr != nil { | 
				
			||||
		return (*implPtr).Implementation() | 
				
			||||
	} | 
				
			||||
	return obs | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (obs *asyncImpl) setDelegate(d metric.MeterImpl) { | 
				
			||||
	implPtr := new(metric.AsyncImpl) | 
				
			||||
 | 
				
			||||
	var err error | 
				
			||||
	*implPtr, err = d.NewAsyncInstrument(obs.descriptor, obs.runner) | 
				
			||||
 | 
				
			||||
	if err != nil { | 
				
			||||
		// TODO: There is no standard way to deliver this error to the user.
 | 
				
			||||
		// See https://github.com/open-telemetry/opentelemetry-go/issues/514
 | 
				
			||||
		// Note that the default SDK will not generate any errors yet, this is
 | 
				
			||||
		// only for added safety.
 | 
				
			||||
		panic(err) | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	atomic.StorePointer(&obs.delegate, unsafe.Pointer(implPtr)) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Metric updates
 | 
				
			||||
 | 
				
			||||
func (m *meterImpl) RecordBatch(ctx context.Context, labels []label.KeyValue, measurements ...metric.Measurement) { | 
				
			||||
	if delegatePtr := (*metric.MeterImpl)(atomic.LoadPointer(&m.delegate)); delegatePtr != nil { | 
				
			||||
		(*delegatePtr).RecordBatch(ctx, labels, measurements...) | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (inst *syncImpl) RecordOne(ctx context.Context, number number.Number, labels []label.KeyValue) { | 
				
			||||
	if instPtr := (*metric.SyncImpl)(atomic.LoadPointer(&inst.delegate)); instPtr != nil { | 
				
			||||
		(*instPtr).RecordOne(ctx, number, labels) | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Bound instrument initialization
 | 
				
			||||
 | 
				
			||||
func (bound *syncHandle) RecordOne(ctx context.Context, number number.Number) { | 
				
			||||
	instPtr := (*metric.SyncImpl)(atomic.LoadPointer(&bound.inst.delegate)) | 
				
			||||
	if instPtr == nil { | 
				
			||||
		return | 
				
			||||
	} | 
				
			||||
	var implPtr *metric.BoundSyncImpl | 
				
			||||
	bound.initialize.Do(func() { | 
				
			||||
		implPtr = new(metric.BoundSyncImpl) | 
				
			||||
		*implPtr = (*instPtr).Bind(bound.labels) | 
				
			||||
		atomic.StorePointer(&bound.delegate, unsafe.Pointer(implPtr)) | 
				
			||||
	}) | 
				
			||||
	if implPtr == nil { | 
				
			||||
		implPtr = (*metric.BoundSyncImpl)(atomic.LoadPointer(&bound.delegate)) | 
				
			||||
	} | 
				
			||||
	// This may still be nil if instrument was created and bound
 | 
				
			||||
	// without a delegate, then the instrument was set to have a
 | 
				
			||||
	// delegate and unbound.
 | 
				
			||||
	if implPtr == nil { | 
				
			||||
		return | 
				
			||||
	} | 
				
			||||
	(*implPtr).RecordOne(ctx, number) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func AtomicFieldOffsets() map[string]uintptr { | 
				
			||||
	return map[string]uintptr{ | 
				
			||||
		"meterProvider.delegate": unsafe.Offsetof(meterProvider{}.delegate), | 
				
			||||
		"meterImpl.delegate":     unsafe.Offsetof(meterImpl{}.delegate), | 
				
			||||
		"syncImpl.delegate":      unsafe.Offsetof(syncImpl{}.delegate), | 
				
			||||
		"asyncImpl.delegate":     unsafe.Offsetof(asyncImpl{}.delegate), | 
				
			||||
		"syncHandle.delegate":    unsafe.Offsetof(syncHandle{}.delegate), | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
@ -0,0 +1,82 @@ | 
				
			||||
// Copyright The OpenTelemetry Authors
 | 
				
			||||
//
 | 
				
			||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||
// you may not use this file except in compliance with the License.
 | 
				
			||||
// You may obtain a copy of the License at
 | 
				
			||||
//
 | 
				
			||||
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||
//
 | 
				
			||||
// Unless required by applicable law or agreed to in writing, software
 | 
				
			||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||
// See the License for the specific language governing permissions and
 | 
				
			||||
// limitations under the License.
 | 
				
			||||
 | 
				
			||||
package global | 
				
			||||
 | 
				
			||||
import ( | 
				
			||||
	"context" | 
				
			||||
	"sync" | 
				
			||||
 | 
				
			||||
	"go.opentelemetry.io/otel/propagation" | 
				
			||||
) | 
				
			||||
 | 
				
			||||
// textMapPropagator is a default TextMapPropagator that delegates calls to a
 | 
				
			||||
// registered delegate if one is set, otherwise it defaults to delegating the
 | 
				
			||||
// calls to a the default no-op propagation.TextMapPropagator.
 | 
				
			||||
type textMapPropagator struct { | 
				
			||||
	mtx      sync.Mutex | 
				
			||||
	once     sync.Once | 
				
			||||
	delegate propagation.TextMapPropagator | 
				
			||||
	noop     propagation.TextMapPropagator | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Compile-time guarantee that textMapPropagator implements the
 | 
				
			||||
// propagation.TextMapPropagator interface.
 | 
				
			||||
var _ propagation.TextMapPropagator = (*textMapPropagator)(nil) | 
				
			||||
 | 
				
			||||
func newTextMapPropagator() *textMapPropagator { | 
				
			||||
	return &textMapPropagator{ | 
				
			||||
		noop: propagation.NewCompositeTextMapPropagator(), | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// SetDelegate sets a delegate propagation.TextMapPropagator that all calls are
 | 
				
			||||
// forwarded to. Delegation can only be performed once, all subsequent calls
 | 
				
			||||
// perform no delegation.
 | 
				
			||||
func (p *textMapPropagator) SetDelegate(delegate propagation.TextMapPropagator) { | 
				
			||||
	if delegate == nil { | 
				
			||||
		return | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	p.mtx.Lock() | 
				
			||||
	p.once.Do(func() { p.delegate = delegate }) | 
				
			||||
	p.mtx.Unlock() | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// effectiveDelegate returns the current delegate of p if one is set,
 | 
				
			||||
// otherwise the default noop TextMapPropagator is returned. This method
 | 
				
			||||
// can be called concurrently.
 | 
				
			||||
func (p *textMapPropagator) effectiveDelegate() propagation.TextMapPropagator { | 
				
			||||
	p.mtx.Lock() | 
				
			||||
	defer p.mtx.Unlock() | 
				
			||||
	if p.delegate != nil { | 
				
			||||
		return p.delegate | 
				
			||||
	} | 
				
			||||
	return p.noop | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Inject set cross-cutting concerns from the Context into the carrier.
 | 
				
			||||
func (p *textMapPropagator) Inject(ctx context.Context, carrier propagation.TextMapCarrier) { | 
				
			||||
	p.effectiveDelegate().Inject(ctx, carrier) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Extract reads cross-cutting concerns from the carrier into a Context.
 | 
				
			||||
func (p *textMapPropagator) Extract(ctx context.Context, carrier propagation.TextMapCarrier) context.Context { | 
				
			||||
	return p.effectiveDelegate().Extract(ctx, carrier) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Fields returns the keys whose values are set with Inject.
 | 
				
			||||
func (p *textMapPropagator) Fields() []string { | 
				
			||||
	return p.effectiveDelegate().Fields() | 
				
			||||
} | 
				
			||||
@ -0,0 +1,143 @@ | 
				
			||||
// Copyright The OpenTelemetry Authors
 | 
				
			||||
//
 | 
				
			||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||
// you may not use this file except in compliance with the License.
 | 
				
			||||
// You may obtain a copy of the License at
 | 
				
			||||
//
 | 
				
			||||
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||
//
 | 
				
			||||
// Unless required by applicable law or agreed to in writing, software
 | 
				
			||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||
// See the License for the specific language governing permissions and
 | 
				
			||||
// limitations under the License.
 | 
				
			||||
 | 
				
			||||
package global | 
				
			||||
 | 
				
			||||
import ( | 
				
			||||
	"sync" | 
				
			||||
	"sync/atomic" | 
				
			||||
 | 
				
			||||
	"go.opentelemetry.io/otel/metric" | 
				
			||||
	"go.opentelemetry.io/otel/propagation" | 
				
			||||
	"go.opentelemetry.io/otel/trace" | 
				
			||||
) | 
				
			||||
 | 
				
			||||
type ( | 
				
			||||
	tracerProviderHolder struct { | 
				
			||||
		tp trace.TracerProvider | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	meterProviderHolder struct { | 
				
			||||
		mp metric.MeterProvider | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	propagatorsHolder struct { | 
				
			||||
		tm propagation.TextMapPropagator | 
				
			||||
	} | 
				
			||||
) | 
				
			||||
 | 
				
			||||
var ( | 
				
			||||
	globalTracer      = defaultTracerValue() | 
				
			||||
	globalMeter       = defaultMeterValue() | 
				
			||||
	globalPropagators = defaultPropagatorsValue() | 
				
			||||
 | 
				
			||||
	delegateMeterOnce             sync.Once | 
				
			||||
	delegateTraceOnce             sync.Once | 
				
			||||
	delegateTextMapPropagatorOnce sync.Once | 
				
			||||
) | 
				
			||||
 | 
				
			||||
// TracerProvider is the internal implementation for global.TracerProvider.
 | 
				
			||||
func TracerProvider() trace.TracerProvider { | 
				
			||||
	return globalTracer.Load().(tracerProviderHolder).tp | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// SetTracerProvider is the internal implementation for global.SetTracerProvider.
 | 
				
			||||
func SetTracerProvider(tp trace.TracerProvider) { | 
				
			||||
	delegateTraceOnce.Do(func() { | 
				
			||||
		current := TracerProvider() | 
				
			||||
		if current == tp { | 
				
			||||
			// Setting the provider to the prior default is nonsense, panic.
 | 
				
			||||
			// Panic is acceptable because we are likely still early in the
 | 
				
			||||
			// process lifetime.
 | 
				
			||||
			panic("invalid TracerProvider, the global instance cannot be reinstalled") | 
				
			||||
		} else if def, ok := current.(*tracerProvider); ok { | 
				
			||||
			def.setDelegate(tp) | 
				
			||||
		} | 
				
			||||
 | 
				
			||||
	}) | 
				
			||||
	globalTracer.Store(tracerProviderHolder{tp: tp}) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// MeterProvider is the internal implementation for global.MeterProvider.
 | 
				
			||||
func MeterProvider() metric.MeterProvider { | 
				
			||||
	return globalMeter.Load().(meterProviderHolder).mp | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// SetMeterProvider is the internal implementation for global.SetMeterProvider.
 | 
				
			||||
func SetMeterProvider(mp metric.MeterProvider) { | 
				
			||||
	delegateMeterOnce.Do(func() { | 
				
			||||
		current := MeterProvider() | 
				
			||||
 | 
				
			||||
		if current == mp { | 
				
			||||
			// Setting the provider to the prior default is nonsense, panic.
 | 
				
			||||
			// Panic is acceptable because we are likely still early in the
 | 
				
			||||
			// process lifetime.
 | 
				
			||||
			panic("invalid MeterProvider, the global instance cannot be reinstalled") | 
				
			||||
		} else if def, ok := current.(*meterProvider); ok { | 
				
			||||
			def.setDelegate(mp) | 
				
			||||
		} | 
				
			||||
	}) | 
				
			||||
	globalMeter.Store(meterProviderHolder{mp: mp}) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// TextMapPropagator is the internal implementation for global.TextMapPropagator.
 | 
				
			||||
func TextMapPropagator() propagation.TextMapPropagator { | 
				
			||||
	return globalPropagators.Load().(propagatorsHolder).tm | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// SetTextMapPropagator is the internal implementation for global.SetTextMapPropagator.
 | 
				
			||||
func SetTextMapPropagator(p propagation.TextMapPropagator) { | 
				
			||||
	// For the textMapPropagator already returned by TextMapPropagator
 | 
				
			||||
	// delegate to p.
 | 
				
			||||
	delegateTextMapPropagatorOnce.Do(func() { | 
				
			||||
		if current := TextMapPropagator(); current == p { | 
				
			||||
			// Setting the provider to the prior default is nonsense, panic.
 | 
				
			||||
			// Panic is acceptable because we are likely still early in the
 | 
				
			||||
			// process lifetime.
 | 
				
			||||
			panic("invalid TextMapPropagator, the global instance cannot be reinstalled") | 
				
			||||
		} else if def, ok := current.(*textMapPropagator); ok { | 
				
			||||
			def.SetDelegate(p) | 
				
			||||
		} | 
				
			||||
	}) | 
				
			||||
	// Return p when subsequent calls to TextMapPropagator are made.
 | 
				
			||||
	globalPropagators.Store(propagatorsHolder{tm: p}) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func defaultTracerValue() *atomic.Value { | 
				
			||||
	v := &atomic.Value{} | 
				
			||||
	v.Store(tracerProviderHolder{tp: &tracerProvider{}}) | 
				
			||||
	return v | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func defaultMeterValue() *atomic.Value { | 
				
			||||
	v := &atomic.Value{} | 
				
			||||
	v.Store(meterProviderHolder{mp: newMeterProvider()}) | 
				
			||||
	return v | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func defaultPropagatorsValue() *atomic.Value { | 
				
			||||
	v := &atomic.Value{} | 
				
			||||
	v.Store(propagatorsHolder{tm: newTextMapPropagator()}) | 
				
			||||
	return v | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// ResetForTest restores the initial global state, for testing purposes.
 | 
				
			||||
func ResetForTest() { | 
				
			||||
	globalTracer = defaultTracerValue() | 
				
			||||
	globalMeter = defaultMeterValue() | 
				
			||||
	globalPropagators = defaultPropagatorsValue() | 
				
			||||
	delegateMeterOnce = sync.Once{} | 
				
			||||
	delegateTraceOnce = sync.Once{} | 
				
			||||
	delegateTextMapPropagatorOnce = sync.Once{} | 
				
			||||
} | 
				
			||||
@ -0,0 +1,128 @@ | 
				
			||||
// Copyright The OpenTelemetry Authors
 | 
				
			||||
//
 | 
				
			||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||
// you may not use this file except in compliance with the License.
 | 
				
			||||
// You may obtain a copy of the License at
 | 
				
			||||
//
 | 
				
			||||
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||
//
 | 
				
			||||
// Unless required by applicable law or agreed to in writing, software
 | 
				
			||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||
// See the License for the specific language governing permissions and
 | 
				
			||||
// limitations under the License.
 | 
				
			||||
 | 
				
			||||
package global | 
				
			||||
 | 
				
			||||
/* | 
				
			||||
This file contains the forwarding implementation of the TracerProvider used as | 
				
			||||
the default global instance. Prior to initialization of an SDK, Tracers | 
				
			||||
returned by the global TracerProvider will provide no-op functionality. This | 
				
			||||
means that all Span created prior to initialization are no-op Spans. | 
				
			||||
 | 
				
			||||
Once an SDK has been initialized, all provided no-op Tracers are swapped for | 
				
			||||
Tracers provided by the SDK defined TracerProvider. However, any Span started | 
				
			||||
prior to this initialization does not change its behavior. Meaning, the Span | 
				
			||||
remains a no-op Span. | 
				
			||||
 | 
				
			||||
The implementation to track and swap Tracers locks all new Tracer creation | 
				
			||||
until the swap is complete. This assumes that this operation is not | 
				
			||||
performance-critical. If that assumption is incorrect, be sure to configure an | 
				
			||||
SDK prior to any Tracer creation. | 
				
			||||
*/ | 
				
			||||
 | 
				
			||||
import ( | 
				
			||||
	"context" | 
				
			||||
	"sync" | 
				
			||||
 | 
				
			||||
	"go.opentelemetry.io/otel/internal/trace/noop" | 
				
			||||
	"go.opentelemetry.io/otel/trace" | 
				
			||||
) | 
				
			||||
 | 
				
			||||
// tracerProvider is a placeholder for a configured SDK TracerProvider.
 | 
				
			||||
//
 | 
				
			||||
// All TracerProvider functionality is forwarded to a delegate once
 | 
				
			||||
// configured.
 | 
				
			||||
type tracerProvider struct { | 
				
			||||
	mtx     sync.Mutex | 
				
			||||
	tracers []*tracer | 
				
			||||
 | 
				
			||||
	delegate trace.TracerProvider | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Compile-time guarantee that tracerProvider implements the TracerProvider
 | 
				
			||||
// interface.
 | 
				
			||||
var _ trace.TracerProvider = &tracerProvider{} | 
				
			||||
 | 
				
			||||
// setDelegate configures p to delegate all TracerProvider functionality to
 | 
				
			||||
// provider.
 | 
				
			||||
//
 | 
				
			||||
// All Tracers provided prior to this function call are switched out to be
 | 
				
			||||
// Tracers provided by provider.
 | 
				
			||||
//
 | 
				
			||||
// Delegation only happens on the first call to this method. All subsequent
 | 
				
			||||
// calls result in no delegation changes.
 | 
				
			||||
func (p *tracerProvider) setDelegate(provider trace.TracerProvider) { | 
				
			||||
	if p.delegate != nil { | 
				
			||||
		return | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	p.mtx.Lock() | 
				
			||||
	defer p.mtx.Unlock() | 
				
			||||
 | 
				
			||||
	p.delegate = provider | 
				
			||||
	for _, t := range p.tracers { | 
				
			||||
		t.setDelegate(provider) | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	p.tracers = nil | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Tracer implements TracerProvider.
 | 
				
			||||
func (p *tracerProvider) Tracer(name string, opts ...trace.TracerOption) trace.Tracer { | 
				
			||||
	p.mtx.Lock() | 
				
			||||
	defer p.mtx.Unlock() | 
				
			||||
 | 
				
			||||
	if p.delegate != nil { | 
				
			||||
		return p.delegate.Tracer(name, opts...) | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	t := &tracer{name: name, opts: opts} | 
				
			||||
	p.tracers = append(p.tracers, t) | 
				
			||||
	return t | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// tracer is a placeholder for a trace.Tracer.
 | 
				
			||||
//
 | 
				
			||||
// All Tracer functionality is forwarded to a delegate once configured.
 | 
				
			||||
// Otherwise, all functionality is forwarded to a NoopTracer.
 | 
				
			||||
type tracer struct { | 
				
			||||
	once sync.Once | 
				
			||||
	name string | 
				
			||||
	opts []trace.TracerOption | 
				
			||||
 | 
				
			||||
	delegate trace.Tracer | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Compile-time guarantee that tracer implements the trace.Tracer interface.
 | 
				
			||||
var _ trace.Tracer = &tracer{} | 
				
			||||
 | 
				
			||||
// setDelegate configures t to delegate all Tracer functionality to Tracers
 | 
				
			||||
// created by provider.
 | 
				
			||||
//
 | 
				
			||||
// All subsequent calls to the Tracer methods will be passed to the delegate.
 | 
				
			||||
//
 | 
				
			||||
// Delegation only happens on the first call to this method. All subsequent
 | 
				
			||||
// calls result in no delegation changes.
 | 
				
			||||
func (t *tracer) setDelegate(provider trace.TracerProvider) { | 
				
			||||
	t.once.Do(func() { t.delegate = provider.Tracer(t.name, t.opts...) }) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Start implements trace.Tracer by forwarding the call to t.delegate if
 | 
				
			||||
// set, otherwise it forwards the call to a NoopTracer.
 | 
				
			||||
func (t *tracer) Start(ctx context.Context, name string, opts ...trace.SpanOption) (context.Context, trace.Span) { | 
				
			||||
	if t.delegate != nil { | 
				
			||||
		return t.delegate.Start(ctx, name, opts...) | 
				
			||||
	} | 
				
			||||
	return noop.Tracer.Start(ctx, name, opts...) | 
				
			||||
} | 
				
			||||
@ -0,0 +1,91 @@ | 
				
			||||
// Copyright The OpenTelemetry Authors
 | 
				
			||||
//
 | 
				
			||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||
// you may not use this file except in compliance with the License.
 | 
				
			||||
// You may obtain a copy of the License at
 | 
				
			||||
//
 | 
				
			||||
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||
//
 | 
				
			||||
// Unless required by applicable law or agreed to in writing, software
 | 
				
			||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||
// See the License for the specific language governing permissions and
 | 
				
			||||
// limitations under the License.
 | 
				
			||||
 | 
				
			||||
package internal | 
				
			||||
 | 
				
			||||
import ( | 
				
			||||
	"math" | 
				
			||||
	"unsafe" | 
				
			||||
) | 
				
			||||
 | 
				
			||||
func BoolToRaw(b bool) uint64 { | 
				
			||||
	if b { | 
				
			||||
		return 1 | 
				
			||||
	} | 
				
			||||
	return 0 | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func RawToBool(r uint64) bool { | 
				
			||||
	return r != 0 | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func Int64ToRaw(i int64) uint64 { | 
				
			||||
	return uint64(i) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func RawToInt64(r uint64) int64 { | 
				
			||||
	return int64(r) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func Uint64ToRaw(u uint64) uint64 { | 
				
			||||
	return u | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func RawToUint64(r uint64) uint64 { | 
				
			||||
	return r | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func Float64ToRaw(f float64) uint64 { | 
				
			||||
	return math.Float64bits(f) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func RawToFloat64(r uint64) float64 { | 
				
			||||
	return math.Float64frombits(r) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func Int32ToRaw(i int32) uint64 { | 
				
			||||
	return uint64(i) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func RawToInt32(r uint64) int32 { | 
				
			||||
	return int32(r) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func Uint32ToRaw(u uint32) uint64 { | 
				
			||||
	return uint64(u) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func RawToUint32(r uint64) uint32 { | 
				
			||||
	return uint32(r) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func Float32ToRaw(f float32) uint64 { | 
				
			||||
	return Uint32ToRaw(math.Float32bits(f)) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func RawToFloat32(r uint64) float32 { | 
				
			||||
	return math.Float32frombits(RawToUint32(r)) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func RawPtrToFloat64Ptr(r *uint64) *float64 { | 
				
			||||
	return (*float64)(unsafe.Pointer(r)) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func RawPtrToInt64Ptr(r *uint64) *int64 { | 
				
			||||
	return (*int64)(unsafe.Pointer(r)) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func RawPtrToUint64Ptr(r *uint64) *uint64 { | 
				
			||||
	return r | 
				
			||||
} | 
				
			||||
Some files were not shown because too many files have changed in this diff Show More
					Loading…
					
					
				
		Reference in new issue