Compare commits
11 Commits
v0.1.0
...
f93f144288
| Author | SHA1 | Date | |
|---|---|---|---|
| f93f144288 | |||
| 56b9570287 | |||
| ba9e54d1fb | |||
| ce58df8a6d | |||
| c5c87da714 | |||
| 05a0af6b74 | |||
| 4c3954ec76 | |||
| 67d1a1d8e5 | |||
| bb99d3b7c9 | |||
| 85fe69dcaa | |||
| 6fd794f7d1 |
@@ -1,8 +1,10 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"flag"
|
"flag"
|
||||||
"github.com/cyrilix/robocar-base/cli"
|
"github.com/cyrilix/robocar-base/cli"
|
||||||
|
"github.com/cyrilix/robocar-steering-tflite-edgetpu/pkg/metrics"
|
||||||
"github.com/cyrilix/robocar-steering-tflite-edgetpu/pkg/steering"
|
"github.com/cyrilix/robocar-steering-tflite-edgetpu/pkg/steering"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"log"
|
"log"
|
||||||
@@ -18,8 +20,7 @@ func main() {
|
|||||||
var cameraTopic, steeringTopic string
|
var cameraTopic, steeringTopic string
|
||||||
var modelPath string
|
var modelPath string
|
||||||
var edgeVerbosity int
|
var edgeVerbosity int
|
||||||
var debug bool
|
var imgWidth, imgHeight, horizon int
|
||||||
|
|
||||||
|
|
||||||
mqttQos := cli.InitIntFlag("MQTT_QOS", 0)
|
mqttQos := cli.InitIntFlag("MQTT_QOS", 0)
|
||||||
_, mqttRetain := os.LookupEnv("MQTT_RETAIN")
|
_, mqttRetain := os.LookupEnv("MQTT_RETAIN")
|
||||||
@@ -30,20 +31,19 @@ func main() {
|
|||||||
flag.StringVar(&steeringTopic, "mqtt-topic-road", os.Getenv("MQTT_TOPIC_STEERING"), "Mqtt topic to publish road detection result, use MQTT_TOPIC_STEERING if args not set")
|
flag.StringVar(&steeringTopic, "mqtt-topic-road", os.Getenv("MQTT_TOPIC_STEERING"), "Mqtt topic to publish road detection result, use MQTT_TOPIC_STEERING if args not set")
|
||||||
flag.StringVar(&cameraTopic, "mqtt-topic-camera", os.Getenv("MQTT_TOPIC_CAMERA"), "Mqtt topic that contains camera frame values, use MQTT_TOPIC_CAMERA if args not set")
|
flag.StringVar(&cameraTopic, "mqtt-topic-camera", os.Getenv("MQTT_TOPIC_CAMERA"), "Mqtt topic that contains camera frame values, use MQTT_TOPIC_CAMERA if args not set")
|
||||||
flag.IntVar(&edgeVerbosity, "edge-verbosity", 0, "Edge TPU Verbosity")
|
flag.IntVar(&edgeVerbosity, "edge-verbosity", 0, "Edge TPU Verbosity")
|
||||||
flag.BoolVar(&debug, "debug", false, "Display debug logs")
|
flag.IntVar(&imgWidth, "img-width", 0, "image width expected by model (mandatory)")
|
||||||
|
flag.IntVar(&imgHeight, "img-height", 0, "image height expected by model (mandatory)")
|
||||||
|
flag.IntVar(&horizon, "horizon", 0, "upper zone to crop from image. Models expect size 'imgHeight - horizon'")
|
||||||
|
logLevel := zap.LevelFlag("log", zap.InfoLevel, "log level")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
if len(os.Args) <= 1 {
|
if len(os.Args) <= 1 {
|
||||||
flag.PrintDefaults()
|
flag.PrintDefaults()
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
config := zap.NewDevelopmentConfig()
|
config := zap.NewDevelopmentConfig()
|
||||||
if debug {
|
config.Level = zap.NewAtomicLevelAt(*logLevel)
|
||||||
config.Level = zap.NewAtomicLevelAt(zap.DebugLevel)
|
|
||||||
} else {
|
|
||||||
config.Level = zap.NewAtomicLevelAt(zap.InfoLevel)
|
|
||||||
}
|
|
||||||
lgr, err := config.Build()
|
lgr, err := config.Build()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("unable to init logger: %v", err)
|
log.Fatalf("unable to init logger: %v", err)
|
||||||
@@ -55,11 +55,19 @@ func main() {
|
|||||||
}()
|
}()
|
||||||
zap.ReplaceGlobals(lgr)
|
zap.ReplaceGlobals(lgr)
|
||||||
|
|
||||||
|
cleanup := metrics.Init(context.Background())
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
if modelPath == "" {
|
if modelPath == "" {
|
||||||
zap.L().Error("model path is mandatory")
|
zap.L().Error("model path is mandatory")
|
||||||
flag.PrintDefaults()
|
flag.PrintDefaults()
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
if imgWidth <= 0 || imgHeight <= 0 {
|
||||||
|
zap.L().Error("img-width and img-height are mandatory")
|
||||||
|
flag.PrintDefaults()
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
client, err := cli.Connect(mqttBroker, username, password, clientId)
|
client, err := cli.Connect(mqttBroker, username, password, clientId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -67,7 +75,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
defer client.Disconnect(50)
|
defer client.Disconnect(50)
|
||||||
|
|
||||||
p := steering.NewPart(client, modelPath, steeringTopic, cameraTopic, edgeVerbosity)
|
p := steering.NewPart(client, modelPath, steeringTopic, cameraTopic, edgeVerbosity, imgWidth, imgHeight, horizon)
|
||||||
defer p.Stop()
|
defer p.Stop()
|
||||||
|
|
||||||
cli.HandleExit(p)
|
cli.HandleExit(p)
|
||||||
|
|||||||
21
go.mod
21
go.mod
@@ -3,19 +3,32 @@ module github.com/cyrilix/robocar-steering-tflite-edgetpu
|
|||||||
go 1.17
|
go 1.17
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/cyrilix/robocar-base v0.1.4
|
github.com/cyrilix/robocar-base v0.1.6
|
||||||
github.com/cyrilix/robocar-protobuf/go v1.0.3
|
github.com/cyrilix/robocar-protobuf/go v1.0.4
|
||||||
|
github.com/disintegration/imaging v1.6.2
|
||||||
github.com/eclipse/paho.mqtt.golang v1.3.5
|
github.com/eclipse/paho.mqtt.golang v1.3.5
|
||||||
github.com/golang/protobuf v1.5.2
|
|
||||||
github.com/mattn/go-tflite v1.0.2-0.20210524070808-ba19dc99415b
|
github.com/mattn/go-tflite v1.0.2-0.20210524070808-ba19dc99415b
|
||||||
|
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v0.26.0
|
||||||
|
go.opentelemetry.io/otel/metric v0.26.0
|
||||||
|
go.opentelemetry.io/otel/sdk/metric v0.26.0
|
||||||
go.uber.org/zap v1.19.1
|
go.uber.org/zap v1.19.1
|
||||||
|
google.golang.org/protobuf v1.27.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/go-logr/logr v1.2.1 // indirect
|
||||||
|
github.com/go-logr/stdr v1.2.0 // indirect
|
||||||
|
github.com/golang/protobuf v1.5.2 // indirect
|
||||||
github.com/gorilla/websocket v1.4.2 // indirect
|
github.com/gorilla/websocket v1.4.2 // indirect
|
||||||
github.com/mattn/go-pointer v0.0.1 // indirect
|
github.com/mattn/go-pointer v0.0.1 // indirect
|
||||||
|
go.opentelemetry.io/otel v1.3.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/internal/metric v0.26.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/sdk v1.3.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/sdk/export/metric v0.26.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/trace v1.3.0 // indirect
|
||||||
go.uber.org/atomic v1.7.0 // indirect
|
go.uber.org/atomic v1.7.0 // indirect
|
||||||
go.uber.org/multierr v1.6.0 // indirect
|
go.uber.org/multierr v1.6.0 // indirect
|
||||||
|
golang.org/x/image v0.0.0-20200430140353-33d19683fad8 // indirect
|
||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 // indirect
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 // indirect
|
||||||
google.golang.org/protobuf v1.26.0 // indirect
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
42
go.sum
42
go.sum
@@ -3,20 +3,22 @@ github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX
|
|||||||
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
|
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
|
||||||
github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
|
github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
|
||||||
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
|
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
|
||||||
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
|
||||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||||
|
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
|
||||||
|
github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||||
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
||||||
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
|
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
|
||||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||||
github.com/cyrilix/robocar-base v0.1.4 h1:nfnjRwAcCfS7xGu6tW9rZhmc/HZIsuDJX5NFhgX5dWE=
|
github.com/cyrilix/robocar-base v0.1.6 h1:VVcSZD8DPsha3XDLxRBMvtcd6uC8CcIjqbxG482dxvo=
|
||||||
github.com/cyrilix/robocar-base v0.1.4/go.mod h1:Tt04UmbGBiQtU0Cn3wFD0q7XoyokTwIlWYQxThKI+04=
|
github.com/cyrilix/robocar-base v0.1.6/go.mod h1:m5ov/7hpRHi0yMp2prKafL6UEsM2O71Uea85WR0/jjI=
|
||||||
github.com/cyrilix/robocar-protobuf/go v1.0.3 h1:iPHw2+7FVXG2C4+Th1m11hQ+2RpAQzlxKhc5M7XOa6Q=
|
github.com/cyrilix/robocar-protobuf/go v1.0.4 h1:XTolFYbiKw4gQ2l+z/LMZkLrmAUMzlHcQBzp/czlANo=
|
||||||
github.com/cyrilix/robocar-protobuf/go v1.0.3/go.mod h1:xb95cK07lYXnKcHZKnGafmAgYRrqZWZgV9LMiJAp+gE=
|
github.com/cyrilix/robocar-protobuf/go v1.0.4/go.mod h1:1fyGMVm4ZodfYRrbWCEQgtvKyvrhyTBe5zA7/Qeh/H0=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
|
||||||
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
||||||
github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||||
github.com/docker/docker v17.12.0-ce-rc1.0.20200916142827-bd33bbf0497b+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
github.com/docker/docker v17.12.0-ce-rc1.0.20200916142827-bd33bbf0497b+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||||
@@ -36,6 +38,11 @@ github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7/go.mod h1:482civXOzJJCPzJ
|
|||||||
github.com/go-gl/glfw v0.0.0-20180426074136-46a8d530c326/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
github.com/go-gl/glfw v0.0.0-20180426074136-46a8d530c326/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||||
github.com/go-gl/mathgl v0.0.0-20190416160123-c4601bc793c7/go.mod h1:yhpkQzEiH9yPyxDUGzkmgScbaBVlhC06qodikEM0ZwQ=
|
github.com/go-gl/mathgl v0.0.0-20190416160123-c4601bc793c7/go.mod h1:yhpkQzEiH9yPyxDUGzkmgScbaBVlhC06qodikEM0ZwQ=
|
||||||
|
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
|
github.com/go-logr/logr v1.2.1 h1:DX7uPQ4WgAWfoh+NGGlbJQswnYIVvz0SRlLS3rPZQDA=
|
||||||
|
github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
|
github.com/go-logr/stdr v1.2.0 h1:j4LrlVXgrbIWO83mmQUnK0Hi+YnbD+vzrE1z/EphbFE=
|
||||||
|
github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=
|
||||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||||
@@ -52,8 +59,9 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS
|
|||||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||||
|
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||||
@@ -111,6 +119,22 @@ github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVM
|
|||||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||||
github.com/vincent-petithory/dataurl v0.0.0-20191104211930-d1553a71de50/go.mod h1:FHafX5vmDzyP+1CQATJn7WFKc9CvnvxyvZy6I1MrG/U=
|
github.com/vincent-petithory/dataurl v0.0.0-20191104211930-d1553a71de50/go.mod h1:FHafX5vmDzyP+1CQATJn7WFKc9CvnvxyvZy6I1MrG/U=
|
||||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
|
go.opentelemetry.io/otel v1.3.0 h1:APxLf0eiBwLl+SOXiJJCVYzA1OOJNyAoV8C5RNRyy7Y=
|
||||||
|
go.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs=
|
||||||
|
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v0.26.0 h1:w7fF+cx3zdxURlLuVhzuYt6BT9COyecNfYYhtHXZoDc=
|
||||||
|
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v0.26.0/go.mod h1:Q4v85sm7QpfKDGBdHSMn1pKqvwtQ4I7JgtuRIjRi17U=
|
||||||
|
go.opentelemetry.io/otel/internal/metric v0.26.0 h1:dlrvawyd/A+X8Jp0EBT4wWEe4k5avYaXsXrBr4dbfnY=
|
||||||
|
go.opentelemetry.io/otel/internal/metric v0.26.0/go.mod h1:CbBP6AxKynRs3QCbhklyLUtpfzbqCLiafV9oY2Zj1Jk=
|
||||||
|
go.opentelemetry.io/otel/metric v0.26.0 h1:VaPYBTvA13h/FsiWfxa3yZnZEm15BhStD8JZQSA773M=
|
||||||
|
go.opentelemetry.io/otel/metric v0.26.0/go.mod h1:c6YL0fhRo4YVoNs6GoByzUgBp36hBL523rECoZA5UWg=
|
||||||
|
go.opentelemetry.io/otel/sdk v1.3.0 h1:3278edCoH89MEJ0Ky8WQXVmDQv3FX4ZJ3Pp+9fJreAI=
|
||||||
|
go.opentelemetry.io/otel/sdk v1.3.0/go.mod h1:rIo4suHNhQwBIPg9axF8V9CA72Wz2mKF1teNrup8yzs=
|
||||||
|
go.opentelemetry.io/otel/sdk/export/metric v0.26.0 h1:eNseg5yyZqaAAY+Att3owR3Bl0Is5rCZywqO1OrGx18=
|
||||||
|
go.opentelemetry.io/otel/sdk/export/metric v0.26.0/go.mod h1:UpqzSnUOjFeSIVQLPp3pYIXfB/MiMFyXXzYT/bercxQ=
|
||||||
|
go.opentelemetry.io/otel/sdk/metric v0.26.0 h1:7IKp3gc/ObieCtshBeYYVFp3ZP7xIH1OzODi1Wao90Y=
|
||||||
|
go.opentelemetry.io/otel/sdk/metric v0.26.0/go.mod h1:2VIeK0kS1YvRLFg3J58ptZTXYpiWlkq2n5RQt6w7He8=
|
||||||
|
go.opentelemetry.io/otel/trace v1.3.0 h1:doy8Hzb1RJ+I3yFhtDmwNc7tIyw1tNMOIsyPzp1NOGY=
|
||||||
|
go.opentelemetry.io/otel/trace v1.3.0/go.mod h1:c/VDhno8888bvQYmbYLqe41/Ldmr/KKunbvWM4/fEjk=
|
||||||
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
||||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||||
go.uber.org/goleak v1.1.11-0.20210813005559-691160354723 h1:sHOAIxRGBp443oHZIPB+HsUGaksVCXVQENPxwTfQdH4=
|
go.uber.org/goleak v1.1.11-0.20210813005559-691160354723 h1:sHOAIxRGBp443oHZIPB+HsUGaksVCXVQENPxwTfQdH4=
|
||||||
@@ -129,6 +153,7 @@ golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86h
|
|||||||
golang.org/x/image v0.0.0-20190321063152-3fc05d484e9f/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
golang.org/x/image v0.0.0-20190321063152-3fc05d484e9f/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||||
golang.org/x/image v0.0.0-20190523035834-f03afa92d3ff/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
golang.org/x/image v0.0.0-20190523035834-f03afa92d3ff/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
|
golang.org/x/image v0.0.0-20200430140353-33d19683fad8 h1:6WW6V3x1P/jokJBpRQYUJnMHRP6isStQwCozxnU7XQw=
|
||||||
golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
@@ -155,6 +180,8 @@ golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=
|
||||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
@@ -179,8 +206,9 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl
|
|||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
|
||||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
|
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
|
||||||
|
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
|
|||||||
69
pkg/metrics/metrics.go
Normal file
69
pkg/metrics/metrics.go
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
package metrics
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
stdout "go.opentelemetry.io/otel/exporters/stdout/stdoutmetric"
|
||||||
|
"go.opentelemetry.io/otel/metric"
|
||||||
|
"go.opentelemetry.io/otel/metric/global"
|
||||||
|
"go.opentelemetry.io/otel/metric/unit"
|
||||||
|
controller "go.opentelemetry.io/otel/sdk/metric/controller/basic"
|
||||||
|
processor "go.opentelemetry.io/otel/sdk/metric/processor/basic"
|
||||||
|
"go.opentelemetry.io/otel/sdk/metric/selector/simple"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
FrameAge metric.Int64Histogram
|
||||||
|
InferenceDuration metric.Int64Histogram
|
||||||
|
)
|
||||||
|
|
||||||
|
func initMeter(ctx context.Context) func() {
|
||||||
|
zap.S().Info("init telemetry")
|
||||||
|
exporter, err := stdout.New(
|
||||||
|
stdout.WithPrettyPrint(),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
zap.S().Panicf("failed to initialize prometheus exporter %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pusher := controller.New(
|
||||||
|
processor.NewFactory(
|
||||||
|
simple.NewWithInexpensiveDistribution(),
|
||||||
|
//simple.NewWithHistogramDistribution(
|
||||||
|
// histogram.WithExplicitBoundaries(
|
||||||
|
// []float64{.005, .5, 1, 2.5, 5, 10, 20, 50, 100},
|
||||||
|
// ),
|
||||||
|
//),
|
||||||
|
exporter,
|
||||||
|
),
|
||||||
|
controller.WithExporter(exporter),
|
||||||
|
)
|
||||||
|
|
||||||
|
if err = pusher.Start(ctx); err != nil {
|
||||||
|
zap.S().Fatalf("starting push controller: %v", err)
|
||||||
|
}
|
||||||
|
global.SetMeterProvider(pusher)
|
||||||
|
return func() {
|
||||||
|
if err := pusher.Stop(ctx); err != nil {
|
||||||
|
zap.S().Fatalf("stopping push controller: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func Init(ctx context.Context) func() {
|
||||||
|
cleaner := initMeter(ctx)
|
||||||
|
|
||||||
|
meter := global.Meter("robocar/rc-steering")
|
||||||
|
|
||||||
|
FrameAge = metric.Must(meter).NewInt64Histogram(
|
||||||
|
"robocar.frame_age",
|
||||||
|
metric.WithUnit(unit.Milliseconds),
|
||||||
|
metric.WithDescription("time before frame processing"))
|
||||||
|
InferenceDuration = metric.Must(meter).NewInt64Histogram(
|
||||||
|
"robocar.inference_duration",
|
||||||
|
metric.WithUnit(unit.Milliseconds),
|
||||||
|
metric.WithDescription("tensorflow inference duration"))
|
||||||
|
|
||||||
|
return cleaner
|
||||||
|
}
|
||||||
@@ -2,26 +2,33 @@ package steering
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/cyrilix/robocar-base/service"
|
"github.com/cyrilix/robocar-base/service"
|
||||||
"github.com/cyrilix/robocar-protobuf/go/events"
|
"github.com/cyrilix/robocar-protobuf/go/events"
|
||||||
|
"github.com/cyrilix/robocar-steering-tflite-edgetpu/pkg/metrics"
|
||||||
|
"github.com/cyrilix/robocar-steering-tflite-edgetpu/pkg/tools"
|
||||||
|
"github.com/disintegration/imaging"
|
||||||
mqtt "github.com/eclipse/paho.mqtt.golang"
|
mqtt "github.com/eclipse/paho.mqtt.golang"
|
||||||
"github.com/golang/protobuf/proto"
|
|
||||||
"github.com/mattn/go-tflite"
|
"github.com/mattn/go-tflite"
|
||||||
"github.com/mattn/go-tflite/delegates/edgetpu"
|
"github.com/mattn/go-tflite/delegates/edgetpu"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
"image"
|
"image"
|
||||||
_ "image/jpeg"
|
_ "image/jpeg"
|
||||||
"sort"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewPart(client mqtt.Client, modelPath, steeringTopic, cameraTopic string, edgeVerbosity int) *Part {
|
func NewPart(client mqtt.Client, modelPath, steeringTopic, cameraTopic string, edgeVerbosity int, imgWidth, imgHeight, horizon int) *Part {
|
||||||
return &Part{
|
return &Part{
|
||||||
client: client,
|
client: client,
|
||||||
modelPath: modelPath,
|
modelPath: modelPath,
|
||||||
steeringTopic: steeringTopic,
|
steeringTopic: steeringTopic,
|
||||||
cameraTopic: cameraTopic,
|
cameraTopic: cameraTopic,
|
||||||
edgeVebosity: edgeVerbosity,
|
edgeVebosity: edgeVerbosity,
|
||||||
|
imgWidth: imgWidth,
|
||||||
|
imgHeight: imgHeight,
|
||||||
|
horizon: horizon,
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -38,6 +45,10 @@ type Part struct {
|
|||||||
modelPath string
|
modelPath string
|
||||||
model *tflite.Model
|
model *tflite.Model
|
||||||
edgeVebosity int
|
edgeVebosity int
|
||||||
|
|
||||||
|
imgWidth int
|
||||||
|
imgHeight int
|
||||||
|
horizon int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Part) Start() error {
|
func (p *Part) Start() error {
|
||||||
@@ -122,6 +133,10 @@ func (p *Part) onFrame(_ mqtt.Client, message mqtt.Message) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
now := time.Now().UnixMilli()
|
||||||
|
frameAge := now - msg.Id.CreatedAt.AsTime().UnixMilli()
|
||||||
|
go metrics.FrameAge.Record(context.Background(), frameAge)
|
||||||
|
|
||||||
img, _, err := image.Decode(bytes.NewReader(msg.GetFrame()))
|
img, _, err := image.Decode(bytes.NewReader(msg.GetFrame()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
zap.L().Error("unable to decode frame, skip frame", zap.Error(err))
|
zap.L().Error("unable to decode frame, skip frame", zap.Error(err))
|
||||||
@@ -129,6 +144,9 @@ func (p *Part) onFrame(_ mqtt.Client, message mqtt.Message) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
steering, confidence, err := p.Value(img)
|
steering, confidence, err := p.Value(img)
|
||||||
|
inferenceDuration := time.Now().UnixMilli() - now
|
||||||
|
go metrics.InferenceDuration.Record(context.Background(), inferenceDuration)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
zap.S().Errorw("unable to compute sterring",
|
zap.S().Errorw("unable to compute sterring",
|
||||||
"frame", msg.GetId().GetId(),
|
"frame", msg.GetId().GetId(),
|
||||||
@@ -164,13 +182,23 @@ func (p *Part) Value(img image.Image) (float32, float32, error) {
|
|||||||
dx := img.Bounds().Dx()
|
dx := img.Bounds().Dx()
|
||||||
dy := img.Bounds().Dy()
|
dy := img.Bounds().Dy()
|
||||||
|
|
||||||
bb := make([]byte, dx*dy*3)
|
if dx != p.imgWidth || dy != p.imgHeight {
|
||||||
for y := 0; y < 128; y++ {
|
img = imaging.Resize(img, p.imgWidth, p.imgHeight, imaging.NearestNeighbor)
|
||||||
for x := 0; x < 160; x++ {
|
}
|
||||||
|
if p.horizon > 0 {
|
||||||
|
img = imaging.Crop(img, image.Rect(0, p.horizon, img.Bounds().Dx(), img.Bounds().Dy()))
|
||||||
|
}
|
||||||
|
|
||||||
|
dx = img.Bounds().Dx()
|
||||||
|
dy = img.Bounds().Dy()
|
||||||
|
|
||||||
|
bb := make([]uint8, dx*dy*3)
|
||||||
|
for y := 0; y < dy; y++ {
|
||||||
|
for x := 0; x < dx; x++ {
|
||||||
r, g, b, _ := img.At(x, y).RGBA()
|
r, g, b, _ := img.At(x, y).RGBA()
|
||||||
bb[(y*dx+x)*3+0] = byte(float64(r) / 255.0)
|
bb[(y*dx+x)*3+0] = uint8(float64(r) / 257.0)
|
||||||
bb[(y*dx+x)*3+1] = byte(float64(g) / 255.0)
|
bb[(y*dx+x)*3+1] = uint8(float64(g) / 257.0)
|
||||||
bb[(y*dx+x)*3+2] = byte(float64(b) / 255.0)
|
bb[(y*dx+x)*3+2] = uint8(float64(b) / 257.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
status = input.CopyFromBuffer(bb)
|
status = input.CopyFromBuffer(bb)
|
||||||
@@ -183,42 +211,15 @@ func (p *Part) Value(img image.Image) (float32, float32, error) {
|
|||||||
return 0., 0., fmt.Errorf("invoke failed: %v", status)
|
return 0., 0., fmt.Errorf("invoke failed: %v", status)
|
||||||
}
|
}
|
||||||
|
|
||||||
output := p.interpreter.GetOutputTensor(0)
|
output := p.interpreter.GetOutputTensor(0).UInt8s()
|
||||||
outputSize := output.Dim(output.NumDims() - 1)
|
zap.L().Debug("raw steering", zap.Uint8s("result", output))
|
||||||
b := make([]byte, outputSize)
|
|
||||||
type result struct {
|
|
||||||
score float64
|
|
||||||
index int
|
|
||||||
}
|
|
||||||
status = output.CopyToBuffer(&b[0])
|
|
||||||
if status != tflite.OK {
|
|
||||||
return 0., 0., fmt.Errorf("output failed: %v", status)
|
|
||||||
}
|
|
||||||
|
|
||||||
var results []result
|
steering, score := tools.LinearBin(output, 15, -1, 2.0)
|
||||||
minScore := 0.2
|
|
||||||
for i := 0; i < outputSize; i++ {
|
|
||||||
score := float64(b[i]) / 255.0
|
|
||||||
if score < minScore {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
results = append(results, result{score: score, index: i})
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(results) == 0 {
|
|
||||||
zap.L().Warn(fmt.Sprintf("none steering with score > %0.2f found", minScore))
|
|
||||||
return 0., 0., nil
|
|
||||||
}
|
|
||||||
sort.Slice(results, func(i, j int) bool {
|
|
||||||
return results[i].score > results[j].score
|
|
||||||
})
|
|
||||||
|
|
||||||
steering := float64(results[0].index)*(2./float64(outputSize)) - 1
|
|
||||||
zap.L().Debug("found steering",
|
zap.L().Debug("found steering",
|
||||||
zap.Float64("steering", steering),
|
zap.Float64("steering", steering),
|
||||||
zap.Float64("score", results[0].score),
|
zap.Float64("score", score),
|
||||||
)
|
)
|
||||||
return float32(steering), float32(results[0].score), nil
|
return float32(steering), float32(score), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var registerCallbacks = func(p *Part) error {
|
var registerCallbacks = func(p *Part) error {
|
||||||
|
|||||||
40
pkg/tools/tools.go
Normal file
40
pkg/tools/tools.go
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
package tools
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LinearBin perform inverse linear_bin, taking
|
||||||
|
func LinearBin(arr []uint8, n int, offset int, r float64) (float64, float64) {
|
||||||
|
outputSize := len(arr)
|
||||||
|
type result struct {
|
||||||
|
score float64
|
||||||
|
index int
|
||||||
|
}
|
||||||
|
|
||||||
|
var results []result
|
||||||
|
minScore := 0.2
|
||||||
|
for i := 0; i < outputSize; i++ {
|
||||||
|
score := float64(int(arr[i])) / 255.0
|
||||||
|
if score < minScore {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
results = append(results, result{score: score, index: i})
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(results) == 0 {
|
||||||
|
zap.L().Warn(fmt.Sprintf("none steering with score > %0.2f found", minScore))
|
||||||
|
return 0., 0.
|
||||||
|
}
|
||||||
|
zap.S().Debugf("raw result: %v", results)
|
||||||
|
|
||||||
|
sort.Slice(results, func(i, j int) bool {
|
||||||
|
return results[i].score > results[j].score
|
||||||
|
})
|
||||||
|
|
||||||
|
b := results[0].index
|
||||||
|
a := float64(b)*(r/(float64(n)+float64(offset))) + float64(offset)
|
||||||
|
return a, results[0].score
|
||||||
|
}
|
||||||
77
pkg/tools/tools_test.go
Normal file
77
pkg/tools/tools_test.go
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
package tools
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_LinearBin(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
arr []byte
|
||||||
|
n int
|
||||||
|
offset int
|
||||||
|
r float64
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want float64
|
||||||
|
want1 float64
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "default",
|
||||||
|
args: args{
|
||||||
|
arr: []byte{0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
n: 15,
|
||||||
|
offset: -1,
|
||||||
|
r: 2.0,
|
||||||
|
},
|
||||||
|
want: 0.,
|
||||||
|
want1: 1.0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "left",
|
||||||
|
args: args{
|
||||||
|
arr: []byte{255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
n: 15,
|
||||||
|
offset: -1,
|
||||||
|
r: 2.0,
|
||||||
|
},
|
||||||
|
want: -1.,
|
||||||
|
want1: 1.0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "right",
|
||||||
|
args: args{
|
||||||
|
arr: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255},
|
||||||
|
n: 15,
|
||||||
|
offset: -1,
|
||||||
|
r: 2.0,
|
||||||
|
},
|
||||||
|
want: 1.,
|
||||||
|
want1: 1.0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "right",
|
||||||
|
args: args{
|
||||||
|
arr: []byte{0, 0, 0, 0, 0, 0, 0, 5, 10, 15, 20, 40, 100, 60, 5},
|
||||||
|
n: 15,
|
||||||
|
offset: -1,
|
||||||
|
r: 2.0,
|
||||||
|
},
|
||||||
|
want: 0.7142857142857142,
|
||||||
|
want1: 0.39215686274509803,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got, got1 := LinearBin(tt.args.arr, tt.args.n, tt.args.offset, tt.args.r)
|
||||||
|
if got != tt.want {
|
||||||
|
t.Errorf("linearBin() got = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
if got1 != tt.want1 {
|
||||||
|
t.Errorf("linearBin() got1 = %v, want %v", got1, tt.want1)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
14
vendor/github.com/cyrilix/robocar-base/cli/cli.go
generated
vendored
14
vendor/github.com/cyrilix/robocar-base/cli/cli.go
generated
vendored
@@ -5,7 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"github.com/cyrilix/robocar-base/service"
|
"github.com/cyrilix/robocar-base/service"
|
||||||
MQTT "github.com/eclipse/paho.mqtt.golang"
|
MQTT "github.com/eclipse/paho.mqtt.golang"
|
||||||
"log"
|
"go.uber.org/zap"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -25,7 +25,7 @@ func SetIntDefaultValueFromEnv(value *int, key string, defaultValue int) error {
|
|||||||
sVal = os.Getenv(key)
|
sVal = os.Getenv(key)
|
||||||
val, err := strconv.Atoi(sVal)
|
val, err := strconv.Atoi(sVal)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("unable to convert string to int: %v", err)
|
zap.S().Errorf("unable to convert string to int: %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
*value = val
|
*value = val
|
||||||
@@ -40,7 +40,7 @@ func SetFloat64DefaultValueFromEnv(value *float64, key string, defaultValue floa
|
|||||||
sVal = os.Getenv(key)
|
sVal = os.Getenv(key)
|
||||||
val, err := strconv.ParseFloat(sVal, 64)
|
val, err := strconv.ParseFloat(sVal, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("unable to convert string to float: %v", err)
|
zap.S().Errorf("unable to convert string to float: %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
*value = val
|
*value = val
|
||||||
@@ -81,7 +81,7 @@ func InitIntFlag(key string, defValue int) int {
|
|||||||
var value int
|
var value int
|
||||||
err := SetIntDefaultValueFromEnv(&value, key, defValue)
|
err := SetIntDefaultValueFromEnv(&value, key, defValue)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panicf("invalid int value: %v", err)
|
zap.S().Panicf("invalid int value: %v", err)
|
||||||
}
|
}
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
@@ -90,7 +90,7 @@ func InitFloat64Flag(key string, defValue float64) float64 {
|
|||||||
var value float64
|
var value float64
|
||||||
err := SetFloat64DefaultValueFromEnv(&value, key, defValue)
|
err := SetFloat64DefaultValueFromEnv(&value, key, defValue)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panicf("invalid value: %v", err)
|
zap.S().Panicf("invalid value: %v", err)
|
||||||
}
|
}
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
@@ -106,8 +106,8 @@ func Connect(uri, username, password, clientId string) (MQTT.Client, error) {
|
|||||||
opts.SetDefaultPublishHandler(
|
opts.SetDefaultPublishHandler(
|
||||||
//define a function for the default message handler
|
//define a function for the default message handler
|
||||||
func(client MQTT.Client, msg MQTT.Message) {
|
func(client MQTT.Client, msg MQTT.Message) {
|
||||||
fmt.Printf("TOPIC: %s\n", msg.Topic())
|
zap.S().Infof("TOPIC: %s", msg.Topic())
|
||||||
fmt.Printf("MSG: %s\n", msg.Payload())
|
zap.S().Infof("MSG: %s", msg.Payload())
|
||||||
})
|
})
|
||||||
|
|
||||||
//create and start a client using the above ClientOptions
|
//create and start a client using the above ClientOptions
|
||||||
|
|||||||
8
vendor/github.com/cyrilix/robocar-base/service/part.go
generated
vendored
8
vendor/github.com/cyrilix/robocar-base/service/part.go
generated
vendored
@@ -3,21 +3,21 @@ package service
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
mqtt "github.com/eclipse/paho.mqtt.golang"
|
mqtt "github.com/eclipse/paho.mqtt.golang"
|
||||||
"log"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
func StopService(name string, client mqtt.Client, topics ...string) {
|
func StopService(name string, client mqtt.Client, topics ...string) {
|
||||||
log.Printf("Stop %s service", name)
|
zap.S().Infof("Stop %s service", name)
|
||||||
token := client.Unsubscribe(topics...)
|
token := client.Unsubscribe(topics...)
|
||||||
token.Wait()
|
token.Wait()
|
||||||
if token.Error() != nil {
|
if token.Error() != nil {
|
||||||
log.Printf("unable to unsubscribe service: %v", token.Error())
|
zap.S().Errorf("unable to unsubscribe service: %v", token.Error())
|
||||||
}
|
}
|
||||||
client.Disconnect(50)
|
client.Disconnect(50)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RegisterCallback(client mqtt.Client, topic string, callback mqtt.MessageHandler) error {
|
func RegisterCallback(client mqtt.Client, topic string, callback mqtt.MessageHandler) error {
|
||||||
log.Printf("Register callback on topic %v", topic)
|
zap.S().Infof("Register callback on topic %v", topic)
|
||||||
token := client.Subscribe(topic, 0, callback)
|
token := client.Subscribe(topic, 0, callback)
|
||||||
token.Wait()
|
token.Wait()
|
||||||
if token.Error() != nil {
|
if token.Error() != nil {
|
||||||
|
|||||||
2
vendor/github.com/cyrilix/robocar-protobuf/go/events/events.pb.go
generated
vendored
2
vendor/github.com/cyrilix/robocar-protobuf/go/events/events.pb.go
generated
vendored
@@ -1,6 +1,6 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.26.0
|
// protoc-gen-go v1.27.1
|
||||||
// protoc v3.12.4
|
// protoc v3.12.4
|
||||||
// source: events/events.proto
|
// source: events/events.proto
|
||||||
|
|
||||||
|
|||||||
12
vendor/github.com/disintegration/imaging/.travis.yml
generated
vendored
Normal file
12
vendor/github.com/disintegration/imaging/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
language: go
|
||||||
|
go:
|
||||||
|
- "1.10.x"
|
||||||
|
- "1.11.x"
|
||||||
|
- "1.12.x"
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- go get github.com/mattn/goveralls
|
||||||
|
|
||||||
|
script:
|
||||||
|
- go test -v -race -cover
|
||||||
|
- $GOPATH/bin/goveralls -service=travis-ci
|
||||||
21
vendor/github.com/disintegration/imaging/LICENSE
generated
vendored
Normal file
21
vendor/github.com/disintegration/imaging/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2012 Grigory Dryapak
|
||||||
|
|
||||||
|
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.
|
||||||
226
vendor/github.com/disintegration/imaging/README.md
generated
vendored
Normal file
226
vendor/github.com/disintegration/imaging/README.md
generated
vendored
Normal file
@@ -0,0 +1,226 @@
|
|||||||
|
# Imaging
|
||||||
|
|
||||||
|
[](https://godoc.org/github.com/disintegration/imaging)
|
||||||
|
[](https://travis-ci.org/disintegration/imaging)
|
||||||
|
[](https://coveralls.io/github/disintegration/imaging?branch=master)
|
||||||
|
[](https://goreportcard.com/report/github.com/disintegration/imaging)
|
||||||
|
|
||||||
|
Package imaging provides basic image processing functions (resize, rotate, crop, brightness/contrast adjustments, etc.).
|
||||||
|
|
||||||
|
All the image processing functions provided by the package accept any image type that implements `image.Image` interface
|
||||||
|
as an input, and return a new image of `*image.NRGBA` type (32bit RGBA colors, non-premultiplied alpha).
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
go get -u github.com/disintegration/imaging
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
http://godoc.org/github.com/disintegration/imaging
|
||||||
|
|
||||||
|
## Usage examples
|
||||||
|
|
||||||
|
A few usage examples can be found below. See the documentation for the full list of supported functions.
|
||||||
|
|
||||||
|
### Image resizing
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Resize srcImage to size = 128x128px using the Lanczos filter.
|
||||||
|
dstImage128 := imaging.Resize(srcImage, 128, 128, imaging.Lanczos)
|
||||||
|
|
||||||
|
// Resize srcImage to width = 800px preserving the aspect ratio.
|
||||||
|
dstImage800 := imaging.Resize(srcImage, 800, 0, imaging.Lanczos)
|
||||||
|
|
||||||
|
// Scale down srcImage to fit the 800x600px bounding box.
|
||||||
|
dstImageFit := imaging.Fit(srcImage, 800, 600, imaging.Lanczos)
|
||||||
|
|
||||||
|
// Resize and crop the srcImage to fill the 100x100px area.
|
||||||
|
dstImageFill := imaging.Fill(srcImage, 100, 100, imaging.Center, imaging.Lanczos)
|
||||||
|
```
|
||||||
|
|
||||||
|
Imaging supports image resizing using various resampling filters. The most notable ones:
|
||||||
|
- `Lanczos` - A high-quality resampling filter for photographic images yielding sharp results.
|
||||||
|
- `CatmullRom` - A sharp cubic filter that is faster than Lanczos filter while providing similar results.
|
||||||
|
- `MitchellNetravali` - A cubic filter that produces smoother results with less ringing artifacts than CatmullRom.
|
||||||
|
- `Linear` - Bilinear resampling filter, produces smooth output. Faster than cubic filters.
|
||||||
|
- `Box` - Simple and fast averaging filter appropriate for downscaling. When upscaling it's similar to NearestNeighbor.
|
||||||
|
- `NearestNeighbor` - Fastest resampling filter, no antialiasing.
|
||||||
|
|
||||||
|
The full list of supported filters: NearestNeighbor, Box, Linear, Hermite, MitchellNetravali, CatmullRom, BSpline, Gaussian, Lanczos, Hann, Hamming, Blackman, Bartlett, Welch, Cosine. Custom filters can be created using ResampleFilter struct.
|
||||||
|
|
||||||
|
**Resampling filters comparison**
|
||||||
|
|
||||||
|
Original image:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
The same image resized from 600x400px to 150x100px using different resampling filters.
|
||||||
|
From faster (lower quality) to slower (higher quality):
|
||||||
|
|
||||||
|
Filter | Resize result
|
||||||
|
--------------------------|---------------------------------------------
|
||||||
|
`imaging.NearestNeighbor` | 
|
||||||
|
`imaging.Linear` | 
|
||||||
|
`imaging.CatmullRom` | 
|
||||||
|
`imaging.Lanczos` | 
|
||||||
|
|
||||||
|
|
||||||
|
### Gaussian Blur
|
||||||
|
|
||||||
|
```go
|
||||||
|
dstImage := imaging.Blur(srcImage, 0.5)
|
||||||
|
```
|
||||||
|
|
||||||
|
Sigma parameter allows to control the strength of the blurring effect.
|
||||||
|
|
||||||
|
Original image | Sigma = 0.5 | Sigma = 1.5
|
||||||
|
-----------------------------------|----------------------------------------|---------------------------------------
|
||||||
|
 |  | 
|
||||||
|
|
||||||
|
### Sharpening
|
||||||
|
|
||||||
|
```go
|
||||||
|
dstImage := imaging.Sharpen(srcImage, 0.5)
|
||||||
|
```
|
||||||
|
|
||||||
|
`Sharpen` uses gaussian function internally. Sigma parameter allows to control the strength of the sharpening effect.
|
||||||
|
|
||||||
|
Original image | Sigma = 0.5 | Sigma = 1.5
|
||||||
|
-----------------------------------|-------------------------------------------|------------------------------------------
|
||||||
|
 |  | 
|
||||||
|
|
||||||
|
### Gamma correction
|
||||||
|
|
||||||
|
```go
|
||||||
|
dstImage := imaging.AdjustGamma(srcImage, 0.75)
|
||||||
|
```
|
||||||
|
|
||||||
|
Original image | Gamma = 0.75 | Gamma = 1.25
|
||||||
|
-----------------------------------|------------------------------------------|-----------------------------------------
|
||||||
|
 |  | 
|
||||||
|
|
||||||
|
### Contrast adjustment
|
||||||
|
|
||||||
|
```go
|
||||||
|
dstImage := imaging.AdjustContrast(srcImage, 20)
|
||||||
|
```
|
||||||
|
|
||||||
|
Original image | Contrast = 15 | Contrast = -15
|
||||||
|
-----------------------------------|--------------------------------------------|-------------------------------------------
|
||||||
|
 |  | 
|
||||||
|
|
||||||
|
### Brightness adjustment
|
||||||
|
|
||||||
|
```go
|
||||||
|
dstImage := imaging.AdjustBrightness(srcImage, 20)
|
||||||
|
```
|
||||||
|
|
||||||
|
Original image | Brightness = 10 | Brightness = -10
|
||||||
|
-----------------------------------|----------------------------------------------|---------------------------------------------
|
||||||
|
 |  | 
|
||||||
|
|
||||||
|
### Saturation adjustment
|
||||||
|
|
||||||
|
```go
|
||||||
|
dstImage := imaging.AdjustSaturation(srcImage, 20)
|
||||||
|
```
|
||||||
|
|
||||||
|
Original image | Saturation = 30 | Saturation = -30
|
||||||
|
-----------------------------------|----------------------------------------------|---------------------------------------------
|
||||||
|
 |  | 
|
||||||
|
|
||||||
|
## FAQ
|
||||||
|
|
||||||
|
### Incorrect image orientation after processing (e.g. an image appears rotated after resizing)
|
||||||
|
|
||||||
|
Most probably, the given image contains the EXIF orientation tag.
|
||||||
|
The stadard `image/*` packages do not support loading and saving
|
||||||
|
this kind of information. To fix the issue, try opening images with
|
||||||
|
the `AutoOrientation` decode option. If this option is set to `true`,
|
||||||
|
the image orientation is changed after decoding, according to the
|
||||||
|
orientation tag (if present). Here's the example:
|
||||||
|
|
||||||
|
```go
|
||||||
|
img, err := imaging.Open("test.jpg", imaging.AutoOrientation(true))
|
||||||
|
```
|
||||||
|
|
||||||
|
### What's the difference between `imaging` and `gift` packages?
|
||||||
|
|
||||||
|
[imaging](https://github.com/disintegration/imaging)
|
||||||
|
is designed to be a lightweight and simple image manipulation package.
|
||||||
|
It provides basic image processing functions and a few helper functions
|
||||||
|
such as `Open` and `Save`. It consistently returns *image.NRGBA image
|
||||||
|
type (8 bits per channel, RGBA).
|
||||||
|
|
||||||
|
[gift](https://github.com/disintegration/gift)
|
||||||
|
supports more advanced image processing, for example, sRGB/Linear color
|
||||||
|
space conversions. It also supports different output image types
|
||||||
|
(e.g. 16 bits per channel) and provides easy-to-use API for chaining
|
||||||
|
multiple processing steps together.
|
||||||
|
|
||||||
|
## Example code
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/disintegration/imaging"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Open a test image.
|
||||||
|
src, err := imaging.Open("testdata/flowers.png")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("failed to open image: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Crop the original image to 300x300px size using the center anchor.
|
||||||
|
src = imaging.CropAnchor(src, 300, 300, imaging.Center)
|
||||||
|
|
||||||
|
// Resize the cropped image to width = 200px preserving the aspect ratio.
|
||||||
|
src = imaging.Resize(src, 200, 0, imaging.Lanczos)
|
||||||
|
|
||||||
|
// Create a blurred version of the image.
|
||||||
|
img1 := imaging.Blur(src, 5)
|
||||||
|
|
||||||
|
// Create a grayscale version of the image with higher contrast and sharpness.
|
||||||
|
img2 := imaging.Grayscale(src)
|
||||||
|
img2 = imaging.AdjustContrast(img2, 20)
|
||||||
|
img2 = imaging.Sharpen(img2, 2)
|
||||||
|
|
||||||
|
// Create an inverted version of the image.
|
||||||
|
img3 := imaging.Invert(src)
|
||||||
|
|
||||||
|
// Create an embossed version of the image using a convolution filter.
|
||||||
|
img4 := imaging.Convolve3x3(
|
||||||
|
src,
|
||||||
|
[9]float64{
|
||||||
|
-1, -1, 0,
|
||||||
|
-1, 1, 1,
|
||||||
|
0, 1, 1,
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
|
||||||
|
// Create a new image and paste the four produced images into it.
|
||||||
|
dst := imaging.New(400, 400, color.NRGBA{0, 0, 0, 0})
|
||||||
|
dst = imaging.Paste(dst, img1, image.Pt(0, 0))
|
||||||
|
dst = imaging.Paste(dst, img2, image.Pt(0, 200))
|
||||||
|
dst = imaging.Paste(dst, img3, image.Pt(200, 0))
|
||||||
|
dst = imaging.Paste(dst, img4, image.Pt(200, 200))
|
||||||
|
|
||||||
|
// Save the resulting image as JPEG.
|
||||||
|
err = imaging.Save(dst, "testdata/out_example.jpg")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("failed to save image: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|

|
||||||
253
vendor/github.com/disintegration/imaging/adjust.go
generated
vendored
Normal file
253
vendor/github.com/disintegration/imaging/adjust.go
generated
vendored
Normal file
@@ -0,0 +1,253 @@
|
|||||||
|
package imaging
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Grayscale produces a grayscale version of the image.
|
||||||
|
func Grayscale(img image.Image) *image.NRGBA {
|
||||||
|
src := newScanner(img)
|
||||||
|
dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h))
|
||||||
|
parallel(0, src.h, func(ys <-chan int) {
|
||||||
|
for y := range ys {
|
||||||
|
i := y * dst.Stride
|
||||||
|
src.scan(0, y, src.w, y+1, dst.Pix[i:i+src.w*4])
|
||||||
|
for x := 0; x < src.w; x++ {
|
||||||
|
d := dst.Pix[i : i+3 : i+3]
|
||||||
|
r := d[0]
|
||||||
|
g := d[1]
|
||||||
|
b := d[2]
|
||||||
|
f := 0.299*float64(r) + 0.587*float64(g) + 0.114*float64(b)
|
||||||
|
y := uint8(f + 0.5)
|
||||||
|
d[0] = y
|
||||||
|
d[1] = y
|
||||||
|
d[2] = y
|
||||||
|
i += 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invert produces an inverted (negated) version of the image.
|
||||||
|
func Invert(img image.Image) *image.NRGBA {
|
||||||
|
src := newScanner(img)
|
||||||
|
dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h))
|
||||||
|
parallel(0, src.h, func(ys <-chan int) {
|
||||||
|
for y := range ys {
|
||||||
|
i := y * dst.Stride
|
||||||
|
src.scan(0, y, src.w, y+1, dst.Pix[i:i+src.w*4])
|
||||||
|
for x := 0; x < src.w; x++ {
|
||||||
|
d := dst.Pix[i : i+3 : i+3]
|
||||||
|
d[0] = 255 - d[0]
|
||||||
|
d[1] = 255 - d[1]
|
||||||
|
d[2] = 255 - d[2]
|
||||||
|
i += 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// AdjustSaturation changes the saturation of the image using the percentage parameter and returns the adjusted image.
|
||||||
|
// The percentage must be in the range (-100, 100).
|
||||||
|
// The percentage = 0 gives the original image.
|
||||||
|
// The percentage = 100 gives the image with the saturation value doubled for each pixel.
|
||||||
|
// The percentage = -100 gives the image with the saturation value zeroed for each pixel (grayscale).
|
||||||
|
//
|
||||||
|
// Examples:
|
||||||
|
// dstImage = imaging.AdjustSaturation(srcImage, 25) // Increase image saturation by 25%.
|
||||||
|
// dstImage = imaging.AdjustSaturation(srcImage, -10) // Decrease image saturation by 10%.
|
||||||
|
//
|
||||||
|
func AdjustSaturation(img image.Image, percentage float64) *image.NRGBA {
|
||||||
|
percentage = math.Min(math.Max(percentage, -100), 100)
|
||||||
|
multiplier := 1 + percentage/100
|
||||||
|
|
||||||
|
return AdjustFunc(img, func(c color.NRGBA) color.NRGBA {
|
||||||
|
h, s, l := rgbToHSL(c.R, c.G, c.B)
|
||||||
|
s *= multiplier
|
||||||
|
if s > 1 {
|
||||||
|
s = 1
|
||||||
|
}
|
||||||
|
r, g, b := hslToRGB(h, s, l)
|
||||||
|
return color.NRGBA{r, g, b, c.A}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// AdjustContrast changes the contrast of the image using the percentage parameter and returns the adjusted image.
|
||||||
|
// The percentage must be in range (-100, 100). The percentage = 0 gives the original image.
|
||||||
|
// The percentage = -100 gives solid gray image.
|
||||||
|
//
|
||||||
|
// Examples:
|
||||||
|
//
|
||||||
|
// dstImage = imaging.AdjustContrast(srcImage, -10) // Decrease image contrast by 10%.
|
||||||
|
// dstImage = imaging.AdjustContrast(srcImage, 20) // Increase image contrast by 20%.
|
||||||
|
//
|
||||||
|
func AdjustContrast(img image.Image, percentage float64) *image.NRGBA {
|
||||||
|
percentage = math.Min(math.Max(percentage, -100.0), 100.0)
|
||||||
|
lut := make([]uint8, 256)
|
||||||
|
|
||||||
|
v := (100.0 + percentage) / 100.0
|
||||||
|
for i := 0; i < 256; i++ {
|
||||||
|
switch {
|
||||||
|
case 0 <= v && v <= 1:
|
||||||
|
lut[i] = clamp((0.5 + (float64(i)/255.0-0.5)*v) * 255.0)
|
||||||
|
case 1 < v && v < 2:
|
||||||
|
lut[i] = clamp((0.5 + (float64(i)/255.0-0.5)*(1/(2.0-v))) * 255.0)
|
||||||
|
default:
|
||||||
|
lut[i] = uint8(float64(i)/255.0+0.5) * 255
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return adjustLUT(img, lut)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AdjustBrightness changes the brightness of the image using the percentage parameter and returns the adjusted image.
|
||||||
|
// The percentage must be in range (-100, 100). The percentage = 0 gives the original image.
|
||||||
|
// The percentage = -100 gives solid black image. The percentage = 100 gives solid white image.
|
||||||
|
//
|
||||||
|
// Examples:
|
||||||
|
//
|
||||||
|
// dstImage = imaging.AdjustBrightness(srcImage, -15) // Decrease image brightness by 15%.
|
||||||
|
// dstImage = imaging.AdjustBrightness(srcImage, 10) // Increase image brightness by 10%.
|
||||||
|
//
|
||||||
|
func AdjustBrightness(img image.Image, percentage float64) *image.NRGBA {
|
||||||
|
percentage = math.Min(math.Max(percentage, -100.0), 100.0)
|
||||||
|
lut := make([]uint8, 256)
|
||||||
|
|
||||||
|
shift := 255.0 * percentage / 100.0
|
||||||
|
for i := 0; i < 256; i++ {
|
||||||
|
lut[i] = clamp(float64(i) + shift)
|
||||||
|
}
|
||||||
|
|
||||||
|
return adjustLUT(img, lut)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AdjustGamma performs a gamma correction on the image and returns the adjusted image.
|
||||||
|
// Gamma parameter must be positive. Gamma = 1.0 gives the original image.
|
||||||
|
// Gamma less than 1.0 darkens the image and gamma greater than 1.0 lightens it.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// dstImage = imaging.AdjustGamma(srcImage, 0.7)
|
||||||
|
//
|
||||||
|
func AdjustGamma(img image.Image, gamma float64) *image.NRGBA {
|
||||||
|
e := 1.0 / math.Max(gamma, 0.0001)
|
||||||
|
lut := make([]uint8, 256)
|
||||||
|
|
||||||
|
for i := 0; i < 256; i++ {
|
||||||
|
lut[i] = clamp(math.Pow(float64(i)/255.0, e) * 255.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
return adjustLUT(img, lut)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AdjustSigmoid changes the contrast of the image using a sigmoidal function and returns the adjusted image.
|
||||||
|
// It's a non-linear contrast change useful for photo adjustments as it preserves highlight and shadow detail.
|
||||||
|
// The midpoint parameter is the midpoint of contrast that must be between 0 and 1, typically 0.5.
|
||||||
|
// The factor parameter indicates how much to increase or decrease the contrast, typically in range (-10, 10).
|
||||||
|
// If the factor parameter is positive the image contrast is increased otherwise the contrast is decreased.
|
||||||
|
//
|
||||||
|
// Examples:
|
||||||
|
//
|
||||||
|
// dstImage = imaging.AdjustSigmoid(srcImage, 0.5, 3.0) // Increase the contrast.
|
||||||
|
// dstImage = imaging.AdjustSigmoid(srcImage, 0.5, -3.0) // Decrease the contrast.
|
||||||
|
//
|
||||||
|
func AdjustSigmoid(img image.Image, midpoint, factor float64) *image.NRGBA {
|
||||||
|
if factor == 0 {
|
||||||
|
return Clone(img)
|
||||||
|
}
|
||||||
|
|
||||||
|
lut := make([]uint8, 256)
|
||||||
|
a := math.Min(math.Max(midpoint, 0.0), 1.0)
|
||||||
|
b := math.Abs(factor)
|
||||||
|
sig0 := sigmoid(a, b, 0)
|
||||||
|
sig1 := sigmoid(a, b, 1)
|
||||||
|
e := 1.0e-6
|
||||||
|
|
||||||
|
if factor > 0 {
|
||||||
|
for i := 0; i < 256; i++ {
|
||||||
|
x := float64(i) / 255.0
|
||||||
|
sigX := sigmoid(a, b, x)
|
||||||
|
f := (sigX - sig0) / (sig1 - sig0)
|
||||||
|
lut[i] = clamp(f * 255.0)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for i := 0; i < 256; i++ {
|
||||||
|
x := float64(i) / 255.0
|
||||||
|
arg := math.Min(math.Max((sig1-sig0)*x+sig0, e), 1.0-e)
|
||||||
|
f := a - math.Log(1.0/arg-1.0)/b
|
||||||
|
lut[i] = clamp(f * 255.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return adjustLUT(img, lut)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sigmoid(a, b, x float64) float64 {
|
||||||
|
return 1 / (1 + math.Exp(b*(a-x)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// adjustLUT applies the given lookup table to the colors of the image.
|
||||||
|
func adjustLUT(img image.Image, lut []uint8) *image.NRGBA {
|
||||||
|
src := newScanner(img)
|
||||||
|
dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h))
|
||||||
|
lut = lut[0:256]
|
||||||
|
parallel(0, src.h, func(ys <-chan int) {
|
||||||
|
for y := range ys {
|
||||||
|
i := y * dst.Stride
|
||||||
|
src.scan(0, y, src.w, y+1, dst.Pix[i:i+src.w*4])
|
||||||
|
for x := 0; x < src.w; x++ {
|
||||||
|
d := dst.Pix[i : i+3 : i+3]
|
||||||
|
d[0] = lut[d[0]]
|
||||||
|
d[1] = lut[d[1]]
|
||||||
|
d[2] = lut[d[2]]
|
||||||
|
i += 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// AdjustFunc applies the fn function to each pixel of the img image and returns the adjusted image.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// dstImage = imaging.AdjustFunc(
|
||||||
|
// srcImage,
|
||||||
|
// func(c color.NRGBA) color.NRGBA {
|
||||||
|
// // Shift the red channel by 16.
|
||||||
|
// r := int(c.R) + 16
|
||||||
|
// if r > 255 {
|
||||||
|
// r = 255
|
||||||
|
// }
|
||||||
|
// return color.NRGBA{uint8(r), c.G, c.B, c.A}
|
||||||
|
// }
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
func AdjustFunc(img image.Image, fn func(c color.NRGBA) color.NRGBA) *image.NRGBA {
|
||||||
|
src := newScanner(img)
|
||||||
|
dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h))
|
||||||
|
parallel(0, src.h, func(ys <-chan int) {
|
||||||
|
for y := range ys {
|
||||||
|
i := y * dst.Stride
|
||||||
|
src.scan(0, y, src.w, y+1, dst.Pix[i:i+src.w*4])
|
||||||
|
for x := 0; x < src.w; x++ {
|
||||||
|
d := dst.Pix[i : i+4 : i+4]
|
||||||
|
r := d[0]
|
||||||
|
g := d[1]
|
||||||
|
b := d[2]
|
||||||
|
a := d[3]
|
||||||
|
c := fn(color.NRGBA{r, g, b, a})
|
||||||
|
d[0] = c.R
|
||||||
|
d[1] = c.G
|
||||||
|
d[2] = c.B
|
||||||
|
d[3] = c.A
|
||||||
|
i += 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return dst
|
||||||
|
}
|
||||||
148
vendor/github.com/disintegration/imaging/convolution.go
generated
vendored
Normal file
148
vendor/github.com/disintegration/imaging/convolution.go
generated
vendored
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
package imaging
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ConvolveOptions are convolution parameters.
|
||||||
|
type ConvolveOptions struct {
|
||||||
|
// If Normalize is true the kernel is normalized before convolution.
|
||||||
|
Normalize bool
|
||||||
|
|
||||||
|
// If Abs is true the absolute value of each color channel is taken after convolution.
|
||||||
|
Abs bool
|
||||||
|
|
||||||
|
// Bias is added to each color channel value after convolution.
|
||||||
|
Bias int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convolve3x3 convolves the image with the specified 3x3 convolution kernel.
|
||||||
|
// Default parameters are used if a nil *ConvolveOptions is passed.
|
||||||
|
func Convolve3x3(img image.Image, kernel [9]float64, options *ConvolveOptions) *image.NRGBA {
|
||||||
|
return convolve(img, kernel[:], options)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convolve5x5 convolves the image with the specified 5x5 convolution kernel.
|
||||||
|
// Default parameters are used if a nil *ConvolveOptions is passed.
|
||||||
|
func Convolve5x5(img image.Image, kernel [25]float64, options *ConvolveOptions) *image.NRGBA {
|
||||||
|
return convolve(img, kernel[:], options)
|
||||||
|
}
|
||||||
|
|
||||||
|
func convolve(img image.Image, kernel []float64, options *ConvolveOptions) *image.NRGBA {
|
||||||
|
src := toNRGBA(img)
|
||||||
|
w := src.Bounds().Max.X
|
||||||
|
h := src.Bounds().Max.Y
|
||||||
|
dst := image.NewNRGBA(image.Rect(0, 0, w, h))
|
||||||
|
|
||||||
|
if w < 1 || h < 1 {
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
if options == nil {
|
||||||
|
options = &ConvolveOptions{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if options.Normalize {
|
||||||
|
normalizeKernel(kernel)
|
||||||
|
}
|
||||||
|
|
||||||
|
type coef struct {
|
||||||
|
x, y int
|
||||||
|
k float64
|
||||||
|
}
|
||||||
|
var coefs []coef
|
||||||
|
var m int
|
||||||
|
|
||||||
|
switch len(kernel) {
|
||||||
|
case 9:
|
||||||
|
m = 1
|
||||||
|
case 25:
|
||||||
|
m = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
i := 0
|
||||||
|
for y := -m; y <= m; y++ {
|
||||||
|
for x := -m; x <= m; x++ {
|
||||||
|
if kernel[i] != 0 {
|
||||||
|
coefs = append(coefs, coef{x: x, y: y, k: kernel[i]})
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parallel(0, h, func(ys <-chan int) {
|
||||||
|
for y := range ys {
|
||||||
|
for x := 0; x < w; x++ {
|
||||||
|
var r, g, b float64
|
||||||
|
for _, c := range coefs {
|
||||||
|
ix := x + c.x
|
||||||
|
if ix < 0 {
|
||||||
|
ix = 0
|
||||||
|
} else if ix >= w {
|
||||||
|
ix = w - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
iy := y + c.y
|
||||||
|
if iy < 0 {
|
||||||
|
iy = 0
|
||||||
|
} else if iy >= h {
|
||||||
|
iy = h - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
off := iy*src.Stride + ix*4
|
||||||
|
s := src.Pix[off : off+3 : off+3]
|
||||||
|
r += float64(s[0]) * c.k
|
||||||
|
g += float64(s[1]) * c.k
|
||||||
|
b += float64(s[2]) * c.k
|
||||||
|
}
|
||||||
|
|
||||||
|
if options.Abs {
|
||||||
|
if r < 0 {
|
||||||
|
r = -r
|
||||||
|
}
|
||||||
|
if g < 0 {
|
||||||
|
g = -g
|
||||||
|
}
|
||||||
|
if b < 0 {
|
||||||
|
b = -b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if options.Bias != 0 {
|
||||||
|
r += float64(options.Bias)
|
||||||
|
g += float64(options.Bias)
|
||||||
|
b += float64(options.Bias)
|
||||||
|
}
|
||||||
|
|
||||||
|
srcOff := y*src.Stride + x*4
|
||||||
|
dstOff := y*dst.Stride + x*4
|
||||||
|
d := dst.Pix[dstOff : dstOff+4 : dstOff+4]
|
||||||
|
d[0] = clamp(r)
|
||||||
|
d[1] = clamp(g)
|
||||||
|
d[2] = clamp(b)
|
||||||
|
d[3] = src.Pix[srcOff+3]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizeKernel(kernel []float64) {
|
||||||
|
var sum, sumpos float64
|
||||||
|
for i := range kernel {
|
||||||
|
sum += kernel[i]
|
||||||
|
if kernel[i] > 0 {
|
||||||
|
sumpos += kernel[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if sum != 0 {
|
||||||
|
for i := range kernel {
|
||||||
|
kernel[i] /= sum
|
||||||
|
}
|
||||||
|
} else if sumpos != 0 {
|
||||||
|
for i := range kernel {
|
||||||
|
kernel[i] /= sumpos
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
vendor/github.com/disintegration/imaging/doc.go
generated
vendored
Normal file
7
vendor/github.com/disintegration/imaging/doc.go
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
/*
|
||||||
|
Package imaging provides basic image processing functions (resize, rotate, crop, brightness/contrast adjustments, etc.).
|
||||||
|
|
||||||
|
All the image processing functions provided by the package accept any image type that implements image.Image interface
|
||||||
|
as an input, and return a new image of *image.NRGBA type (32bit RGBA colors, non-premultiplied alpha).
|
||||||
|
*/
|
||||||
|
package imaging
|
||||||
169
vendor/github.com/disintegration/imaging/effects.go
generated
vendored
Normal file
169
vendor/github.com/disintegration/imaging/effects.go
generated
vendored
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
package imaging
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
func gaussianBlurKernel(x, sigma float64) float64 {
|
||||||
|
return math.Exp(-(x*x)/(2*sigma*sigma)) / (sigma * math.Sqrt(2*math.Pi))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Blur produces a blurred version of the image using a Gaussian function.
|
||||||
|
// Sigma parameter must be positive and indicates how much the image will be blurred.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// dstImage := imaging.Blur(srcImage, 3.5)
|
||||||
|
//
|
||||||
|
func Blur(img image.Image, sigma float64) *image.NRGBA {
|
||||||
|
if sigma <= 0 {
|
||||||
|
return Clone(img)
|
||||||
|
}
|
||||||
|
|
||||||
|
radius := int(math.Ceil(sigma * 3.0))
|
||||||
|
kernel := make([]float64, radius+1)
|
||||||
|
|
||||||
|
for i := 0; i <= radius; i++ {
|
||||||
|
kernel[i] = gaussianBlurKernel(float64(i), sigma)
|
||||||
|
}
|
||||||
|
|
||||||
|
return blurVertical(blurHorizontal(img, kernel), kernel)
|
||||||
|
}
|
||||||
|
|
||||||
|
func blurHorizontal(img image.Image, kernel []float64) *image.NRGBA {
|
||||||
|
src := newScanner(img)
|
||||||
|
dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h))
|
||||||
|
radius := len(kernel) - 1
|
||||||
|
|
||||||
|
parallel(0, src.h, func(ys <-chan int) {
|
||||||
|
scanLine := make([]uint8, src.w*4)
|
||||||
|
scanLineF := make([]float64, len(scanLine))
|
||||||
|
for y := range ys {
|
||||||
|
src.scan(0, y, src.w, y+1, scanLine)
|
||||||
|
for i, v := range scanLine {
|
||||||
|
scanLineF[i] = float64(v)
|
||||||
|
}
|
||||||
|
for x := 0; x < src.w; x++ {
|
||||||
|
min := x - radius
|
||||||
|
if min < 0 {
|
||||||
|
min = 0
|
||||||
|
}
|
||||||
|
max := x + radius
|
||||||
|
if max > src.w-1 {
|
||||||
|
max = src.w - 1
|
||||||
|
}
|
||||||
|
var r, g, b, a, wsum float64
|
||||||
|
for ix := min; ix <= max; ix++ {
|
||||||
|
i := ix * 4
|
||||||
|
weight := kernel[absint(x-ix)]
|
||||||
|
wsum += weight
|
||||||
|
s := scanLineF[i : i+4 : i+4]
|
||||||
|
wa := s[3] * weight
|
||||||
|
r += s[0] * wa
|
||||||
|
g += s[1] * wa
|
||||||
|
b += s[2] * wa
|
||||||
|
a += wa
|
||||||
|
}
|
||||||
|
if a != 0 {
|
||||||
|
aInv := 1 / a
|
||||||
|
j := y*dst.Stride + x*4
|
||||||
|
d := dst.Pix[j : j+4 : j+4]
|
||||||
|
d[0] = clamp(r * aInv)
|
||||||
|
d[1] = clamp(g * aInv)
|
||||||
|
d[2] = clamp(b * aInv)
|
||||||
|
d[3] = clamp(a / wsum)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
func blurVertical(img image.Image, kernel []float64) *image.NRGBA {
|
||||||
|
src := newScanner(img)
|
||||||
|
dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h))
|
||||||
|
radius := len(kernel) - 1
|
||||||
|
|
||||||
|
parallel(0, src.w, func(xs <-chan int) {
|
||||||
|
scanLine := make([]uint8, src.h*4)
|
||||||
|
scanLineF := make([]float64, len(scanLine))
|
||||||
|
for x := range xs {
|
||||||
|
src.scan(x, 0, x+1, src.h, scanLine)
|
||||||
|
for i, v := range scanLine {
|
||||||
|
scanLineF[i] = float64(v)
|
||||||
|
}
|
||||||
|
for y := 0; y < src.h; y++ {
|
||||||
|
min := y - radius
|
||||||
|
if min < 0 {
|
||||||
|
min = 0
|
||||||
|
}
|
||||||
|
max := y + radius
|
||||||
|
if max > src.h-1 {
|
||||||
|
max = src.h - 1
|
||||||
|
}
|
||||||
|
var r, g, b, a, wsum float64
|
||||||
|
for iy := min; iy <= max; iy++ {
|
||||||
|
i := iy * 4
|
||||||
|
weight := kernel[absint(y-iy)]
|
||||||
|
wsum += weight
|
||||||
|
s := scanLineF[i : i+4 : i+4]
|
||||||
|
wa := s[3] * weight
|
||||||
|
r += s[0] * wa
|
||||||
|
g += s[1] * wa
|
||||||
|
b += s[2] * wa
|
||||||
|
a += wa
|
||||||
|
}
|
||||||
|
if a != 0 {
|
||||||
|
aInv := 1 / a
|
||||||
|
j := y*dst.Stride + x*4
|
||||||
|
d := dst.Pix[j : j+4 : j+4]
|
||||||
|
d[0] = clamp(r * aInv)
|
||||||
|
d[1] = clamp(g * aInv)
|
||||||
|
d[2] = clamp(b * aInv)
|
||||||
|
d[3] = clamp(a / wsum)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sharpen produces a sharpened version of the image.
|
||||||
|
// Sigma parameter must be positive and indicates how much the image will be sharpened.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// dstImage := imaging.Sharpen(srcImage, 3.5)
|
||||||
|
//
|
||||||
|
func Sharpen(img image.Image, sigma float64) *image.NRGBA {
|
||||||
|
if sigma <= 0 {
|
||||||
|
return Clone(img)
|
||||||
|
}
|
||||||
|
|
||||||
|
src := newScanner(img)
|
||||||
|
dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h))
|
||||||
|
blurred := Blur(img, sigma)
|
||||||
|
|
||||||
|
parallel(0, src.h, func(ys <-chan int) {
|
||||||
|
scanLine := make([]uint8, src.w*4)
|
||||||
|
for y := range ys {
|
||||||
|
src.scan(0, y, src.w, y+1, scanLine)
|
||||||
|
j := y * dst.Stride
|
||||||
|
for i := 0; i < src.w*4; i++ {
|
||||||
|
val := int(scanLine[i])<<1 - int(blurred.Pix[j])
|
||||||
|
if val < 0 {
|
||||||
|
val = 0
|
||||||
|
} else if val > 0xff {
|
||||||
|
val = 0xff
|
||||||
|
}
|
||||||
|
dst.Pix[j] = uint8(val)
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return dst
|
||||||
|
}
|
||||||
52
vendor/github.com/disintegration/imaging/histogram.go
generated
vendored
Normal file
52
vendor/github.com/disintegration/imaging/histogram.go
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
package imaging
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Histogram returns a normalized histogram of an image.
|
||||||
|
//
|
||||||
|
// Resulting histogram is represented as an array of 256 floats, where
|
||||||
|
// histogram[i] is a probability of a pixel being of a particular luminance i.
|
||||||
|
func Histogram(img image.Image) [256]float64 {
|
||||||
|
var mu sync.Mutex
|
||||||
|
var histogram [256]float64
|
||||||
|
var total float64
|
||||||
|
|
||||||
|
src := newScanner(img)
|
||||||
|
if src.w == 0 || src.h == 0 {
|
||||||
|
return histogram
|
||||||
|
}
|
||||||
|
|
||||||
|
parallel(0, src.h, func(ys <-chan int) {
|
||||||
|
var tmpHistogram [256]float64
|
||||||
|
var tmpTotal float64
|
||||||
|
scanLine := make([]uint8, src.w*4)
|
||||||
|
for y := range ys {
|
||||||
|
src.scan(0, y, src.w, y+1, scanLine)
|
||||||
|
i := 0
|
||||||
|
for x := 0; x < src.w; x++ {
|
||||||
|
s := scanLine[i : i+3 : i+3]
|
||||||
|
r := s[0]
|
||||||
|
g := s[1]
|
||||||
|
b := s[2]
|
||||||
|
y := 0.299*float32(r) + 0.587*float32(g) + 0.114*float32(b)
|
||||||
|
tmpHistogram[int(y+0.5)]++
|
||||||
|
tmpTotal++
|
||||||
|
i += 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mu.Lock()
|
||||||
|
for i := 0; i < 256; i++ {
|
||||||
|
histogram[i] += tmpHistogram[i]
|
||||||
|
}
|
||||||
|
total += tmpTotal
|
||||||
|
mu.Unlock()
|
||||||
|
})
|
||||||
|
|
||||||
|
for i := 0; i < 256; i++ {
|
||||||
|
histogram[i] = histogram[i] / total
|
||||||
|
}
|
||||||
|
return histogram
|
||||||
|
}
|
||||||
444
vendor/github.com/disintegration/imaging/io.go
generated
vendored
Normal file
444
vendor/github.com/disintegration/imaging/io.go
generated
vendored
Normal file
@@ -0,0 +1,444 @@
|
|||||||
|
package imaging
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"image"
|
||||||
|
"image/draw"
|
||||||
|
"image/gif"
|
||||||
|
"image/jpeg"
|
||||||
|
"image/png"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/image/bmp"
|
||||||
|
"golang.org/x/image/tiff"
|
||||||
|
)
|
||||||
|
|
||||||
|
type fileSystem interface {
|
||||||
|
Create(string) (io.WriteCloser, error)
|
||||||
|
Open(string) (io.ReadCloser, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type localFS struct{}
|
||||||
|
|
||||||
|
func (localFS) Create(name string) (io.WriteCloser, error) { return os.Create(name) }
|
||||||
|
func (localFS) Open(name string) (io.ReadCloser, error) { return os.Open(name) }
|
||||||
|
|
||||||
|
var fs fileSystem = localFS{}
|
||||||
|
|
||||||
|
type decodeConfig struct {
|
||||||
|
autoOrientation bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultDecodeConfig = decodeConfig{
|
||||||
|
autoOrientation: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeOption sets an optional parameter for the Decode and Open functions.
|
||||||
|
type DecodeOption func(*decodeConfig)
|
||||||
|
|
||||||
|
// AutoOrientation returns a DecodeOption that sets the auto-orientation mode.
|
||||||
|
// If auto-orientation is enabled, the image will be transformed after decoding
|
||||||
|
// according to the EXIF orientation tag (if present). By default it's disabled.
|
||||||
|
func AutoOrientation(enabled bool) DecodeOption {
|
||||||
|
return func(c *decodeConfig) {
|
||||||
|
c.autoOrientation = enabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode reads an image from r.
|
||||||
|
func Decode(r io.Reader, opts ...DecodeOption) (image.Image, error) {
|
||||||
|
cfg := defaultDecodeConfig
|
||||||
|
for _, option := range opts {
|
||||||
|
option(&cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cfg.autoOrientation {
|
||||||
|
img, _, err := image.Decode(r)
|
||||||
|
return img, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var orient orientation
|
||||||
|
pr, pw := io.Pipe()
|
||||||
|
r = io.TeeReader(r, pw)
|
||||||
|
done := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
defer close(done)
|
||||||
|
orient = readOrientation(pr)
|
||||||
|
io.Copy(ioutil.Discard, pr)
|
||||||
|
}()
|
||||||
|
|
||||||
|
img, _, err := image.Decode(r)
|
||||||
|
pw.Close()
|
||||||
|
<-done
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return fixOrientation(img, orient), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open loads an image from file.
|
||||||
|
//
|
||||||
|
// Examples:
|
||||||
|
//
|
||||||
|
// // Load an image from file.
|
||||||
|
// img, err := imaging.Open("test.jpg")
|
||||||
|
//
|
||||||
|
// // Load an image and transform it depending on the EXIF orientation tag (if present).
|
||||||
|
// img, err := imaging.Open("test.jpg", imaging.AutoOrientation(true))
|
||||||
|
//
|
||||||
|
func Open(filename string, opts ...DecodeOption) (image.Image, error) {
|
||||||
|
file, err := fs.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
return Decode(file, opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format is an image file format.
|
||||||
|
type Format int
|
||||||
|
|
||||||
|
// Image file formats.
|
||||||
|
const (
|
||||||
|
JPEG Format = iota
|
||||||
|
PNG
|
||||||
|
GIF
|
||||||
|
TIFF
|
||||||
|
BMP
|
||||||
|
)
|
||||||
|
|
||||||
|
var formatExts = map[string]Format{
|
||||||
|
"jpg": JPEG,
|
||||||
|
"jpeg": JPEG,
|
||||||
|
"png": PNG,
|
||||||
|
"gif": GIF,
|
||||||
|
"tif": TIFF,
|
||||||
|
"tiff": TIFF,
|
||||||
|
"bmp": BMP,
|
||||||
|
}
|
||||||
|
|
||||||
|
var formatNames = map[Format]string{
|
||||||
|
JPEG: "JPEG",
|
||||||
|
PNG: "PNG",
|
||||||
|
GIF: "GIF",
|
||||||
|
TIFF: "TIFF",
|
||||||
|
BMP: "BMP",
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f Format) String() string {
|
||||||
|
return formatNames[f]
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrUnsupportedFormat means the given image format is not supported.
|
||||||
|
var ErrUnsupportedFormat = errors.New("imaging: unsupported image format")
|
||||||
|
|
||||||
|
// FormatFromExtension parses image format from filename extension:
|
||||||
|
// "jpg" (or "jpeg"), "png", "gif", "tif" (or "tiff") and "bmp" are supported.
|
||||||
|
func FormatFromExtension(ext string) (Format, error) {
|
||||||
|
if f, ok := formatExts[strings.ToLower(strings.TrimPrefix(ext, "."))]; ok {
|
||||||
|
return f, nil
|
||||||
|
}
|
||||||
|
return -1, ErrUnsupportedFormat
|
||||||
|
}
|
||||||
|
|
||||||
|
// FormatFromFilename parses image format from filename:
|
||||||
|
// "jpg" (or "jpeg"), "png", "gif", "tif" (or "tiff") and "bmp" are supported.
|
||||||
|
func FormatFromFilename(filename string) (Format, error) {
|
||||||
|
ext := filepath.Ext(filename)
|
||||||
|
return FormatFromExtension(ext)
|
||||||
|
}
|
||||||
|
|
||||||
|
type encodeConfig struct {
|
||||||
|
jpegQuality int
|
||||||
|
gifNumColors int
|
||||||
|
gifQuantizer draw.Quantizer
|
||||||
|
gifDrawer draw.Drawer
|
||||||
|
pngCompressionLevel png.CompressionLevel
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultEncodeConfig = encodeConfig{
|
||||||
|
jpegQuality: 95,
|
||||||
|
gifNumColors: 256,
|
||||||
|
gifQuantizer: nil,
|
||||||
|
gifDrawer: nil,
|
||||||
|
pngCompressionLevel: png.DefaultCompression,
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeOption sets an optional parameter for the Encode and Save functions.
|
||||||
|
type EncodeOption func(*encodeConfig)
|
||||||
|
|
||||||
|
// JPEGQuality returns an EncodeOption that sets the output JPEG quality.
|
||||||
|
// Quality ranges from 1 to 100 inclusive, higher is better. Default is 95.
|
||||||
|
func JPEGQuality(quality int) EncodeOption {
|
||||||
|
return func(c *encodeConfig) {
|
||||||
|
c.jpegQuality = quality
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GIFNumColors returns an EncodeOption that sets the maximum number of colors
|
||||||
|
// used in the GIF-encoded image. It ranges from 1 to 256. Default is 256.
|
||||||
|
func GIFNumColors(numColors int) EncodeOption {
|
||||||
|
return func(c *encodeConfig) {
|
||||||
|
c.gifNumColors = numColors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GIFQuantizer returns an EncodeOption that sets the quantizer that is used to produce
|
||||||
|
// a palette of the GIF-encoded image.
|
||||||
|
func GIFQuantizer(quantizer draw.Quantizer) EncodeOption {
|
||||||
|
return func(c *encodeConfig) {
|
||||||
|
c.gifQuantizer = quantizer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GIFDrawer returns an EncodeOption that sets the drawer that is used to convert
|
||||||
|
// the source image to the desired palette of the GIF-encoded image.
|
||||||
|
func GIFDrawer(drawer draw.Drawer) EncodeOption {
|
||||||
|
return func(c *encodeConfig) {
|
||||||
|
c.gifDrawer = drawer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PNGCompressionLevel returns an EncodeOption that sets the compression level
|
||||||
|
// of the PNG-encoded image. Default is png.DefaultCompression.
|
||||||
|
func PNGCompressionLevel(level png.CompressionLevel) EncodeOption {
|
||||||
|
return func(c *encodeConfig) {
|
||||||
|
c.pngCompressionLevel = level
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode writes the image img to w in the specified format (JPEG, PNG, GIF, TIFF or BMP).
|
||||||
|
func Encode(w io.Writer, img image.Image, format Format, opts ...EncodeOption) error {
|
||||||
|
cfg := defaultEncodeConfig
|
||||||
|
for _, option := range opts {
|
||||||
|
option(&cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch format {
|
||||||
|
case JPEG:
|
||||||
|
if nrgba, ok := img.(*image.NRGBA); ok && nrgba.Opaque() {
|
||||||
|
rgba := &image.RGBA{
|
||||||
|
Pix: nrgba.Pix,
|
||||||
|
Stride: nrgba.Stride,
|
||||||
|
Rect: nrgba.Rect,
|
||||||
|
}
|
||||||
|
return jpeg.Encode(w, rgba, &jpeg.Options{Quality: cfg.jpegQuality})
|
||||||
|
}
|
||||||
|
return jpeg.Encode(w, img, &jpeg.Options{Quality: cfg.jpegQuality})
|
||||||
|
|
||||||
|
case PNG:
|
||||||
|
encoder := png.Encoder{CompressionLevel: cfg.pngCompressionLevel}
|
||||||
|
return encoder.Encode(w, img)
|
||||||
|
|
||||||
|
case GIF:
|
||||||
|
return gif.Encode(w, img, &gif.Options{
|
||||||
|
NumColors: cfg.gifNumColors,
|
||||||
|
Quantizer: cfg.gifQuantizer,
|
||||||
|
Drawer: cfg.gifDrawer,
|
||||||
|
})
|
||||||
|
|
||||||
|
case TIFF:
|
||||||
|
return tiff.Encode(w, img, &tiff.Options{Compression: tiff.Deflate, Predictor: true})
|
||||||
|
|
||||||
|
case BMP:
|
||||||
|
return bmp.Encode(w, img)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ErrUnsupportedFormat
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save saves the image to file with the specified filename.
|
||||||
|
// The format is determined from the filename extension:
|
||||||
|
// "jpg" (or "jpeg"), "png", "gif", "tif" (or "tiff") and "bmp" are supported.
|
||||||
|
//
|
||||||
|
// Examples:
|
||||||
|
//
|
||||||
|
// // Save the image as PNG.
|
||||||
|
// err := imaging.Save(img, "out.png")
|
||||||
|
//
|
||||||
|
// // Save the image as JPEG with optional quality parameter set to 80.
|
||||||
|
// err := imaging.Save(img, "out.jpg", imaging.JPEGQuality(80))
|
||||||
|
//
|
||||||
|
func Save(img image.Image, filename string, opts ...EncodeOption) (err error) {
|
||||||
|
f, err := FormatFromFilename(filename)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
file, err := fs.Create(filename)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = Encode(file, img, f, opts...)
|
||||||
|
errc := file.Close()
|
||||||
|
if err == nil {
|
||||||
|
err = errc
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// orientation is an EXIF flag that specifies the transformation
|
||||||
|
// that should be applied to image to display it correctly.
|
||||||
|
type orientation int
|
||||||
|
|
||||||
|
const (
|
||||||
|
orientationUnspecified = 0
|
||||||
|
orientationNormal = 1
|
||||||
|
orientationFlipH = 2
|
||||||
|
orientationRotate180 = 3
|
||||||
|
orientationFlipV = 4
|
||||||
|
orientationTranspose = 5
|
||||||
|
orientationRotate270 = 6
|
||||||
|
orientationTransverse = 7
|
||||||
|
orientationRotate90 = 8
|
||||||
|
)
|
||||||
|
|
||||||
|
// readOrientation tries to read the orientation EXIF flag from image data in r.
|
||||||
|
// If the EXIF data block is not found or the orientation flag is not found
|
||||||
|
// or any other error occures while reading the data, it returns the
|
||||||
|
// orientationUnspecified (0) value.
|
||||||
|
func readOrientation(r io.Reader) orientation {
|
||||||
|
const (
|
||||||
|
markerSOI = 0xffd8
|
||||||
|
markerAPP1 = 0xffe1
|
||||||
|
exifHeader = 0x45786966
|
||||||
|
byteOrderBE = 0x4d4d
|
||||||
|
byteOrderLE = 0x4949
|
||||||
|
orientationTag = 0x0112
|
||||||
|
)
|
||||||
|
|
||||||
|
// Check if JPEG SOI marker is present.
|
||||||
|
var soi uint16
|
||||||
|
if err := binary.Read(r, binary.BigEndian, &soi); err != nil {
|
||||||
|
return orientationUnspecified
|
||||||
|
}
|
||||||
|
if soi != markerSOI {
|
||||||
|
return orientationUnspecified // Missing JPEG SOI marker.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find JPEG APP1 marker.
|
||||||
|
for {
|
||||||
|
var marker, size uint16
|
||||||
|
if err := binary.Read(r, binary.BigEndian, &marker); err != nil {
|
||||||
|
return orientationUnspecified
|
||||||
|
}
|
||||||
|
if err := binary.Read(r, binary.BigEndian, &size); err != nil {
|
||||||
|
return orientationUnspecified
|
||||||
|
}
|
||||||
|
if marker>>8 != 0xff {
|
||||||
|
return orientationUnspecified // Invalid JPEG marker.
|
||||||
|
}
|
||||||
|
if marker == markerAPP1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if size < 2 {
|
||||||
|
return orientationUnspecified // Invalid block size.
|
||||||
|
}
|
||||||
|
if _, err := io.CopyN(ioutil.Discard, r, int64(size-2)); err != nil {
|
||||||
|
return orientationUnspecified
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if EXIF header is present.
|
||||||
|
var header uint32
|
||||||
|
if err := binary.Read(r, binary.BigEndian, &header); err != nil {
|
||||||
|
return orientationUnspecified
|
||||||
|
}
|
||||||
|
if header != exifHeader {
|
||||||
|
return orientationUnspecified
|
||||||
|
}
|
||||||
|
if _, err := io.CopyN(ioutil.Discard, r, 2); err != nil {
|
||||||
|
return orientationUnspecified
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read byte order information.
|
||||||
|
var (
|
||||||
|
byteOrderTag uint16
|
||||||
|
byteOrder binary.ByteOrder
|
||||||
|
)
|
||||||
|
if err := binary.Read(r, binary.BigEndian, &byteOrderTag); err != nil {
|
||||||
|
return orientationUnspecified
|
||||||
|
}
|
||||||
|
switch byteOrderTag {
|
||||||
|
case byteOrderBE:
|
||||||
|
byteOrder = binary.BigEndian
|
||||||
|
case byteOrderLE:
|
||||||
|
byteOrder = binary.LittleEndian
|
||||||
|
default:
|
||||||
|
return orientationUnspecified // Invalid byte order flag.
|
||||||
|
}
|
||||||
|
if _, err := io.CopyN(ioutil.Discard, r, 2); err != nil {
|
||||||
|
return orientationUnspecified
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip the EXIF offset.
|
||||||
|
var offset uint32
|
||||||
|
if err := binary.Read(r, byteOrder, &offset); err != nil {
|
||||||
|
return orientationUnspecified
|
||||||
|
}
|
||||||
|
if offset < 8 {
|
||||||
|
return orientationUnspecified // Invalid offset value.
|
||||||
|
}
|
||||||
|
if _, err := io.CopyN(ioutil.Discard, r, int64(offset-8)); err != nil {
|
||||||
|
return orientationUnspecified
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the number of tags.
|
||||||
|
var numTags uint16
|
||||||
|
if err := binary.Read(r, byteOrder, &numTags); err != nil {
|
||||||
|
return orientationUnspecified
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the orientation tag.
|
||||||
|
for i := 0; i < int(numTags); i++ {
|
||||||
|
var tag uint16
|
||||||
|
if err := binary.Read(r, byteOrder, &tag); err != nil {
|
||||||
|
return orientationUnspecified
|
||||||
|
}
|
||||||
|
if tag != orientationTag {
|
||||||
|
if _, err := io.CopyN(ioutil.Discard, r, 10); err != nil {
|
||||||
|
return orientationUnspecified
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, err := io.CopyN(ioutil.Discard, r, 6); err != nil {
|
||||||
|
return orientationUnspecified
|
||||||
|
}
|
||||||
|
var val uint16
|
||||||
|
if err := binary.Read(r, byteOrder, &val); err != nil {
|
||||||
|
return orientationUnspecified
|
||||||
|
}
|
||||||
|
if val < 1 || val > 8 {
|
||||||
|
return orientationUnspecified // Invalid tag value.
|
||||||
|
}
|
||||||
|
return orientation(val)
|
||||||
|
}
|
||||||
|
return orientationUnspecified // Missing orientation tag.
|
||||||
|
}
|
||||||
|
|
||||||
|
// fixOrientation applies a transform to img corresponding to the given orientation flag.
|
||||||
|
func fixOrientation(img image.Image, o orientation) image.Image {
|
||||||
|
switch o {
|
||||||
|
case orientationNormal:
|
||||||
|
case orientationFlipH:
|
||||||
|
img = FlipH(img)
|
||||||
|
case orientationFlipV:
|
||||||
|
img = FlipV(img)
|
||||||
|
case orientationRotate90:
|
||||||
|
img = Rotate90(img)
|
||||||
|
case orientationRotate180:
|
||||||
|
img = Rotate180(img)
|
||||||
|
case orientationRotate270:
|
||||||
|
img = Rotate270(img)
|
||||||
|
case orientationTranspose:
|
||||||
|
img = Transpose(img)
|
||||||
|
case orientationTransverse:
|
||||||
|
img = Transverse(img)
|
||||||
|
}
|
||||||
|
return img
|
||||||
|
}
|
||||||
595
vendor/github.com/disintegration/imaging/resize.go
generated
vendored
Normal file
595
vendor/github.com/disintegration/imaging/resize.go
generated
vendored
Normal file
@@ -0,0 +1,595 @@
|
|||||||
|
package imaging
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
type indexWeight struct {
|
||||||
|
index int
|
||||||
|
weight float64
|
||||||
|
}
|
||||||
|
|
||||||
|
func precomputeWeights(dstSize, srcSize int, filter ResampleFilter) [][]indexWeight {
|
||||||
|
du := float64(srcSize) / float64(dstSize)
|
||||||
|
scale := du
|
||||||
|
if scale < 1.0 {
|
||||||
|
scale = 1.0
|
||||||
|
}
|
||||||
|
ru := math.Ceil(scale * filter.Support)
|
||||||
|
|
||||||
|
out := make([][]indexWeight, dstSize)
|
||||||
|
tmp := make([]indexWeight, 0, dstSize*int(ru+2)*2)
|
||||||
|
|
||||||
|
for v := 0; v < dstSize; v++ {
|
||||||
|
fu := (float64(v)+0.5)*du - 0.5
|
||||||
|
|
||||||
|
begin := int(math.Ceil(fu - ru))
|
||||||
|
if begin < 0 {
|
||||||
|
begin = 0
|
||||||
|
}
|
||||||
|
end := int(math.Floor(fu + ru))
|
||||||
|
if end > srcSize-1 {
|
||||||
|
end = srcSize - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
var sum float64
|
||||||
|
for u := begin; u <= end; u++ {
|
||||||
|
w := filter.Kernel((float64(u) - fu) / scale)
|
||||||
|
if w != 0 {
|
||||||
|
sum += w
|
||||||
|
tmp = append(tmp, indexWeight{index: u, weight: w})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if sum != 0 {
|
||||||
|
for i := range tmp {
|
||||||
|
tmp[i].weight /= sum
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out[v] = tmp
|
||||||
|
tmp = tmp[len(tmp):]
|
||||||
|
}
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resize resizes the image to the specified width and height using the specified resampling
|
||||||
|
// filter and returns the transformed image. If one of width or height is 0, the image aspect
|
||||||
|
// ratio is preserved.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// dstImage := imaging.Resize(srcImage, 800, 600, imaging.Lanczos)
|
||||||
|
//
|
||||||
|
func Resize(img image.Image, width, height int, filter ResampleFilter) *image.NRGBA {
|
||||||
|
dstW, dstH := width, height
|
||||||
|
if dstW < 0 || dstH < 0 {
|
||||||
|
return &image.NRGBA{}
|
||||||
|
}
|
||||||
|
if dstW == 0 && dstH == 0 {
|
||||||
|
return &image.NRGBA{}
|
||||||
|
}
|
||||||
|
|
||||||
|
srcW := img.Bounds().Dx()
|
||||||
|
srcH := img.Bounds().Dy()
|
||||||
|
if srcW <= 0 || srcH <= 0 {
|
||||||
|
return &image.NRGBA{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If new width or height is 0 then preserve aspect ratio, minimum 1px.
|
||||||
|
if dstW == 0 {
|
||||||
|
tmpW := float64(dstH) * float64(srcW) / float64(srcH)
|
||||||
|
dstW = int(math.Max(1.0, math.Floor(tmpW+0.5)))
|
||||||
|
}
|
||||||
|
if dstH == 0 {
|
||||||
|
tmpH := float64(dstW) * float64(srcH) / float64(srcW)
|
||||||
|
dstH = int(math.Max(1.0, math.Floor(tmpH+0.5)))
|
||||||
|
}
|
||||||
|
|
||||||
|
if filter.Support <= 0 {
|
||||||
|
// Nearest-neighbor special case.
|
||||||
|
return resizeNearest(img, dstW, dstH)
|
||||||
|
}
|
||||||
|
|
||||||
|
if srcW != dstW && srcH != dstH {
|
||||||
|
return resizeVertical(resizeHorizontal(img, dstW, filter), dstH, filter)
|
||||||
|
}
|
||||||
|
if srcW != dstW {
|
||||||
|
return resizeHorizontal(img, dstW, filter)
|
||||||
|
}
|
||||||
|
if srcH != dstH {
|
||||||
|
return resizeVertical(img, dstH, filter)
|
||||||
|
}
|
||||||
|
return Clone(img)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resizeHorizontal(img image.Image, width int, filter ResampleFilter) *image.NRGBA {
|
||||||
|
src := newScanner(img)
|
||||||
|
dst := image.NewNRGBA(image.Rect(0, 0, width, src.h))
|
||||||
|
weights := precomputeWeights(width, src.w, filter)
|
||||||
|
parallel(0, src.h, func(ys <-chan int) {
|
||||||
|
scanLine := make([]uint8, src.w*4)
|
||||||
|
for y := range ys {
|
||||||
|
src.scan(0, y, src.w, y+1, scanLine)
|
||||||
|
j0 := y * dst.Stride
|
||||||
|
for x := range weights {
|
||||||
|
var r, g, b, a float64
|
||||||
|
for _, w := range weights[x] {
|
||||||
|
i := w.index * 4
|
||||||
|
s := scanLine[i : i+4 : i+4]
|
||||||
|
aw := float64(s[3]) * w.weight
|
||||||
|
r += float64(s[0]) * aw
|
||||||
|
g += float64(s[1]) * aw
|
||||||
|
b += float64(s[2]) * aw
|
||||||
|
a += aw
|
||||||
|
}
|
||||||
|
if a != 0 {
|
||||||
|
aInv := 1 / a
|
||||||
|
j := j0 + x*4
|
||||||
|
d := dst.Pix[j : j+4 : j+4]
|
||||||
|
d[0] = clamp(r * aInv)
|
||||||
|
d[1] = clamp(g * aInv)
|
||||||
|
d[2] = clamp(b * aInv)
|
||||||
|
d[3] = clamp(a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
func resizeVertical(img image.Image, height int, filter ResampleFilter) *image.NRGBA {
|
||||||
|
src := newScanner(img)
|
||||||
|
dst := image.NewNRGBA(image.Rect(0, 0, src.w, height))
|
||||||
|
weights := precomputeWeights(height, src.h, filter)
|
||||||
|
parallel(0, src.w, func(xs <-chan int) {
|
||||||
|
scanLine := make([]uint8, src.h*4)
|
||||||
|
for x := range xs {
|
||||||
|
src.scan(x, 0, x+1, src.h, scanLine)
|
||||||
|
for y := range weights {
|
||||||
|
var r, g, b, a float64
|
||||||
|
for _, w := range weights[y] {
|
||||||
|
i := w.index * 4
|
||||||
|
s := scanLine[i : i+4 : i+4]
|
||||||
|
aw := float64(s[3]) * w.weight
|
||||||
|
r += float64(s[0]) * aw
|
||||||
|
g += float64(s[1]) * aw
|
||||||
|
b += float64(s[2]) * aw
|
||||||
|
a += aw
|
||||||
|
}
|
||||||
|
if a != 0 {
|
||||||
|
aInv := 1 / a
|
||||||
|
j := y*dst.Stride + x*4
|
||||||
|
d := dst.Pix[j : j+4 : j+4]
|
||||||
|
d[0] = clamp(r * aInv)
|
||||||
|
d[1] = clamp(g * aInv)
|
||||||
|
d[2] = clamp(b * aInv)
|
||||||
|
d[3] = clamp(a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// resizeNearest is a fast nearest-neighbor resize, no filtering.
|
||||||
|
func resizeNearest(img image.Image, width, height int) *image.NRGBA {
|
||||||
|
dst := image.NewNRGBA(image.Rect(0, 0, width, height))
|
||||||
|
dx := float64(img.Bounds().Dx()) / float64(width)
|
||||||
|
dy := float64(img.Bounds().Dy()) / float64(height)
|
||||||
|
|
||||||
|
if dx > 1 && dy > 1 {
|
||||||
|
src := newScanner(img)
|
||||||
|
parallel(0, height, func(ys <-chan int) {
|
||||||
|
for y := range ys {
|
||||||
|
srcY := int((float64(y) + 0.5) * dy)
|
||||||
|
dstOff := y * dst.Stride
|
||||||
|
for x := 0; x < width; x++ {
|
||||||
|
srcX := int((float64(x) + 0.5) * dx)
|
||||||
|
src.scan(srcX, srcY, srcX+1, srcY+1, dst.Pix[dstOff:dstOff+4])
|
||||||
|
dstOff += 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
src := toNRGBA(img)
|
||||||
|
parallel(0, height, func(ys <-chan int) {
|
||||||
|
for y := range ys {
|
||||||
|
srcY := int((float64(y) + 0.5) * dy)
|
||||||
|
srcOff0 := srcY * src.Stride
|
||||||
|
dstOff := y * dst.Stride
|
||||||
|
for x := 0; x < width; x++ {
|
||||||
|
srcX := int((float64(x) + 0.5) * dx)
|
||||||
|
srcOff := srcOff0 + srcX*4
|
||||||
|
copy(dst.Pix[dstOff:dstOff+4], src.Pix[srcOff:srcOff+4])
|
||||||
|
dstOff += 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fit scales down the image using the specified resample filter to fit the specified
|
||||||
|
// maximum width and height and returns the transformed image.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// dstImage := imaging.Fit(srcImage, 800, 600, imaging.Lanczos)
|
||||||
|
//
|
||||||
|
func Fit(img image.Image, width, height int, filter ResampleFilter) *image.NRGBA {
|
||||||
|
maxW, maxH := width, height
|
||||||
|
|
||||||
|
if maxW <= 0 || maxH <= 0 {
|
||||||
|
return &image.NRGBA{}
|
||||||
|
}
|
||||||
|
|
||||||
|
srcBounds := img.Bounds()
|
||||||
|
srcW := srcBounds.Dx()
|
||||||
|
srcH := srcBounds.Dy()
|
||||||
|
|
||||||
|
if srcW <= 0 || srcH <= 0 {
|
||||||
|
return &image.NRGBA{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if srcW <= maxW && srcH <= maxH {
|
||||||
|
return Clone(img)
|
||||||
|
}
|
||||||
|
|
||||||
|
srcAspectRatio := float64(srcW) / float64(srcH)
|
||||||
|
maxAspectRatio := float64(maxW) / float64(maxH)
|
||||||
|
|
||||||
|
var newW, newH int
|
||||||
|
if srcAspectRatio > maxAspectRatio {
|
||||||
|
newW = maxW
|
||||||
|
newH = int(float64(newW) / srcAspectRatio)
|
||||||
|
} else {
|
||||||
|
newH = maxH
|
||||||
|
newW = int(float64(newH) * srcAspectRatio)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Resize(img, newW, newH, filter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill creates an image with the specified dimensions and fills it with the scaled source image.
|
||||||
|
// To achieve the correct aspect ratio without stretching, the source image will be cropped.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// dstImage := imaging.Fill(srcImage, 800, 600, imaging.Center, imaging.Lanczos)
|
||||||
|
//
|
||||||
|
func Fill(img image.Image, width, height int, anchor Anchor, filter ResampleFilter) *image.NRGBA {
|
||||||
|
dstW, dstH := width, height
|
||||||
|
|
||||||
|
if dstW <= 0 || dstH <= 0 {
|
||||||
|
return &image.NRGBA{}
|
||||||
|
}
|
||||||
|
|
||||||
|
srcBounds := img.Bounds()
|
||||||
|
srcW := srcBounds.Dx()
|
||||||
|
srcH := srcBounds.Dy()
|
||||||
|
|
||||||
|
if srcW <= 0 || srcH <= 0 {
|
||||||
|
return &image.NRGBA{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if srcW == dstW && srcH == dstH {
|
||||||
|
return Clone(img)
|
||||||
|
}
|
||||||
|
|
||||||
|
if srcW >= 100 && srcH >= 100 {
|
||||||
|
return cropAndResize(img, dstW, dstH, anchor, filter)
|
||||||
|
}
|
||||||
|
return resizeAndCrop(img, dstW, dstH, anchor, filter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// cropAndResize crops the image to the smallest possible size that has the required aspect ratio using
|
||||||
|
// the given anchor point, then scales it to the specified dimensions and returns the transformed image.
|
||||||
|
//
|
||||||
|
// This is generally faster than resizing first, but may result in inaccuracies when used on small source images.
|
||||||
|
func cropAndResize(img image.Image, width, height int, anchor Anchor, filter ResampleFilter) *image.NRGBA {
|
||||||
|
dstW, dstH := width, height
|
||||||
|
|
||||||
|
srcBounds := img.Bounds()
|
||||||
|
srcW := srcBounds.Dx()
|
||||||
|
srcH := srcBounds.Dy()
|
||||||
|
srcAspectRatio := float64(srcW) / float64(srcH)
|
||||||
|
dstAspectRatio := float64(dstW) / float64(dstH)
|
||||||
|
|
||||||
|
var tmp *image.NRGBA
|
||||||
|
if srcAspectRatio < dstAspectRatio {
|
||||||
|
cropH := float64(srcW) * float64(dstH) / float64(dstW)
|
||||||
|
tmp = CropAnchor(img, srcW, int(math.Max(1, cropH)+0.5), anchor)
|
||||||
|
} else {
|
||||||
|
cropW := float64(srcH) * float64(dstW) / float64(dstH)
|
||||||
|
tmp = CropAnchor(img, int(math.Max(1, cropW)+0.5), srcH, anchor)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Resize(tmp, dstW, dstH, filter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// resizeAndCrop resizes the image to the smallest possible size that will cover the specified dimensions,
|
||||||
|
// crops the resized image to the specified dimensions using the given anchor point and returns
|
||||||
|
// the transformed image.
|
||||||
|
func resizeAndCrop(img image.Image, width, height int, anchor Anchor, filter ResampleFilter) *image.NRGBA {
|
||||||
|
dstW, dstH := width, height
|
||||||
|
|
||||||
|
srcBounds := img.Bounds()
|
||||||
|
srcW := srcBounds.Dx()
|
||||||
|
srcH := srcBounds.Dy()
|
||||||
|
srcAspectRatio := float64(srcW) / float64(srcH)
|
||||||
|
dstAspectRatio := float64(dstW) / float64(dstH)
|
||||||
|
|
||||||
|
var tmp *image.NRGBA
|
||||||
|
if srcAspectRatio < dstAspectRatio {
|
||||||
|
tmp = Resize(img, dstW, 0, filter)
|
||||||
|
} else {
|
||||||
|
tmp = Resize(img, 0, dstH, filter)
|
||||||
|
}
|
||||||
|
|
||||||
|
return CropAnchor(tmp, dstW, dstH, anchor)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Thumbnail scales the image up or down using the specified resample filter, crops it
|
||||||
|
// to the specified width and hight and returns the transformed image.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// dstImage := imaging.Thumbnail(srcImage, 100, 100, imaging.Lanczos)
|
||||||
|
//
|
||||||
|
func Thumbnail(img image.Image, width, height int, filter ResampleFilter) *image.NRGBA {
|
||||||
|
return Fill(img, width, height, Center, filter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResampleFilter specifies a resampling filter to be used for image resizing.
|
||||||
|
//
|
||||||
|
// General filter recommendations:
|
||||||
|
//
|
||||||
|
// - Lanczos
|
||||||
|
// A high-quality resampling filter for photographic images yielding sharp results.
|
||||||
|
//
|
||||||
|
// - CatmullRom
|
||||||
|
// A sharp cubic filter that is faster than Lanczos filter while providing similar results.
|
||||||
|
//
|
||||||
|
// - MitchellNetravali
|
||||||
|
// A cubic filter that produces smoother results with less ringing artifacts than CatmullRom.
|
||||||
|
//
|
||||||
|
// - Linear
|
||||||
|
// Bilinear resampling filter, produces a smooth output. Faster than cubic filters.
|
||||||
|
//
|
||||||
|
// - Box
|
||||||
|
// Simple and fast averaging filter appropriate for downscaling.
|
||||||
|
// When upscaling it's similar to NearestNeighbor.
|
||||||
|
//
|
||||||
|
// - NearestNeighbor
|
||||||
|
// Fastest resampling filter, no antialiasing.
|
||||||
|
//
|
||||||
|
type ResampleFilter struct {
|
||||||
|
Support float64
|
||||||
|
Kernel func(float64) float64
|
||||||
|
}
|
||||||
|
|
||||||
|
// NearestNeighbor is a nearest-neighbor filter (no anti-aliasing).
|
||||||
|
var NearestNeighbor ResampleFilter
|
||||||
|
|
||||||
|
// Box filter (averaging pixels).
|
||||||
|
var Box ResampleFilter
|
||||||
|
|
||||||
|
// Linear filter.
|
||||||
|
var Linear ResampleFilter
|
||||||
|
|
||||||
|
// Hermite cubic spline filter (BC-spline; B=0; C=0).
|
||||||
|
var Hermite ResampleFilter
|
||||||
|
|
||||||
|
// MitchellNetravali is Mitchell-Netravali cubic filter (BC-spline; B=1/3; C=1/3).
|
||||||
|
var MitchellNetravali ResampleFilter
|
||||||
|
|
||||||
|
// CatmullRom is a Catmull-Rom - sharp cubic filter (BC-spline; B=0; C=0.5).
|
||||||
|
var CatmullRom ResampleFilter
|
||||||
|
|
||||||
|
// BSpline is a smooth cubic filter (BC-spline; B=1; C=0).
|
||||||
|
var BSpline ResampleFilter
|
||||||
|
|
||||||
|
// Gaussian is a Gaussian blurring filter.
|
||||||
|
var Gaussian ResampleFilter
|
||||||
|
|
||||||
|
// Bartlett is a Bartlett-windowed sinc filter (3 lobes).
|
||||||
|
var Bartlett ResampleFilter
|
||||||
|
|
||||||
|
// Lanczos filter (3 lobes).
|
||||||
|
var Lanczos ResampleFilter
|
||||||
|
|
||||||
|
// Hann is a Hann-windowed sinc filter (3 lobes).
|
||||||
|
var Hann ResampleFilter
|
||||||
|
|
||||||
|
// Hamming is a Hamming-windowed sinc filter (3 lobes).
|
||||||
|
var Hamming ResampleFilter
|
||||||
|
|
||||||
|
// Blackman is a Blackman-windowed sinc filter (3 lobes).
|
||||||
|
var Blackman ResampleFilter
|
||||||
|
|
||||||
|
// Welch is a Welch-windowed sinc filter (parabolic window, 3 lobes).
|
||||||
|
var Welch ResampleFilter
|
||||||
|
|
||||||
|
// Cosine is a Cosine-windowed sinc filter (3 lobes).
|
||||||
|
var Cosine ResampleFilter
|
||||||
|
|
||||||
|
func bcspline(x, b, c float64) float64 {
|
||||||
|
var y float64
|
||||||
|
x = math.Abs(x)
|
||||||
|
if x < 1.0 {
|
||||||
|
y = ((12-9*b-6*c)*x*x*x + (-18+12*b+6*c)*x*x + (6 - 2*b)) / 6
|
||||||
|
} else if x < 2.0 {
|
||||||
|
y = ((-b-6*c)*x*x*x + (6*b+30*c)*x*x + (-12*b-48*c)*x + (8*b + 24*c)) / 6
|
||||||
|
}
|
||||||
|
return y
|
||||||
|
}
|
||||||
|
|
||||||
|
func sinc(x float64) float64 {
|
||||||
|
if x == 0 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return math.Sin(math.Pi*x) / (math.Pi * x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
NearestNeighbor = ResampleFilter{
|
||||||
|
Support: 0.0, // special case - not applying the filter
|
||||||
|
}
|
||||||
|
|
||||||
|
Box = ResampleFilter{
|
||||||
|
Support: 0.5,
|
||||||
|
Kernel: func(x float64) float64 {
|
||||||
|
x = math.Abs(x)
|
||||||
|
if x <= 0.5 {
|
||||||
|
return 1.0
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
Linear = ResampleFilter{
|
||||||
|
Support: 1.0,
|
||||||
|
Kernel: func(x float64) float64 {
|
||||||
|
x = math.Abs(x)
|
||||||
|
if x < 1.0 {
|
||||||
|
return 1.0 - x
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
Hermite = ResampleFilter{
|
||||||
|
Support: 1.0,
|
||||||
|
Kernel: func(x float64) float64 {
|
||||||
|
x = math.Abs(x)
|
||||||
|
if x < 1.0 {
|
||||||
|
return bcspline(x, 0.0, 0.0)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
MitchellNetravali = ResampleFilter{
|
||||||
|
Support: 2.0,
|
||||||
|
Kernel: func(x float64) float64 {
|
||||||
|
x = math.Abs(x)
|
||||||
|
if x < 2.0 {
|
||||||
|
return bcspline(x, 1.0/3.0, 1.0/3.0)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
CatmullRom = ResampleFilter{
|
||||||
|
Support: 2.0,
|
||||||
|
Kernel: func(x float64) float64 {
|
||||||
|
x = math.Abs(x)
|
||||||
|
if x < 2.0 {
|
||||||
|
return bcspline(x, 0.0, 0.5)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
BSpline = ResampleFilter{
|
||||||
|
Support: 2.0,
|
||||||
|
Kernel: func(x float64) float64 {
|
||||||
|
x = math.Abs(x)
|
||||||
|
if x < 2.0 {
|
||||||
|
return bcspline(x, 1.0, 0.0)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
Gaussian = ResampleFilter{
|
||||||
|
Support: 2.0,
|
||||||
|
Kernel: func(x float64) float64 {
|
||||||
|
x = math.Abs(x)
|
||||||
|
if x < 2.0 {
|
||||||
|
return math.Exp(-2 * x * x)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
Bartlett = ResampleFilter{
|
||||||
|
Support: 3.0,
|
||||||
|
Kernel: func(x float64) float64 {
|
||||||
|
x = math.Abs(x)
|
||||||
|
if x < 3.0 {
|
||||||
|
return sinc(x) * (3.0 - x) / 3.0
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
Lanczos = ResampleFilter{
|
||||||
|
Support: 3.0,
|
||||||
|
Kernel: func(x float64) float64 {
|
||||||
|
x = math.Abs(x)
|
||||||
|
if x < 3.0 {
|
||||||
|
return sinc(x) * sinc(x/3.0)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
Hann = ResampleFilter{
|
||||||
|
Support: 3.0,
|
||||||
|
Kernel: func(x float64) float64 {
|
||||||
|
x = math.Abs(x)
|
||||||
|
if x < 3.0 {
|
||||||
|
return sinc(x) * (0.5 + 0.5*math.Cos(math.Pi*x/3.0))
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
Hamming = ResampleFilter{
|
||||||
|
Support: 3.0,
|
||||||
|
Kernel: func(x float64) float64 {
|
||||||
|
x = math.Abs(x)
|
||||||
|
if x < 3.0 {
|
||||||
|
return sinc(x) * (0.54 + 0.46*math.Cos(math.Pi*x/3.0))
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
Blackman = ResampleFilter{
|
||||||
|
Support: 3.0,
|
||||||
|
Kernel: func(x float64) float64 {
|
||||||
|
x = math.Abs(x)
|
||||||
|
if x < 3.0 {
|
||||||
|
return sinc(x) * (0.42 - 0.5*math.Cos(math.Pi*x/3.0+math.Pi) + 0.08*math.Cos(2.0*math.Pi*x/3.0))
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
Welch = ResampleFilter{
|
||||||
|
Support: 3.0,
|
||||||
|
Kernel: func(x float64) float64 {
|
||||||
|
x = math.Abs(x)
|
||||||
|
if x < 3.0 {
|
||||||
|
return sinc(x) * (1.0 - (x * x / 9.0))
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
Cosine = ResampleFilter{
|
||||||
|
Support: 3.0,
|
||||||
|
Kernel: func(x float64) float64 {
|
||||||
|
x = math.Abs(x)
|
||||||
|
if x < 3.0 {
|
||||||
|
return sinc(x) * math.Cos((math.Pi/2.0)*(x/3.0))
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
285
vendor/github.com/disintegration/imaging/scanner.go
generated
vendored
Normal file
285
vendor/github.com/disintegration/imaging/scanner.go
generated
vendored
Normal file
@@ -0,0 +1,285 @@
|
|||||||
|
package imaging
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
)
|
||||||
|
|
||||||
|
type scanner struct {
|
||||||
|
image image.Image
|
||||||
|
w, h int
|
||||||
|
palette []color.NRGBA
|
||||||
|
}
|
||||||
|
|
||||||
|
func newScanner(img image.Image) *scanner {
|
||||||
|
s := &scanner{
|
||||||
|
image: img,
|
||||||
|
w: img.Bounds().Dx(),
|
||||||
|
h: img.Bounds().Dy(),
|
||||||
|
}
|
||||||
|
if img, ok := img.(*image.Paletted); ok {
|
||||||
|
s.palette = make([]color.NRGBA, len(img.Palette))
|
||||||
|
for i := 0; i < len(img.Palette); i++ {
|
||||||
|
s.palette[i] = color.NRGBAModel.Convert(img.Palette[i]).(color.NRGBA)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// scan scans the given rectangular region of the image into dst.
|
||||||
|
func (s *scanner) scan(x1, y1, x2, y2 int, dst []uint8) {
|
||||||
|
switch img := s.image.(type) {
|
||||||
|
case *image.NRGBA:
|
||||||
|
size := (x2 - x1) * 4
|
||||||
|
j := 0
|
||||||
|
i := y1*img.Stride + x1*4
|
||||||
|
if size == 4 {
|
||||||
|
for y := y1; y < y2; y++ {
|
||||||
|
d := dst[j : j+4 : j+4]
|
||||||
|
s := img.Pix[i : i+4 : i+4]
|
||||||
|
d[0] = s[0]
|
||||||
|
d[1] = s[1]
|
||||||
|
d[2] = s[2]
|
||||||
|
d[3] = s[3]
|
||||||
|
j += size
|
||||||
|
i += img.Stride
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for y := y1; y < y2; y++ {
|
||||||
|
copy(dst[j:j+size], img.Pix[i:i+size])
|
||||||
|
j += size
|
||||||
|
i += img.Stride
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case *image.NRGBA64:
|
||||||
|
j := 0
|
||||||
|
for y := y1; y < y2; y++ {
|
||||||
|
i := y*img.Stride + x1*8
|
||||||
|
for x := x1; x < x2; x++ {
|
||||||
|
s := img.Pix[i : i+8 : i+8]
|
||||||
|
d := dst[j : j+4 : j+4]
|
||||||
|
d[0] = s[0]
|
||||||
|
d[1] = s[2]
|
||||||
|
d[2] = s[4]
|
||||||
|
d[3] = s[6]
|
||||||
|
j += 4
|
||||||
|
i += 8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case *image.RGBA:
|
||||||
|
j := 0
|
||||||
|
for y := y1; y < y2; y++ {
|
||||||
|
i := y*img.Stride + x1*4
|
||||||
|
for x := x1; x < x2; x++ {
|
||||||
|
d := dst[j : j+4 : j+4]
|
||||||
|
a := img.Pix[i+3]
|
||||||
|
switch a {
|
||||||
|
case 0:
|
||||||
|
d[0] = 0
|
||||||
|
d[1] = 0
|
||||||
|
d[2] = 0
|
||||||
|
d[3] = a
|
||||||
|
case 0xff:
|
||||||
|
s := img.Pix[i : i+4 : i+4]
|
||||||
|
d[0] = s[0]
|
||||||
|
d[1] = s[1]
|
||||||
|
d[2] = s[2]
|
||||||
|
d[3] = a
|
||||||
|
default:
|
||||||
|
s := img.Pix[i : i+4 : i+4]
|
||||||
|
r16 := uint16(s[0])
|
||||||
|
g16 := uint16(s[1])
|
||||||
|
b16 := uint16(s[2])
|
||||||
|
a16 := uint16(a)
|
||||||
|
d[0] = uint8(r16 * 0xff / a16)
|
||||||
|
d[1] = uint8(g16 * 0xff / a16)
|
||||||
|
d[2] = uint8(b16 * 0xff / a16)
|
||||||
|
d[3] = a
|
||||||
|
}
|
||||||
|
j += 4
|
||||||
|
i += 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case *image.RGBA64:
|
||||||
|
j := 0
|
||||||
|
for y := y1; y < y2; y++ {
|
||||||
|
i := y*img.Stride + x1*8
|
||||||
|
for x := x1; x < x2; x++ {
|
||||||
|
s := img.Pix[i : i+8 : i+8]
|
||||||
|
d := dst[j : j+4 : j+4]
|
||||||
|
a := s[6]
|
||||||
|
switch a {
|
||||||
|
case 0:
|
||||||
|
d[0] = 0
|
||||||
|
d[1] = 0
|
||||||
|
d[2] = 0
|
||||||
|
case 0xff:
|
||||||
|
d[0] = s[0]
|
||||||
|
d[1] = s[2]
|
||||||
|
d[2] = s[4]
|
||||||
|
default:
|
||||||
|
r32 := uint32(s[0])<<8 | uint32(s[1])
|
||||||
|
g32 := uint32(s[2])<<8 | uint32(s[3])
|
||||||
|
b32 := uint32(s[4])<<8 | uint32(s[5])
|
||||||
|
a32 := uint32(s[6])<<8 | uint32(s[7])
|
||||||
|
d[0] = uint8((r32 * 0xffff / a32) >> 8)
|
||||||
|
d[1] = uint8((g32 * 0xffff / a32) >> 8)
|
||||||
|
d[2] = uint8((b32 * 0xffff / a32) >> 8)
|
||||||
|
}
|
||||||
|
d[3] = a
|
||||||
|
j += 4
|
||||||
|
i += 8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case *image.Gray:
|
||||||
|
j := 0
|
||||||
|
for y := y1; y < y2; y++ {
|
||||||
|
i := y*img.Stride + x1
|
||||||
|
for x := x1; x < x2; x++ {
|
||||||
|
c := img.Pix[i]
|
||||||
|
d := dst[j : j+4 : j+4]
|
||||||
|
d[0] = c
|
||||||
|
d[1] = c
|
||||||
|
d[2] = c
|
||||||
|
d[3] = 0xff
|
||||||
|
j += 4
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case *image.Gray16:
|
||||||
|
j := 0
|
||||||
|
for y := y1; y < y2; y++ {
|
||||||
|
i := y*img.Stride + x1*2
|
||||||
|
for x := x1; x < x2; x++ {
|
||||||
|
c := img.Pix[i]
|
||||||
|
d := dst[j : j+4 : j+4]
|
||||||
|
d[0] = c
|
||||||
|
d[1] = c
|
||||||
|
d[2] = c
|
||||||
|
d[3] = 0xff
|
||||||
|
j += 4
|
||||||
|
i += 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case *image.YCbCr:
|
||||||
|
j := 0
|
||||||
|
x1 += img.Rect.Min.X
|
||||||
|
x2 += img.Rect.Min.X
|
||||||
|
y1 += img.Rect.Min.Y
|
||||||
|
y2 += img.Rect.Min.Y
|
||||||
|
|
||||||
|
hy := img.Rect.Min.Y / 2
|
||||||
|
hx := img.Rect.Min.X / 2
|
||||||
|
for y := y1; y < y2; y++ {
|
||||||
|
iy := (y-img.Rect.Min.Y)*img.YStride + (x1 - img.Rect.Min.X)
|
||||||
|
|
||||||
|
var yBase int
|
||||||
|
switch img.SubsampleRatio {
|
||||||
|
case image.YCbCrSubsampleRatio444, image.YCbCrSubsampleRatio422:
|
||||||
|
yBase = (y - img.Rect.Min.Y) * img.CStride
|
||||||
|
case image.YCbCrSubsampleRatio420, image.YCbCrSubsampleRatio440:
|
||||||
|
yBase = (y/2 - hy) * img.CStride
|
||||||
|
}
|
||||||
|
|
||||||
|
for x := x1; x < x2; x++ {
|
||||||
|
var ic int
|
||||||
|
switch img.SubsampleRatio {
|
||||||
|
case image.YCbCrSubsampleRatio444, image.YCbCrSubsampleRatio440:
|
||||||
|
ic = yBase + (x - img.Rect.Min.X)
|
||||||
|
case image.YCbCrSubsampleRatio422, image.YCbCrSubsampleRatio420:
|
||||||
|
ic = yBase + (x/2 - hx)
|
||||||
|
default:
|
||||||
|
ic = img.COffset(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
yy1 := int32(img.Y[iy]) * 0x10101
|
||||||
|
cb1 := int32(img.Cb[ic]) - 128
|
||||||
|
cr1 := int32(img.Cr[ic]) - 128
|
||||||
|
|
||||||
|
r := yy1 + 91881*cr1
|
||||||
|
if uint32(r)&0xff000000 == 0 {
|
||||||
|
r >>= 16
|
||||||
|
} else {
|
||||||
|
r = ^(r >> 31)
|
||||||
|
}
|
||||||
|
|
||||||
|
g := yy1 - 22554*cb1 - 46802*cr1
|
||||||
|
if uint32(g)&0xff000000 == 0 {
|
||||||
|
g >>= 16
|
||||||
|
} else {
|
||||||
|
g = ^(g >> 31)
|
||||||
|
}
|
||||||
|
|
||||||
|
b := yy1 + 116130*cb1
|
||||||
|
if uint32(b)&0xff000000 == 0 {
|
||||||
|
b >>= 16
|
||||||
|
} else {
|
||||||
|
b = ^(b >> 31)
|
||||||
|
}
|
||||||
|
|
||||||
|
d := dst[j : j+4 : j+4]
|
||||||
|
d[0] = uint8(r)
|
||||||
|
d[1] = uint8(g)
|
||||||
|
d[2] = uint8(b)
|
||||||
|
d[3] = 0xff
|
||||||
|
|
||||||
|
iy++
|
||||||
|
j += 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case *image.Paletted:
|
||||||
|
j := 0
|
||||||
|
for y := y1; y < y2; y++ {
|
||||||
|
i := y*img.Stride + x1
|
||||||
|
for x := x1; x < x2; x++ {
|
||||||
|
c := s.palette[img.Pix[i]]
|
||||||
|
d := dst[j : j+4 : j+4]
|
||||||
|
d[0] = c.R
|
||||||
|
d[1] = c.G
|
||||||
|
d[2] = c.B
|
||||||
|
d[3] = c.A
|
||||||
|
j += 4
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
j := 0
|
||||||
|
b := s.image.Bounds()
|
||||||
|
x1 += b.Min.X
|
||||||
|
x2 += b.Min.X
|
||||||
|
y1 += b.Min.Y
|
||||||
|
y2 += b.Min.Y
|
||||||
|
for y := y1; y < y2; y++ {
|
||||||
|
for x := x1; x < x2; x++ {
|
||||||
|
r16, g16, b16, a16 := s.image.At(x, y).RGBA()
|
||||||
|
d := dst[j : j+4 : j+4]
|
||||||
|
switch a16 {
|
||||||
|
case 0xffff:
|
||||||
|
d[0] = uint8(r16 >> 8)
|
||||||
|
d[1] = uint8(g16 >> 8)
|
||||||
|
d[2] = uint8(b16 >> 8)
|
||||||
|
d[3] = 0xff
|
||||||
|
case 0:
|
||||||
|
d[0] = 0
|
||||||
|
d[1] = 0
|
||||||
|
d[2] = 0
|
||||||
|
d[3] = 0
|
||||||
|
default:
|
||||||
|
d[0] = uint8(((r16 * 0xffff) / a16) >> 8)
|
||||||
|
d[1] = uint8(((g16 * 0xffff) / a16) >> 8)
|
||||||
|
d[2] = uint8(((b16 * 0xffff) / a16) >> 8)
|
||||||
|
d[3] = uint8(a16 >> 8)
|
||||||
|
}
|
||||||
|
j += 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
249
vendor/github.com/disintegration/imaging/tools.go
generated
vendored
Normal file
249
vendor/github.com/disintegration/imaging/tools.go
generated
vendored
Normal file
@@ -0,0 +1,249 @@
|
|||||||
|
package imaging
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
// New creates a new image with the specified width and height, and fills it with the specified color.
|
||||||
|
func New(width, height int, fillColor color.Color) *image.NRGBA {
|
||||||
|
if width <= 0 || height <= 0 {
|
||||||
|
return &image.NRGBA{}
|
||||||
|
}
|
||||||
|
|
||||||
|
c := color.NRGBAModel.Convert(fillColor).(color.NRGBA)
|
||||||
|
if (c == color.NRGBA{0, 0, 0, 0}) {
|
||||||
|
return image.NewNRGBA(image.Rect(0, 0, width, height))
|
||||||
|
}
|
||||||
|
|
||||||
|
return &image.NRGBA{
|
||||||
|
Pix: bytes.Repeat([]byte{c.R, c.G, c.B, c.A}, width*height),
|
||||||
|
Stride: 4 * width,
|
||||||
|
Rect: image.Rect(0, 0, width, height),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clone returns a copy of the given image.
|
||||||
|
func Clone(img image.Image) *image.NRGBA {
|
||||||
|
src := newScanner(img)
|
||||||
|
dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h))
|
||||||
|
size := src.w * 4
|
||||||
|
parallel(0, src.h, func(ys <-chan int) {
|
||||||
|
for y := range ys {
|
||||||
|
i := y * dst.Stride
|
||||||
|
src.scan(0, y, src.w, y+1, dst.Pix[i:i+size])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// Anchor is the anchor point for image alignment.
|
||||||
|
type Anchor int
|
||||||
|
|
||||||
|
// Anchor point positions.
|
||||||
|
const (
|
||||||
|
Center Anchor = iota
|
||||||
|
TopLeft
|
||||||
|
Top
|
||||||
|
TopRight
|
||||||
|
Left
|
||||||
|
Right
|
||||||
|
BottomLeft
|
||||||
|
Bottom
|
||||||
|
BottomRight
|
||||||
|
)
|
||||||
|
|
||||||
|
func anchorPt(b image.Rectangle, w, h int, anchor Anchor) image.Point {
|
||||||
|
var x, y int
|
||||||
|
switch anchor {
|
||||||
|
case TopLeft:
|
||||||
|
x = b.Min.X
|
||||||
|
y = b.Min.Y
|
||||||
|
case Top:
|
||||||
|
x = b.Min.X + (b.Dx()-w)/2
|
||||||
|
y = b.Min.Y
|
||||||
|
case TopRight:
|
||||||
|
x = b.Max.X - w
|
||||||
|
y = b.Min.Y
|
||||||
|
case Left:
|
||||||
|
x = b.Min.X
|
||||||
|
y = b.Min.Y + (b.Dy()-h)/2
|
||||||
|
case Right:
|
||||||
|
x = b.Max.X - w
|
||||||
|
y = b.Min.Y + (b.Dy()-h)/2
|
||||||
|
case BottomLeft:
|
||||||
|
x = b.Min.X
|
||||||
|
y = b.Max.Y - h
|
||||||
|
case Bottom:
|
||||||
|
x = b.Min.X + (b.Dx()-w)/2
|
||||||
|
y = b.Max.Y - h
|
||||||
|
case BottomRight:
|
||||||
|
x = b.Max.X - w
|
||||||
|
y = b.Max.Y - h
|
||||||
|
default:
|
||||||
|
x = b.Min.X + (b.Dx()-w)/2
|
||||||
|
y = b.Min.Y + (b.Dy()-h)/2
|
||||||
|
}
|
||||||
|
return image.Pt(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Crop cuts out a rectangular region with the specified bounds
|
||||||
|
// from the image and returns the cropped image.
|
||||||
|
func Crop(img image.Image, rect image.Rectangle) *image.NRGBA {
|
||||||
|
r := rect.Intersect(img.Bounds()).Sub(img.Bounds().Min)
|
||||||
|
if r.Empty() {
|
||||||
|
return &image.NRGBA{}
|
||||||
|
}
|
||||||
|
src := newScanner(img)
|
||||||
|
dst := image.NewNRGBA(image.Rect(0, 0, r.Dx(), r.Dy()))
|
||||||
|
rowSize := r.Dx() * 4
|
||||||
|
parallel(r.Min.Y, r.Max.Y, func(ys <-chan int) {
|
||||||
|
for y := range ys {
|
||||||
|
i := (y - r.Min.Y) * dst.Stride
|
||||||
|
src.scan(r.Min.X, y, r.Max.X, y+1, dst.Pix[i:i+rowSize])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// CropAnchor cuts out a rectangular region with the specified size
|
||||||
|
// from the image using the specified anchor point and returns the cropped image.
|
||||||
|
func CropAnchor(img image.Image, width, height int, anchor Anchor) *image.NRGBA {
|
||||||
|
srcBounds := img.Bounds()
|
||||||
|
pt := anchorPt(srcBounds, width, height, anchor)
|
||||||
|
r := image.Rect(0, 0, width, height).Add(pt)
|
||||||
|
b := srcBounds.Intersect(r)
|
||||||
|
return Crop(img, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CropCenter cuts out a rectangular region with the specified size
|
||||||
|
// from the center of the image and returns the cropped image.
|
||||||
|
func CropCenter(img image.Image, width, height int) *image.NRGBA {
|
||||||
|
return CropAnchor(img, width, height, Center)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Paste pastes the img image to the background image at the specified position and returns the combined image.
|
||||||
|
func Paste(background, img image.Image, pos image.Point) *image.NRGBA {
|
||||||
|
dst := Clone(background)
|
||||||
|
pos = pos.Sub(background.Bounds().Min)
|
||||||
|
pasteRect := image.Rectangle{Min: pos, Max: pos.Add(img.Bounds().Size())}
|
||||||
|
interRect := pasteRect.Intersect(dst.Bounds())
|
||||||
|
if interRect.Empty() {
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
src := newScanner(img)
|
||||||
|
parallel(interRect.Min.Y, interRect.Max.Y, func(ys <-chan int) {
|
||||||
|
for y := range ys {
|
||||||
|
x1 := interRect.Min.X - pasteRect.Min.X
|
||||||
|
x2 := interRect.Max.X - pasteRect.Min.X
|
||||||
|
y1 := y - pasteRect.Min.Y
|
||||||
|
y2 := y1 + 1
|
||||||
|
i1 := y*dst.Stride + interRect.Min.X*4
|
||||||
|
i2 := i1 + interRect.Dx()*4
|
||||||
|
src.scan(x1, y1, x2, y2, dst.Pix[i1:i2])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// PasteCenter pastes the img image to the center of the background image and returns the combined image.
|
||||||
|
func PasteCenter(background, img image.Image) *image.NRGBA {
|
||||||
|
bgBounds := background.Bounds()
|
||||||
|
bgW := bgBounds.Dx()
|
||||||
|
bgH := bgBounds.Dy()
|
||||||
|
bgMinX := bgBounds.Min.X
|
||||||
|
bgMinY := bgBounds.Min.Y
|
||||||
|
|
||||||
|
centerX := bgMinX + bgW/2
|
||||||
|
centerY := bgMinY + bgH/2
|
||||||
|
|
||||||
|
x0 := centerX - img.Bounds().Dx()/2
|
||||||
|
y0 := centerY - img.Bounds().Dy()/2
|
||||||
|
|
||||||
|
return Paste(background, img, image.Pt(x0, y0))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overlay draws the img image over the background image at given position
|
||||||
|
// and returns the combined image. Opacity parameter is the opacity of the img
|
||||||
|
// image layer, used to compose the images, it must be from 0.0 to 1.0.
|
||||||
|
//
|
||||||
|
// Examples:
|
||||||
|
//
|
||||||
|
// // Draw spriteImage over backgroundImage at the given position (x=50, y=50).
|
||||||
|
// dstImage := imaging.Overlay(backgroundImage, spriteImage, image.Pt(50, 50), 1.0)
|
||||||
|
//
|
||||||
|
// // Blend two opaque images of the same size.
|
||||||
|
// dstImage := imaging.Overlay(imageOne, imageTwo, image.Pt(0, 0), 0.5)
|
||||||
|
//
|
||||||
|
func Overlay(background, img image.Image, pos image.Point, opacity float64) *image.NRGBA {
|
||||||
|
opacity = math.Min(math.Max(opacity, 0.0), 1.0) // Ensure 0.0 <= opacity <= 1.0.
|
||||||
|
dst := Clone(background)
|
||||||
|
pos = pos.Sub(background.Bounds().Min)
|
||||||
|
pasteRect := image.Rectangle{Min: pos, Max: pos.Add(img.Bounds().Size())}
|
||||||
|
interRect := pasteRect.Intersect(dst.Bounds())
|
||||||
|
if interRect.Empty() {
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
src := newScanner(img)
|
||||||
|
parallel(interRect.Min.Y, interRect.Max.Y, func(ys <-chan int) {
|
||||||
|
scanLine := make([]uint8, interRect.Dx()*4)
|
||||||
|
for y := range ys {
|
||||||
|
x1 := interRect.Min.X - pasteRect.Min.X
|
||||||
|
x2 := interRect.Max.X - pasteRect.Min.X
|
||||||
|
y1 := y - pasteRect.Min.Y
|
||||||
|
y2 := y1 + 1
|
||||||
|
src.scan(x1, y1, x2, y2, scanLine)
|
||||||
|
i := y*dst.Stride + interRect.Min.X*4
|
||||||
|
j := 0
|
||||||
|
for x := interRect.Min.X; x < interRect.Max.X; x++ {
|
||||||
|
d := dst.Pix[i : i+4 : i+4]
|
||||||
|
r1 := float64(d[0])
|
||||||
|
g1 := float64(d[1])
|
||||||
|
b1 := float64(d[2])
|
||||||
|
a1 := float64(d[3])
|
||||||
|
|
||||||
|
s := scanLine[j : j+4 : j+4]
|
||||||
|
r2 := float64(s[0])
|
||||||
|
g2 := float64(s[1])
|
||||||
|
b2 := float64(s[2])
|
||||||
|
a2 := float64(s[3])
|
||||||
|
|
||||||
|
coef2 := opacity * a2 / 255
|
||||||
|
coef1 := (1 - coef2) * a1 / 255
|
||||||
|
coefSum := coef1 + coef2
|
||||||
|
coef1 /= coefSum
|
||||||
|
coef2 /= coefSum
|
||||||
|
|
||||||
|
d[0] = uint8(r1*coef1 + r2*coef2)
|
||||||
|
d[1] = uint8(g1*coef1 + g2*coef2)
|
||||||
|
d[2] = uint8(b1*coef1 + b2*coef2)
|
||||||
|
d[3] = uint8(math.Min(a1+a2*opacity*(255-a1)/255, 255))
|
||||||
|
|
||||||
|
i += 4
|
||||||
|
j += 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// OverlayCenter overlays the img image to the center of the background image and
|
||||||
|
// returns the combined image. Opacity parameter is the opacity of the img
|
||||||
|
// image layer, used to compose the images, it must be from 0.0 to 1.0.
|
||||||
|
func OverlayCenter(background, img image.Image, opacity float64) *image.NRGBA {
|
||||||
|
bgBounds := background.Bounds()
|
||||||
|
bgW := bgBounds.Dx()
|
||||||
|
bgH := bgBounds.Dy()
|
||||||
|
bgMinX := bgBounds.Min.X
|
||||||
|
bgMinY := bgBounds.Min.Y
|
||||||
|
|
||||||
|
centerX := bgMinX + bgW/2
|
||||||
|
centerY := bgMinY + bgH/2
|
||||||
|
|
||||||
|
x0 := centerX - img.Bounds().Dx()/2
|
||||||
|
y0 := centerY - img.Bounds().Dy()/2
|
||||||
|
|
||||||
|
return Overlay(background, img, image.Point{x0, y0}, opacity)
|
||||||
|
}
|
||||||
268
vendor/github.com/disintegration/imaging/transform.go
generated
vendored
Normal file
268
vendor/github.com/disintegration/imaging/transform.go
generated
vendored
Normal file
@@ -0,0 +1,268 @@
|
|||||||
|
package imaging
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FlipH flips the image horizontally (from left to right) and returns the transformed image.
|
||||||
|
func FlipH(img image.Image) *image.NRGBA {
|
||||||
|
src := newScanner(img)
|
||||||
|
dstW := src.w
|
||||||
|
dstH := src.h
|
||||||
|
rowSize := dstW * 4
|
||||||
|
dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
|
||||||
|
parallel(0, dstH, func(ys <-chan int) {
|
||||||
|
for dstY := range ys {
|
||||||
|
i := dstY * dst.Stride
|
||||||
|
srcY := dstY
|
||||||
|
src.scan(0, srcY, src.w, srcY+1, dst.Pix[i:i+rowSize])
|
||||||
|
reverse(dst.Pix[i : i+rowSize])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlipV flips the image vertically (from top to bottom) and returns the transformed image.
|
||||||
|
func FlipV(img image.Image) *image.NRGBA {
|
||||||
|
src := newScanner(img)
|
||||||
|
dstW := src.w
|
||||||
|
dstH := src.h
|
||||||
|
rowSize := dstW * 4
|
||||||
|
dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
|
||||||
|
parallel(0, dstH, func(ys <-chan int) {
|
||||||
|
for dstY := range ys {
|
||||||
|
i := dstY * dst.Stride
|
||||||
|
srcY := dstH - dstY - 1
|
||||||
|
src.scan(0, srcY, src.w, srcY+1, dst.Pix[i:i+rowSize])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transpose flips the image horizontally and rotates 90 degrees counter-clockwise.
|
||||||
|
func Transpose(img image.Image) *image.NRGBA {
|
||||||
|
src := newScanner(img)
|
||||||
|
dstW := src.h
|
||||||
|
dstH := src.w
|
||||||
|
rowSize := dstW * 4
|
||||||
|
dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
|
||||||
|
parallel(0, dstH, func(ys <-chan int) {
|
||||||
|
for dstY := range ys {
|
||||||
|
i := dstY * dst.Stride
|
||||||
|
srcX := dstY
|
||||||
|
src.scan(srcX, 0, srcX+1, src.h, dst.Pix[i:i+rowSize])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transverse flips the image vertically and rotates 90 degrees counter-clockwise.
|
||||||
|
func Transverse(img image.Image) *image.NRGBA {
|
||||||
|
src := newScanner(img)
|
||||||
|
dstW := src.h
|
||||||
|
dstH := src.w
|
||||||
|
rowSize := dstW * 4
|
||||||
|
dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
|
||||||
|
parallel(0, dstH, func(ys <-chan int) {
|
||||||
|
for dstY := range ys {
|
||||||
|
i := dstY * dst.Stride
|
||||||
|
srcX := dstH - dstY - 1
|
||||||
|
src.scan(srcX, 0, srcX+1, src.h, dst.Pix[i:i+rowSize])
|
||||||
|
reverse(dst.Pix[i : i+rowSize])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rotate90 rotates the image 90 degrees counter-clockwise and returns the transformed image.
|
||||||
|
func Rotate90(img image.Image) *image.NRGBA {
|
||||||
|
src := newScanner(img)
|
||||||
|
dstW := src.h
|
||||||
|
dstH := src.w
|
||||||
|
rowSize := dstW * 4
|
||||||
|
dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
|
||||||
|
parallel(0, dstH, func(ys <-chan int) {
|
||||||
|
for dstY := range ys {
|
||||||
|
i := dstY * dst.Stride
|
||||||
|
srcX := dstH - dstY - 1
|
||||||
|
src.scan(srcX, 0, srcX+1, src.h, dst.Pix[i:i+rowSize])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rotate180 rotates the image 180 degrees counter-clockwise and returns the transformed image.
|
||||||
|
func Rotate180(img image.Image) *image.NRGBA {
|
||||||
|
src := newScanner(img)
|
||||||
|
dstW := src.w
|
||||||
|
dstH := src.h
|
||||||
|
rowSize := dstW * 4
|
||||||
|
dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
|
||||||
|
parallel(0, dstH, func(ys <-chan int) {
|
||||||
|
for dstY := range ys {
|
||||||
|
i := dstY * dst.Stride
|
||||||
|
srcY := dstH - dstY - 1
|
||||||
|
src.scan(0, srcY, src.w, srcY+1, dst.Pix[i:i+rowSize])
|
||||||
|
reverse(dst.Pix[i : i+rowSize])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rotate270 rotates the image 270 degrees counter-clockwise and returns the transformed image.
|
||||||
|
func Rotate270(img image.Image) *image.NRGBA {
|
||||||
|
src := newScanner(img)
|
||||||
|
dstW := src.h
|
||||||
|
dstH := src.w
|
||||||
|
rowSize := dstW * 4
|
||||||
|
dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
|
||||||
|
parallel(0, dstH, func(ys <-chan int) {
|
||||||
|
for dstY := range ys {
|
||||||
|
i := dstY * dst.Stride
|
||||||
|
srcX := dstY
|
||||||
|
src.scan(srcX, 0, srcX+1, src.h, dst.Pix[i:i+rowSize])
|
||||||
|
reverse(dst.Pix[i : i+rowSize])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rotate rotates an image by the given angle counter-clockwise .
|
||||||
|
// The angle parameter is the rotation angle in degrees.
|
||||||
|
// The bgColor parameter specifies the color of the uncovered zone after the rotation.
|
||||||
|
func Rotate(img image.Image, angle float64, bgColor color.Color) *image.NRGBA {
|
||||||
|
angle = angle - math.Floor(angle/360)*360
|
||||||
|
|
||||||
|
switch angle {
|
||||||
|
case 0:
|
||||||
|
return Clone(img)
|
||||||
|
case 90:
|
||||||
|
return Rotate90(img)
|
||||||
|
case 180:
|
||||||
|
return Rotate180(img)
|
||||||
|
case 270:
|
||||||
|
return Rotate270(img)
|
||||||
|
}
|
||||||
|
|
||||||
|
src := toNRGBA(img)
|
||||||
|
srcW := src.Bounds().Max.X
|
||||||
|
srcH := src.Bounds().Max.Y
|
||||||
|
dstW, dstH := rotatedSize(srcW, srcH, angle)
|
||||||
|
dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
|
||||||
|
|
||||||
|
if dstW <= 0 || dstH <= 0 {
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
srcXOff := float64(srcW)/2 - 0.5
|
||||||
|
srcYOff := float64(srcH)/2 - 0.5
|
||||||
|
dstXOff := float64(dstW)/2 - 0.5
|
||||||
|
dstYOff := float64(dstH)/2 - 0.5
|
||||||
|
|
||||||
|
bgColorNRGBA := color.NRGBAModel.Convert(bgColor).(color.NRGBA)
|
||||||
|
sin, cos := math.Sincos(math.Pi * angle / 180)
|
||||||
|
|
||||||
|
parallel(0, dstH, func(ys <-chan int) {
|
||||||
|
for dstY := range ys {
|
||||||
|
for dstX := 0; dstX < dstW; dstX++ {
|
||||||
|
xf, yf := rotatePoint(float64(dstX)-dstXOff, float64(dstY)-dstYOff, sin, cos)
|
||||||
|
xf, yf = xf+srcXOff, yf+srcYOff
|
||||||
|
interpolatePoint(dst, dstX, dstY, src, xf, yf, bgColorNRGBA)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
func rotatePoint(x, y, sin, cos float64) (float64, float64) {
|
||||||
|
return x*cos - y*sin, x*sin + y*cos
|
||||||
|
}
|
||||||
|
|
||||||
|
func rotatedSize(w, h int, angle float64) (int, int) {
|
||||||
|
if w <= 0 || h <= 0 {
|
||||||
|
return 0, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
sin, cos := math.Sincos(math.Pi * angle / 180)
|
||||||
|
x1, y1 := rotatePoint(float64(w-1), 0, sin, cos)
|
||||||
|
x2, y2 := rotatePoint(float64(w-1), float64(h-1), sin, cos)
|
||||||
|
x3, y3 := rotatePoint(0, float64(h-1), sin, cos)
|
||||||
|
|
||||||
|
minx := math.Min(x1, math.Min(x2, math.Min(x3, 0)))
|
||||||
|
maxx := math.Max(x1, math.Max(x2, math.Max(x3, 0)))
|
||||||
|
miny := math.Min(y1, math.Min(y2, math.Min(y3, 0)))
|
||||||
|
maxy := math.Max(y1, math.Max(y2, math.Max(y3, 0)))
|
||||||
|
|
||||||
|
neww := maxx - minx + 1
|
||||||
|
if neww-math.Floor(neww) > 0.1 {
|
||||||
|
neww++
|
||||||
|
}
|
||||||
|
newh := maxy - miny + 1
|
||||||
|
if newh-math.Floor(newh) > 0.1 {
|
||||||
|
newh++
|
||||||
|
}
|
||||||
|
|
||||||
|
return int(neww), int(newh)
|
||||||
|
}
|
||||||
|
|
||||||
|
func interpolatePoint(dst *image.NRGBA, dstX, dstY int, src *image.NRGBA, xf, yf float64, bgColor color.NRGBA) {
|
||||||
|
j := dstY*dst.Stride + dstX*4
|
||||||
|
d := dst.Pix[j : j+4 : j+4]
|
||||||
|
|
||||||
|
x0 := int(math.Floor(xf))
|
||||||
|
y0 := int(math.Floor(yf))
|
||||||
|
bounds := src.Bounds()
|
||||||
|
if !image.Pt(x0, y0).In(image.Rect(bounds.Min.X-1, bounds.Min.Y-1, bounds.Max.X, bounds.Max.Y)) {
|
||||||
|
d[0] = bgColor.R
|
||||||
|
d[1] = bgColor.G
|
||||||
|
d[2] = bgColor.B
|
||||||
|
d[3] = bgColor.A
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
xq := xf - float64(x0)
|
||||||
|
yq := yf - float64(y0)
|
||||||
|
points := [4]image.Point{
|
||||||
|
{x0, y0},
|
||||||
|
{x0 + 1, y0},
|
||||||
|
{x0, y0 + 1},
|
||||||
|
{x0 + 1, y0 + 1},
|
||||||
|
}
|
||||||
|
weights := [4]float64{
|
||||||
|
(1 - xq) * (1 - yq),
|
||||||
|
xq * (1 - yq),
|
||||||
|
(1 - xq) * yq,
|
||||||
|
xq * yq,
|
||||||
|
}
|
||||||
|
|
||||||
|
var r, g, b, a float64
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
p := points[i]
|
||||||
|
w := weights[i]
|
||||||
|
if p.In(bounds) {
|
||||||
|
i := p.Y*src.Stride + p.X*4
|
||||||
|
s := src.Pix[i : i+4 : i+4]
|
||||||
|
wa := float64(s[3]) * w
|
||||||
|
r += float64(s[0]) * wa
|
||||||
|
g += float64(s[1]) * wa
|
||||||
|
b += float64(s[2]) * wa
|
||||||
|
a += wa
|
||||||
|
} else {
|
||||||
|
wa := float64(bgColor.A) * w
|
||||||
|
r += float64(bgColor.R) * wa
|
||||||
|
g += float64(bgColor.G) * wa
|
||||||
|
b += float64(bgColor.B) * wa
|
||||||
|
a += wa
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if a != 0 {
|
||||||
|
aInv := 1 / a
|
||||||
|
d[0] = clamp(r * aInv)
|
||||||
|
d[1] = clamp(g * aInv)
|
||||||
|
d[2] = clamp(b * aInv)
|
||||||
|
d[3] = clamp(a)
|
||||||
|
}
|
||||||
|
}
|
||||||
167
vendor/github.com/disintegration/imaging/utils.go
generated
vendored
Normal file
167
vendor/github.com/disintegration/imaging/utils.go
generated
vendored
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
package imaging
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"math"
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// parallel processes the data in separate goroutines.
|
||||||
|
func parallel(start, stop int, fn func(<-chan int)) {
|
||||||
|
count := stop - start
|
||||||
|
if count < 1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
procs := runtime.GOMAXPROCS(0)
|
||||||
|
if procs > count {
|
||||||
|
procs = count
|
||||||
|
}
|
||||||
|
|
||||||
|
c := make(chan int, count)
|
||||||
|
for i := start; i < stop; i++ {
|
||||||
|
c <- i
|
||||||
|
}
|
||||||
|
close(c)
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
for i := 0; i < procs; i++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
fn(c)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
// absint returns the absolute value of i.
|
||||||
|
func absint(i int) int {
|
||||||
|
if i < 0 {
|
||||||
|
return -i
|
||||||
|
}
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
// clamp rounds and clamps float64 value to fit into uint8.
|
||||||
|
func clamp(x float64) uint8 {
|
||||||
|
v := int64(x + 0.5)
|
||||||
|
if v > 255 {
|
||||||
|
return 255
|
||||||
|
}
|
||||||
|
if v > 0 {
|
||||||
|
return uint8(v)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func reverse(pix []uint8) {
|
||||||
|
if len(pix) <= 4 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
i := 0
|
||||||
|
j := len(pix) - 4
|
||||||
|
for i < j {
|
||||||
|
pi := pix[i : i+4 : i+4]
|
||||||
|
pj := pix[j : j+4 : j+4]
|
||||||
|
pi[0], pj[0] = pj[0], pi[0]
|
||||||
|
pi[1], pj[1] = pj[1], pi[1]
|
||||||
|
pi[2], pj[2] = pj[2], pi[2]
|
||||||
|
pi[3], pj[3] = pj[3], pi[3]
|
||||||
|
i += 4
|
||||||
|
j -= 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func toNRGBA(img image.Image) *image.NRGBA {
|
||||||
|
if img, ok := img.(*image.NRGBA); ok {
|
||||||
|
return &image.NRGBA{
|
||||||
|
Pix: img.Pix,
|
||||||
|
Stride: img.Stride,
|
||||||
|
Rect: img.Rect.Sub(img.Rect.Min),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Clone(img)
|
||||||
|
}
|
||||||
|
|
||||||
|
// rgbToHSL converts a color from RGB to HSL.
|
||||||
|
func rgbToHSL(r, g, b uint8) (float64, float64, float64) {
|
||||||
|
rr := float64(r) / 255
|
||||||
|
gg := float64(g) / 255
|
||||||
|
bb := float64(b) / 255
|
||||||
|
|
||||||
|
max := math.Max(rr, math.Max(gg, bb))
|
||||||
|
min := math.Min(rr, math.Min(gg, bb))
|
||||||
|
|
||||||
|
l := (max + min) / 2
|
||||||
|
|
||||||
|
if max == min {
|
||||||
|
return 0, 0, l
|
||||||
|
}
|
||||||
|
|
||||||
|
var h, s float64
|
||||||
|
d := max - min
|
||||||
|
if l > 0.5 {
|
||||||
|
s = d / (2 - max - min)
|
||||||
|
} else {
|
||||||
|
s = d / (max + min)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch max {
|
||||||
|
case rr:
|
||||||
|
h = (gg - bb) / d
|
||||||
|
if g < b {
|
||||||
|
h += 6
|
||||||
|
}
|
||||||
|
case gg:
|
||||||
|
h = (bb-rr)/d + 2
|
||||||
|
case bb:
|
||||||
|
h = (rr-gg)/d + 4
|
||||||
|
}
|
||||||
|
h /= 6
|
||||||
|
|
||||||
|
return h, s, l
|
||||||
|
}
|
||||||
|
|
||||||
|
// hslToRGB converts a color from HSL to RGB.
|
||||||
|
func hslToRGB(h, s, l float64) (uint8, uint8, uint8) {
|
||||||
|
var r, g, b float64
|
||||||
|
if s == 0 {
|
||||||
|
v := clamp(l * 255)
|
||||||
|
return v, v, v
|
||||||
|
}
|
||||||
|
|
||||||
|
var q float64
|
||||||
|
if l < 0.5 {
|
||||||
|
q = l * (1 + s)
|
||||||
|
} else {
|
||||||
|
q = l + s - l*s
|
||||||
|
}
|
||||||
|
p := 2*l - q
|
||||||
|
|
||||||
|
r = hueToRGB(p, q, h+1/3.0)
|
||||||
|
g = hueToRGB(p, q, h)
|
||||||
|
b = hueToRGB(p, q, h-1/3.0)
|
||||||
|
|
||||||
|
return clamp(r * 255), clamp(g * 255), clamp(b * 255)
|
||||||
|
}
|
||||||
|
|
||||||
|
func hueToRGB(p, q, t float64) float64 {
|
||||||
|
if t < 0 {
|
||||||
|
t++
|
||||||
|
}
|
||||||
|
if t > 1 {
|
||||||
|
t--
|
||||||
|
}
|
||||||
|
if t < 1/6.0 {
|
||||||
|
return p + (q-p)*6*t
|
||||||
|
}
|
||||||
|
if t < 1/2.0 {
|
||||||
|
return q
|
||||||
|
}
|
||||||
|
if t < 2/3.0 {
|
||||||
|
return p + (q-p)*(2/3.0-t)*6
|
||||||
|
}
|
||||||
|
return p
|
||||||
|
}
|
||||||
29
vendor/github.com/go-logr/logr/.golangci.yaml
generated
vendored
Normal file
29
vendor/github.com/go-logr/logr/.golangci.yaml
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
run:
|
||||||
|
timeout: 1m
|
||||||
|
tests: true
|
||||||
|
|
||||||
|
linters:
|
||||||
|
disable-all: true
|
||||||
|
enable:
|
||||||
|
- asciicheck
|
||||||
|
- deadcode
|
||||||
|
- errcheck
|
||||||
|
- forcetypeassert
|
||||||
|
- gocritic
|
||||||
|
- gofmt
|
||||||
|
- goimports
|
||||||
|
- gosimple
|
||||||
|
- govet
|
||||||
|
- ineffassign
|
||||||
|
- misspell
|
||||||
|
- revive
|
||||||
|
- staticcheck
|
||||||
|
- structcheck
|
||||||
|
- typecheck
|
||||||
|
- unused
|
||||||
|
- varcheck
|
||||||
|
|
||||||
|
issues:
|
||||||
|
exclude-use-default: false
|
||||||
|
max-issues-per-linter: 0
|
||||||
|
max-same-issues: 10
|
||||||
6
vendor/github.com/go-logr/logr/CHANGELOG.md
generated
vendored
Normal file
6
vendor/github.com/go-logr/logr/CHANGELOG.md
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# CHANGELOG
|
||||||
|
|
||||||
|
## v1.0.0-rc1
|
||||||
|
|
||||||
|
This is the first logged release. Major changes (including breaking changes)
|
||||||
|
have occurred since earlier tags.
|
||||||
17
vendor/github.com/go-logr/logr/CONTRIBUTING.md
generated
vendored
Normal file
17
vendor/github.com/go-logr/logr/CONTRIBUTING.md
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# Contributing
|
||||||
|
|
||||||
|
Logr is open to pull-requests, provided they fit within the intended scope of
|
||||||
|
the project. Specifically, this library aims to be VERY small and minimalist,
|
||||||
|
with no external dependencies.
|
||||||
|
|
||||||
|
## Compatibility
|
||||||
|
|
||||||
|
This project intends to follow [semantic versioning](http://semver.org) and
|
||||||
|
is very strict about compatibility. Any proposed changes MUST follow those
|
||||||
|
rules.
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
|
||||||
|
As a logging library, logr must be as light-weight as possible. Any proposed
|
||||||
|
code change must include results of running the [benchmark](./benchmark)
|
||||||
|
before and after the change.
|
||||||
201
vendor/github.com/go-logr/logr/LICENSE
generated
vendored
Normal file
201
vendor/github.com/go-logr/logr/LICENSE
generated
vendored
Normal file
@@ -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.
|
||||||
278
vendor/github.com/go-logr/logr/README.md
generated
vendored
Normal file
278
vendor/github.com/go-logr/logr/README.md
generated
vendored
Normal file
@@ -0,0 +1,278 @@
|
|||||||
|
# A minimal logging API for Go
|
||||||
|
|
||||||
|
[](https://pkg.go.dev/github.com/go-logr/logr)
|
||||||
|
|
||||||
|
logr offers an(other) opinion on how Go programs and libraries can do logging
|
||||||
|
without becoming coupled to a particular logging implementation. This is not
|
||||||
|
an implementation of logging - it is an API. In fact it is two APIs with two
|
||||||
|
different sets of users.
|
||||||
|
|
||||||
|
The `Logger` type is intended for application and library authors. It provides
|
||||||
|
a relatively small API which can be used everywhere you want to emit logs. It
|
||||||
|
defers the actual act of writing logs (to files, to stdout, or whatever) to the
|
||||||
|
`LogSink` interface.
|
||||||
|
|
||||||
|
The `LogSink` interface is intended for logging library implementers. It is a
|
||||||
|
pure interface which can be implemented by logging frameworks to provide the actual logging
|
||||||
|
functionality.
|
||||||
|
|
||||||
|
This decoupling allows application and library developers to write code in
|
||||||
|
terms of `logr.Logger` (which has very low dependency fan-out) while the
|
||||||
|
implementation of logging is managed "up stack" (e.g. in or near `main()`.)
|
||||||
|
Application developers can then switch out implementations as necessary.
|
||||||
|
|
||||||
|
Many people assert that libraries should not be logging, and as such efforts
|
||||||
|
like this are pointless. Those people are welcome to convince the authors of
|
||||||
|
the tens-of-thousands of libraries that *DO* write logs that they are all
|
||||||
|
wrong. In the meantime, logr takes a more practical approach.
|
||||||
|
|
||||||
|
## Typical usage
|
||||||
|
|
||||||
|
Somewhere, early in an application's life, it will make a decision about which
|
||||||
|
logging library (implementation) it actually wants to use. Something like:
|
||||||
|
|
||||||
|
```
|
||||||
|
func main() {
|
||||||
|
// ... other setup code ...
|
||||||
|
|
||||||
|
// Create the "root" logger. We have chosen the "logimpl" implementation,
|
||||||
|
// which takes some initial parameters and returns a logr.Logger.
|
||||||
|
logger := logimpl.New(param1, param2)
|
||||||
|
|
||||||
|
// ... other setup code ...
|
||||||
|
```
|
||||||
|
|
||||||
|
Most apps will call into other libraries, create structures to govern the flow,
|
||||||
|
etc. The `logr.Logger` object can be passed to these other libraries, stored
|
||||||
|
in structs, or even used as a package-global variable, if needed. For example:
|
||||||
|
|
||||||
|
```
|
||||||
|
app := createTheAppObject(logger)
|
||||||
|
app.Run()
|
||||||
|
```
|
||||||
|
|
||||||
|
Outside of this early setup, no other packages need to know about the choice of
|
||||||
|
implementation. They write logs in terms of the `logr.Logger` that they
|
||||||
|
received:
|
||||||
|
|
||||||
|
```
|
||||||
|
type appObject struct {
|
||||||
|
// ... other fields ...
|
||||||
|
logger logr.Logger
|
||||||
|
// ... other fields ...
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *appObject) Run() {
|
||||||
|
app.logger.Info("starting up", "timestamp", time.Now())
|
||||||
|
|
||||||
|
// ... app code ...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Background
|
||||||
|
|
||||||
|
If the Go standard library had defined an interface for logging, this project
|
||||||
|
probably would not be needed. Alas, here we are.
|
||||||
|
|
||||||
|
### Inspiration
|
||||||
|
|
||||||
|
Before you consider this package, please read [this blog post by the
|
||||||
|
inimitable Dave Cheney][warning-makes-no-sense]. We really appreciate what
|
||||||
|
he has to say, and it largely aligns with our own experiences.
|
||||||
|
|
||||||
|
### Differences from Dave's ideas
|
||||||
|
|
||||||
|
The main differences are:
|
||||||
|
|
||||||
|
1. Dave basically proposes doing away with the notion of a logging API in favor
|
||||||
|
of `fmt.Printf()`. We disagree, especially when you consider things like output
|
||||||
|
locations, timestamps, file and line decorations, and structured logging. This
|
||||||
|
package restricts the logging API to just 2 types of logs: info and error.
|
||||||
|
|
||||||
|
Info logs are things you want to tell the user which are not errors. Error
|
||||||
|
logs are, well, errors. If your code receives an `error` from a subordinate
|
||||||
|
function call and is logging that `error` *and not returning it*, use error
|
||||||
|
logs.
|
||||||
|
|
||||||
|
2. Verbosity-levels on info logs. This gives developers a chance to indicate
|
||||||
|
arbitrary grades of importance for info logs, without assigning names with
|
||||||
|
semantic meaning such as "warning", "trace", and "debug." Superficially this
|
||||||
|
may feel very similar, but the primary difference is the lack of semantics.
|
||||||
|
Because verbosity is a numerical value, it's safe to assume that an app running
|
||||||
|
with higher verbosity means more (and less important) logs will be generated.
|
||||||
|
|
||||||
|
## Implementations (non-exhaustive)
|
||||||
|
|
||||||
|
There are implementations for the following logging libraries:
|
||||||
|
|
||||||
|
- **a function** (can bridge to non-structured libraries): [funcr](https://github.com/go-logr/logr/tree/master/funcr)
|
||||||
|
- **github.com/google/glog**: [glogr](https://github.com/go-logr/glogr)
|
||||||
|
- **k8s.io/klog** (for Kubernetes): [klogr](https://git.k8s.io/klog/klogr)
|
||||||
|
- **go.uber.org/zap**: [zapr](https://github.com/go-logr/zapr)
|
||||||
|
- **log** (the Go standard library logger): [stdr](https://github.com/go-logr/stdr)
|
||||||
|
- **github.com/sirupsen/logrus**: [logrusr](https://github.com/bombsimon/logrusr)
|
||||||
|
- **github.com/wojas/genericr**: [genericr](https://github.com/wojas/genericr) (makes it easy to implement your own backend)
|
||||||
|
- **logfmt** (Heroku style [logging](https://www.brandur.org/logfmt)): [logfmtr](https://github.com/iand/logfmtr)
|
||||||
|
- **github.com/rs/zerolog**: [zerologr](https://github.com/go-logr/zerologr)
|
||||||
|
|
||||||
|
## FAQ
|
||||||
|
|
||||||
|
### Conceptual
|
||||||
|
|
||||||
|
#### Why structured logging?
|
||||||
|
|
||||||
|
- **Structured logs are more easily queryable**: Since you've got
|
||||||
|
key-value pairs, it's much easier to query your structured logs for
|
||||||
|
particular values by filtering on the contents of a particular key --
|
||||||
|
think searching request logs for error codes, Kubernetes reconcilers for
|
||||||
|
the name and namespace of the reconciled object, etc.
|
||||||
|
|
||||||
|
- **Structured logging makes it easier to have cross-referenceable logs**:
|
||||||
|
Similarly to searchability, if you maintain conventions around your
|
||||||
|
keys, it becomes easy to gather all log lines related to a particular
|
||||||
|
concept.
|
||||||
|
|
||||||
|
- **Structured logs allow better dimensions of filtering**: if you have
|
||||||
|
structure to your logs, you've got more precise control over how much
|
||||||
|
information is logged -- you might choose in a particular configuration
|
||||||
|
to log certain keys but not others, only log lines where a certain key
|
||||||
|
matches a certain value, etc., instead of just having v-levels and names
|
||||||
|
to key off of.
|
||||||
|
|
||||||
|
- **Structured logs better represent structured data**: sometimes, the
|
||||||
|
data that you want to log is inherently structured (think tuple-link
|
||||||
|
objects.) Structured logs allow you to preserve that structure when
|
||||||
|
outputting.
|
||||||
|
|
||||||
|
#### Why V-levels?
|
||||||
|
|
||||||
|
**V-levels give operators an easy way to control the chattiness of log
|
||||||
|
operations**. V-levels provide a way for a given package to distinguish
|
||||||
|
the relative importance or verbosity of a given log message. Then, if
|
||||||
|
a particular logger or package is logging too many messages, the user
|
||||||
|
of the package can simply change the v-levels for that library.
|
||||||
|
|
||||||
|
#### Why not named levels, like Info/Warning/Error?
|
||||||
|
|
||||||
|
Read [Dave Cheney's post][warning-makes-no-sense]. Then read [Differences
|
||||||
|
from Dave's ideas](#differences-from-daves-ideas).
|
||||||
|
|
||||||
|
#### Why not allow format strings, too?
|
||||||
|
|
||||||
|
**Format strings negate many of the benefits of structured logs**:
|
||||||
|
|
||||||
|
- They're not easily searchable without resorting to fuzzy searching,
|
||||||
|
regular expressions, etc.
|
||||||
|
|
||||||
|
- They don't store structured data well, since contents are flattened into
|
||||||
|
a string.
|
||||||
|
|
||||||
|
- They're not cross-referenceable.
|
||||||
|
|
||||||
|
- They don't compress easily, since the message is not constant.
|
||||||
|
|
||||||
|
(Unless you turn positional parameters into key-value pairs with numerical
|
||||||
|
keys, at which point you've gotten key-value logging with meaningless
|
||||||
|
keys.)
|
||||||
|
|
||||||
|
### Practical
|
||||||
|
|
||||||
|
#### Why key-value pairs, and not a map?
|
||||||
|
|
||||||
|
Key-value pairs are *much* easier to optimize, especially around
|
||||||
|
allocations. Zap (a structured logger that inspired logr's interface) has
|
||||||
|
[performance measurements](https://github.com/uber-go/zap#performance)
|
||||||
|
that show this quite nicely.
|
||||||
|
|
||||||
|
While the interface ends up being a little less obvious, you get
|
||||||
|
potentially better performance, plus avoid making users type
|
||||||
|
`map[string]string{}` every time they want to log.
|
||||||
|
|
||||||
|
#### What if my V-levels differ between libraries?
|
||||||
|
|
||||||
|
That's fine. Control your V-levels on a per-logger basis, and use the
|
||||||
|
`WithName` method to pass different loggers to different libraries.
|
||||||
|
|
||||||
|
Generally, you should take care to ensure that you have relatively
|
||||||
|
consistent V-levels within a given logger, however, as this makes deciding
|
||||||
|
on what verbosity of logs to request easier.
|
||||||
|
|
||||||
|
#### But I really want to use a format string!
|
||||||
|
|
||||||
|
That's not actually a question. Assuming your question is "how do
|
||||||
|
I convert my mental model of logging with format strings to logging with
|
||||||
|
constant messages":
|
||||||
|
|
||||||
|
1. Figure out what the error actually is, as you'd write in a TL;DR style,
|
||||||
|
and use that as a message.
|
||||||
|
|
||||||
|
2. For every place you'd write a format specifier, look to the word before
|
||||||
|
it, and add that as a key value pair.
|
||||||
|
|
||||||
|
For instance, consider the following examples (all taken from spots in the
|
||||||
|
Kubernetes codebase):
|
||||||
|
|
||||||
|
- `klog.V(4).Infof("Client is returning errors: code %v, error %v",
|
||||||
|
responseCode, err)` becomes `logger.Error(err, "client returned an
|
||||||
|
error", "code", responseCode)`
|
||||||
|
|
||||||
|
- `klog.V(4).Infof("Got a Retry-After %ds response for attempt %d to %v",
|
||||||
|
seconds, retries, url)` becomes `logger.V(4).Info("got a retry-after
|
||||||
|
response when requesting url", "attempt", retries, "after
|
||||||
|
seconds", seconds, "url", url)`
|
||||||
|
|
||||||
|
If you *really* must use a format string, use it in a key's value, and
|
||||||
|
call `fmt.Sprintf` yourself. For instance: `log.Printf("unable to
|
||||||
|
reflect over type %T")` becomes `logger.Info("unable to reflect over
|
||||||
|
type", "type", fmt.Sprintf("%T"))`. In general though, the cases where
|
||||||
|
this is necessary should be few and far between.
|
||||||
|
|
||||||
|
#### How do I choose my V-levels?
|
||||||
|
|
||||||
|
This is basically the only hard constraint: increase V-levels to denote
|
||||||
|
more verbose or more debug-y logs.
|
||||||
|
|
||||||
|
Otherwise, you can start out with `0` as "you always want to see this",
|
||||||
|
`1` as "common logging that you might *possibly* want to turn off", and
|
||||||
|
`10` as "I would like to performance-test your log collection stack."
|
||||||
|
|
||||||
|
Then gradually choose levels in between as you need them, working your way
|
||||||
|
down from 10 (for debug and trace style logs) and up from 1 (for chattier
|
||||||
|
info-type logs.)
|
||||||
|
|
||||||
|
#### How do I choose my keys?
|
||||||
|
|
||||||
|
Keys are fairly flexible, and can hold more or less any string
|
||||||
|
value. For best compatibility with implementations and consistency
|
||||||
|
with existing code in other projects, there are a few conventions you
|
||||||
|
should consider.
|
||||||
|
|
||||||
|
- Make your keys human-readable.
|
||||||
|
- Constant keys are generally a good idea.
|
||||||
|
- Be consistent across your codebase.
|
||||||
|
- Keys should naturally match parts of the message string.
|
||||||
|
- Use lower case for simple keys and
|
||||||
|
[lowerCamelCase](https://en.wiktionary.org/wiki/lowerCamelCase) for
|
||||||
|
more complex ones. Kubernetes is one example of a project that has
|
||||||
|
[adopted that
|
||||||
|
convention](https://github.com/kubernetes/community/blob/HEAD/contributors/devel/sig-instrumentation/migration-to-structured-logging.md#name-arguments).
|
||||||
|
|
||||||
|
While key names are mostly unrestricted (and spaces are acceptable),
|
||||||
|
it's generally a good idea to stick to printable ascii characters, or at
|
||||||
|
least match the general character set of your log lines.
|
||||||
|
|
||||||
|
#### Why should keys be constant values?
|
||||||
|
|
||||||
|
The point of structured logging is to make later log processing easier. Your
|
||||||
|
keys are, effectively, the schema of each log message. If you use different
|
||||||
|
keys across instances of the same log line, you will make your structured logs
|
||||||
|
much harder to use. `Sprintf()` is for values, not for keys!
|
||||||
|
|
||||||
|
#### Why is this not a pure interface?
|
||||||
|
|
||||||
|
The Logger type is implemented as a struct in order to allow the Go compiler to
|
||||||
|
optimize things like high-V `Info` logs that are not triggered. Not all of
|
||||||
|
these implementations are implemented yet, but this structure was suggested as
|
||||||
|
a way to ensure they *can* be implemented. All of the real work is behind the
|
||||||
|
`LogSink` interface.
|
||||||
|
|
||||||
|
[warning-makes-no-sense]: http://dave.cheney.net/2015/11/05/lets-talk-about-logging
|
||||||
54
vendor/github.com/go-logr/logr/discard.go
generated
vendored
Normal file
54
vendor/github.com/go-logr/logr/discard.go
generated
vendored
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2020 The logr 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 logr
|
||||||
|
|
||||||
|
// Discard returns a Logger that discards all messages logged to it. It can be
|
||||||
|
// used whenever the caller is not interested in the logs. Logger instances
|
||||||
|
// produced by this function always compare as equal.
|
||||||
|
func Discard() Logger {
|
||||||
|
return Logger{
|
||||||
|
level: 0,
|
||||||
|
sink: discardLogSink{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// discardLogSink is a LogSink that discards all messages.
|
||||||
|
type discardLogSink struct{}
|
||||||
|
|
||||||
|
// Verify that it actually implements the interface
|
||||||
|
var _ LogSink = discardLogSink{}
|
||||||
|
|
||||||
|
func (l discardLogSink) Init(RuntimeInfo) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l discardLogSink) Enabled(int) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l discardLogSink) Info(int, string, ...interface{}) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l discardLogSink) Error(error, string, ...interface{}) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l discardLogSink) WithValues(...interface{}) LogSink {
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l discardLogSink) WithName(string) LogSink {
|
||||||
|
return l
|
||||||
|
}
|
||||||
742
vendor/github.com/go-logr/logr/funcr/funcr.go
generated
vendored
Normal file
742
vendor/github.com/go-logr/logr/funcr/funcr.go
generated
vendored
Normal file
@@ -0,0 +1,742 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2021 The logr 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 funcr implements formatting of structured log messages and
|
||||||
|
// optionally captures the call site and timestamp.
|
||||||
|
//
|
||||||
|
// The simplest way to use it is via its implementation of a
|
||||||
|
// github.com/go-logr/logr.LogSink with output through an arbitrary
|
||||||
|
// "write" function. See New and NewJSON for details.
|
||||||
|
//
|
||||||
|
// Custom LogSinks
|
||||||
|
//
|
||||||
|
// For users who need more control, a funcr.Formatter can be embedded inside
|
||||||
|
// your own custom LogSink implementation. This is useful when the LogSink
|
||||||
|
// needs to implement additional methods, for example.
|
||||||
|
//
|
||||||
|
// Formatting
|
||||||
|
//
|
||||||
|
// This will respect logr.Marshaler, fmt.Stringer, and error interfaces for
|
||||||
|
// values which are being logged. When rendering a struct, funcr will use Go's
|
||||||
|
// standard JSON tags (all except "string").
|
||||||
|
package funcr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding"
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-logr/logr"
|
||||||
|
)
|
||||||
|
|
||||||
|
// New returns a logr.Logger which is implemented by an arbitrary function.
|
||||||
|
func New(fn func(prefix, args string), opts Options) logr.Logger {
|
||||||
|
return logr.New(newSink(fn, NewFormatter(opts)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewJSON returns a logr.Logger which is implemented by an arbitrary function
|
||||||
|
// and produces JSON output.
|
||||||
|
func NewJSON(fn func(obj string), opts Options) logr.Logger {
|
||||||
|
fnWrapper := func(_, obj string) {
|
||||||
|
fn(obj)
|
||||||
|
}
|
||||||
|
return logr.New(newSink(fnWrapper, NewFormatterJSON(opts)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Underlier exposes access to the underlying logging function. Since
|
||||||
|
// callers only have a logr.Logger, they have to know which
|
||||||
|
// implementation is in use, so this interface is less of an
|
||||||
|
// abstraction and more of a way to test type conversion.
|
||||||
|
type Underlier interface {
|
||||||
|
GetUnderlying() func(prefix, args string)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSink(fn func(prefix, args string), formatter Formatter) logr.LogSink {
|
||||||
|
l := &fnlogger{
|
||||||
|
Formatter: formatter,
|
||||||
|
write: fn,
|
||||||
|
}
|
||||||
|
// For skipping fnlogger.Info and fnlogger.Error.
|
||||||
|
l.Formatter.AddCallDepth(1)
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
// Options carries parameters which influence the way logs are generated.
|
||||||
|
type Options struct {
|
||||||
|
// LogCaller tells funcr to add a "caller" key to some or all log lines.
|
||||||
|
// This has some overhead, so some users might not want it.
|
||||||
|
LogCaller MessageClass
|
||||||
|
|
||||||
|
// LogCallerFunc tells funcr to also log the calling function name. This
|
||||||
|
// has no effect if caller logging is not enabled (see Options.LogCaller).
|
||||||
|
LogCallerFunc bool
|
||||||
|
|
||||||
|
// LogTimestamp tells funcr to add a "ts" key to log lines. This has some
|
||||||
|
// overhead, so some users might not want it.
|
||||||
|
LogTimestamp bool
|
||||||
|
|
||||||
|
// TimestampFormat tells funcr how to render timestamps when LogTimestamp
|
||||||
|
// is enabled. If not specified, a default format will be used. For more
|
||||||
|
// details, see docs for Go's time.Layout.
|
||||||
|
TimestampFormat string
|
||||||
|
|
||||||
|
// Verbosity tells funcr which V logs to produce. Higher values enable
|
||||||
|
// more logs. Info logs at or below this level will be written, while logs
|
||||||
|
// above this level will be discarded.
|
||||||
|
Verbosity int
|
||||||
|
|
||||||
|
// RenderBuiltinsHook allows users to mutate the list of key-value pairs
|
||||||
|
// while a log line is being rendered. The kvList argument follows logr
|
||||||
|
// conventions - each pair of slice elements is comprised of a string key
|
||||||
|
// and an arbitrary value (verified and sanitized before calling this
|
||||||
|
// hook). The value returned must follow the same conventions. This hook
|
||||||
|
// can be used to audit or modify logged data. For example, you might want
|
||||||
|
// to prefix all of funcr's built-in keys with some string. This hook is
|
||||||
|
// only called for built-in (provided by funcr itself) key-value pairs.
|
||||||
|
// Equivalent hooks are offered for key-value pairs saved via
|
||||||
|
// logr.Logger.WithValues or Formatter.AddValues (see RenderValuesHook) and
|
||||||
|
// for user-provided pairs (see RenderArgsHook).
|
||||||
|
RenderBuiltinsHook func(kvList []interface{}) []interface{}
|
||||||
|
|
||||||
|
// RenderValuesHook is the same as RenderBuiltinsHook, except that it is
|
||||||
|
// only called for key-value pairs saved via logr.Logger.WithValues. See
|
||||||
|
// RenderBuiltinsHook for more details.
|
||||||
|
RenderValuesHook func(kvList []interface{}) []interface{}
|
||||||
|
|
||||||
|
// RenderArgsHook is the same as RenderBuiltinsHook, except that it is only
|
||||||
|
// called for key-value pairs passed directly to Info and Error. See
|
||||||
|
// RenderBuiltinsHook for more details.
|
||||||
|
RenderArgsHook func(kvList []interface{}) []interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MessageClass indicates which category or categories of messages to consider.
|
||||||
|
type MessageClass int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// None ignores all message classes.
|
||||||
|
None MessageClass = iota
|
||||||
|
// All considers all message classes.
|
||||||
|
All
|
||||||
|
// Info only considers info messages.
|
||||||
|
Info
|
||||||
|
// Error only considers error messages.
|
||||||
|
Error
|
||||||
|
)
|
||||||
|
|
||||||
|
// fnlogger inherits some of its LogSink implementation from Formatter
|
||||||
|
// and just needs to add some glue code.
|
||||||
|
type fnlogger struct {
|
||||||
|
Formatter
|
||||||
|
write func(prefix, args string)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l fnlogger) WithName(name string) logr.LogSink {
|
||||||
|
l.Formatter.AddName(name)
|
||||||
|
return &l
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l fnlogger) WithValues(kvList ...interface{}) logr.LogSink {
|
||||||
|
l.Formatter.AddValues(kvList)
|
||||||
|
return &l
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l fnlogger) WithCallDepth(depth int) logr.LogSink {
|
||||||
|
l.Formatter.AddCallDepth(depth)
|
||||||
|
return &l
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l fnlogger) Info(level int, msg string, kvList ...interface{}) {
|
||||||
|
prefix, args := l.FormatInfo(level, msg, kvList)
|
||||||
|
l.write(prefix, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l fnlogger) Error(err error, msg string, kvList ...interface{}) {
|
||||||
|
prefix, args := l.FormatError(err, msg, kvList)
|
||||||
|
l.write(prefix, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l fnlogger) GetUnderlying() func(prefix, args string) {
|
||||||
|
return l.write
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assert conformance to the interfaces.
|
||||||
|
var _ logr.LogSink = &fnlogger{}
|
||||||
|
var _ logr.CallDepthLogSink = &fnlogger{}
|
||||||
|
var _ Underlier = &fnlogger{}
|
||||||
|
|
||||||
|
// NewFormatter constructs a Formatter which emits a JSON-like key=value format.
|
||||||
|
func NewFormatter(opts Options) Formatter {
|
||||||
|
return newFormatter(opts, outputKeyValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFormatterJSON constructs a Formatter which emits strict JSON.
|
||||||
|
func NewFormatterJSON(opts Options) Formatter {
|
||||||
|
return newFormatter(opts, outputJSON)
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultTimestampFmt = "2006-01-02 15:04:05.000000"
|
||||||
|
|
||||||
|
func newFormatter(opts Options, outfmt outputFormat) Formatter {
|
||||||
|
if opts.TimestampFormat == "" {
|
||||||
|
opts.TimestampFormat = defaultTimestampFmt
|
||||||
|
}
|
||||||
|
f := Formatter{
|
||||||
|
outputFormat: outfmt,
|
||||||
|
prefix: "",
|
||||||
|
values: nil,
|
||||||
|
depth: 0,
|
||||||
|
opts: opts,
|
||||||
|
}
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
// Formatter is an opaque struct which can be embedded in a LogSink
|
||||||
|
// implementation. It should be constructed with NewFormatter. Some of
|
||||||
|
// its methods directly implement logr.LogSink.
|
||||||
|
type Formatter struct {
|
||||||
|
outputFormat outputFormat
|
||||||
|
prefix string
|
||||||
|
values []interface{}
|
||||||
|
valuesStr string
|
||||||
|
depth int
|
||||||
|
opts Options
|
||||||
|
}
|
||||||
|
|
||||||
|
// outputFormat indicates which outputFormat to use.
|
||||||
|
type outputFormat int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// outputKeyValue emits a JSON-like key=value format, but not strict JSON.
|
||||||
|
outputKeyValue outputFormat = iota
|
||||||
|
// outputJSON emits strict JSON.
|
||||||
|
outputJSON
|
||||||
|
)
|
||||||
|
|
||||||
|
// PseudoStruct is a list of key-value pairs that gets logged as a struct.
|
||||||
|
type PseudoStruct []interface{}
|
||||||
|
|
||||||
|
// render produces a log line, ready to use.
|
||||||
|
func (f Formatter) render(builtins, args []interface{}) string {
|
||||||
|
// Empirically bytes.Buffer is faster than strings.Builder for this.
|
||||||
|
buf := bytes.NewBuffer(make([]byte, 0, 1024))
|
||||||
|
if f.outputFormat == outputJSON {
|
||||||
|
buf.WriteByte('{')
|
||||||
|
}
|
||||||
|
vals := builtins
|
||||||
|
if hook := f.opts.RenderBuiltinsHook; hook != nil {
|
||||||
|
vals = hook(f.sanitize(vals))
|
||||||
|
}
|
||||||
|
f.flatten(buf, vals, false, false) // keys are ours, no need to escape
|
||||||
|
continuing := len(builtins) > 0
|
||||||
|
if len(f.valuesStr) > 0 {
|
||||||
|
if continuing {
|
||||||
|
if f.outputFormat == outputJSON {
|
||||||
|
buf.WriteByte(',')
|
||||||
|
} else {
|
||||||
|
buf.WriteByte(' ')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continuing = true
|
||||||
|
buf.WriteString(f.valuesStr)
|
||||||
|
}
|
||||||
|
vals = args
|
||||||
|
if hook := f.opts.RenderArgsHook; hook != nil {
|
||||||
|
vals = hook(f.sanitize(vals))
|
||||||
|
}
|
||||||
|
f.flatten(buf, vals, continuing, true) // escape user-provided keys
|
||||||
|
if f.outputFormat == outputJSON {
|
||||||
|
buf.WriteByte('}')
|
||||||
|
}
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// flatten renders a list of key-value pairs into a buffer. If continuing is
|
||||||
|
// true, it assumes that the buffer has previous values and will emit a
|
||||||
|
// separator (which depends on the output format) before the first pair it
|
||||||
|
// writes. If escapeKeys is true, the keys are assumed to have
|
||||||
|
// non-JSON-compatible characters in them and must be evaluated for escapes.
|
||||||
|
//
|
||||||
|
// This function returns a potentially modified version of kvList, which
|
||||||
|
// ensures that there is a value for every key (adding a value if needed) and
|
||||||
|
// that each key is a string (substituting a key if needed).
|
||||||
|
func (f Formatter) flatten(buf *bytes.Buffer, kvList []interface{}, continuing bool, escapeKeys bool) []interface{} {
|
||||||
|
// This logic overlaps with sanitize() but saves one type-cast per key,
|
||||||
|
// which can be measurable.
|
||||||
|
if len(kvList)%2 != 0 {
|
||||||
|
kvList = append(kvList, noValue)
|
||||||
|
}
|
||||||
|
for i := 0; i < len(kvList); i += 2 {
|
||||||
|
k, ok := kvList[i].(string)
|
||||||
|
if !ok {
|
||||||
|
k = f.nonStringKey(kvList[i])
|
||||||
|
kvList[i] = k
|
||||||
|
}
|
||||||
|
v := kvList[i+1]
|
||||||
|
|
||||||
|
if i > 0 || continuing {
|
||||||
|
if f.outputFormat == outputJSON {
|
||||||
|
buf.WriteByte(',')
|
||||||
|
} else {
|
||||||
|
// In theory the format could be something we don't understand. In
|
||||||
|
// practice, we control it, so it won't be.
|
||||||
|
buf.WriteByte(' ')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if escapeKeys {
|
||||||
|
buf.WriteString(prettyString(k))
|
||||||
|
} else {
|
||||||
|
// this is faster
|
||||||
|
buf.WriteByte('"')
|
||||||
|
buf.WriteString(k)
|
||||||
|
buf.WriteByte('"')
|
||||||
|
}
|
||||||
|
if f.outputFormat == outputJSON {
|
||||||
|
buf.WriteByte(':')
|
||||||
|
} else {
|
||||||
|
buf.WriteByte('=')
|
||||||
|
}
|
||||||
|
buf.WriteString(f.pretty(v))
|
||||||
|
}
|
||||||
|
return kvList
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f Formatter) pretty(value interface{}) string {
|
||||||
|
return f.prettyWithFlags(value, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
flagRawStruct = 0x1 // do not print braces on structs
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO: This is not fast. Most of the overhead goes here.
|
||||||
|
func (f Formatter) prettyWithFlags(value interface{}, flags uint32) string {
|
||||||
|
// Handle types that take full control of logging.
|
||||||
|
if v, ok := value.(logr.Marshaler); ok {
|
||||||
|
// Replace the value with what the type wants to get logged.
|
||||||
|
// That then gets handled below via reflection.
|
||||||
|
value = v.MarshalLog()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle types that want to format themselves.
|
||||||
|
switch v := value.(type) {
|
||||||
|
case fmt.Stringer:
|
||||||
|
value = v.String()
|
||||||
|
case error:
|
||||||
|
value = v.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handling the most common types without reflect is a small perf win.
|
||||||
|
switch v := value.(type) {
|
||||||
|
case bool:
|
||||||
|
return strconv.FormatBool(v)
|
||||||
|
case string:
|
||||||
|
return prettyString(v)
|
||||||
|
case int:
|
||||||
|
return strconv.FormatInt(int64(v), 10)
|
||||||
|
case int8:
|
||||||
|
return strconv.FormatInt(int64(v), 10)
|
||||||
|
case int16:
|
||||||
|
return strconv.FormatInt(int64(v), 10)
|
||||||
|
case int32:
|
||||||
|
return strconv.FormatInt(int64(v), 10)
|
||||||
|
case int64:
|
||||||
|
return strconv.FormatInt(int64(v), 10)
|
||||||
|
case uint:
|
||||||
|
return strconv.FormatUint(uint64(v), 10)
|
||||||
|
case uint8:
|
||||||
|
return strconv.FormatUint(uint64(v), 10)
|
||||||
|
case uint16:
|
||||||
|
return strconv.FormatUint(uint64(v), 10)
|
||||||
|
case uint32:
|
||||||
|
return strconv.FormatUint(uint64(v), 10)
|
||||||
|
case uint64:
|
||||||
|
return strconv.FormatUint(v, 10)
|
||||||
|
case uintptr:
|
||||||
|
return strconv.FormatUint(uint64(v), 10)
|
||||||
|
case float32:
|
||||||
|
return strconv.FormatFloat(float64(v), 'f', -1, 32)
|
||||||
|
case float64:
|
||||||
|
return strconv.FormatFloat(v, 'f', -1, 64)
|
||||||
|
case complex64:
|
||||||
|
return `"` + strconv.FormatComplex(complex128(v), 'f', -1, 64) + `"`
|
||||||
|
case complex128:
|
||||||
|
return `"` + strconv.FormatComplex(v, 'f', -1, 128) + `"`
|
||||||
|
case PseudoStruct:
|
||||||
|
buf := bytes.NewBuffer(make([]byte, 0, 1024))
|
||||||
|
v = f.sanitize(v)
|
||||||
|
if flags&flagRawStruct == 0 {
|
||||||
|
buf.WriteByte('{')
|
||||||
|
}
|
||||||
|
for i := 0; i < len(v); i += 2 {
|
||||||
|
if i > 0 {
|
||||||
|
buf.WriteByte(',')
|
||||||
|
}
|
||||||
|
// arbitrary keys might need escaping
|
||||||
|
buf.WriteString(prettyString(v[i].(string)))
|
||||||
|
buf.WriteByte(':')
|
||||||
|
buf.WriteString(f.pretty(v[i+1]))
|
||||||
|
}
|
||||||
|
if flags&flagRawStruct == 0 {
|
||||||
|
buf.WriteByte('}')
|
||||||
|
}
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := bytes.NewBuffer(make([]byte, 0, 256))
|
||||||
|
t := reflect.TypeOf(value)
|
||||||
|
if t == nil {
|
||||||
|
return "null"
|
||||||
|
}
|
||||||
|
v := reflect.ValueOf(value)
|
||||||
|
switch t.Kind() {
|
||||||
|
case reflect.Bool:
|
||||||
|
return strconv.FormatBool(v.Bool())
|
||||||
|
case reflect.String:
|
||||||
|
return prettyString(v.String())
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
return strconv.FormatInt(int64(v.Int()), 10)
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||||
|
return strconv.FormatUint(uint64(v.Uint()), 10)
|
||||||
|
case reflect.Float32:
|
||||||
|
return strconv.FormatFloat(float64(v.Float()), 'f', -1, 32)
|
||||||
|
case reflect.Float64:
|
||||||
|
return strconv.FormatFloat(v.Float(), 'f', -1, 64)
|
||||||
|
case reflect.Complex64:
|
||||||
|
return `"` + strconv.FormatComplex(complex128(v.Complex()), 'f', -1, 64) + `"`
|
||||||
|
case reflect.Complex128:
|
||||||
|
return `"` + strconv.FormatComplex(v.Complex(), 'f', -1, 128) + `"`
|
||||||
|
case reflect.Struct:
|
||||||
|
if flags&flagRawStruct == 0 {
|
||||||
|
buf.WriteByte('{')
|
||||||
|
}
|
||||||
|
for i := 0; i < t.NumField(); i++ {
|
||||||
|
fld := t.Field(i)
|
||||||
|
if fld.PkgPath != "" {
|
||||||
|
// reflect says this field is only defined for non-exported fields.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !v.Field(i).CanInterface() {
|
||||||
|
// reflect isn't clear exactly what this means, but we can't use it.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
name := ""
|
||||||
|
omitempty := false
|
||||||
|
if tag, found := fld.Tag.Lookup("json"); found {
|
||||||
|
if tag == "-" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if comma := strings.Index(tag, ","); comma != -1 {
|
||||||
|
if n := tag[:comma]; n != "" {
|
||||||
|
name = n
|
||||||
|
}
|
||||||
|
rest := tag[comma:]
|
||||||
|
if strings.Contains(rest, ",omitempty,") || strings.HasSuffix(rest, ",omitempty") {
|
||||||
|
omitempty = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
name = tag
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if omitempty && isEmpty(v.Field(i)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if i > 0 {
|
||||||
|
buf.WriteByte(',')
|
||||||
|
}
|
||||||
|
if fld.Anonymous && fld.Type.Kind() == reflect.Struct && name == "" {
|
||||||
|
buf.WriteString(f.prettyWithFlags(v.Field(i).Interface(), flags|flagRawStruct))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if name == "" {
|
||||||
|
name = fld.Name
|
||||||
|
}
|
||||||
|
// field names can't contain characters which need escaping
|
||||||
|
buf.WriteByte('"')
|
||||||
|
buf.WriteString(name)
|
||||||
|
buf.WriteByte('"')
|
||||||
|
buf.WriteByte(':')
|
||||||
|
buf.WriteString(f.pretty(v.Field(i).Interface()))
|
||||||
|
}
|
||||||
|
if flags&flagRawStruct == 0 {
|
||||||
|
buf.WriteByte('}')
|
||||||
|
}
|
||||||
|
return buf.String()
|
||||||
|
case reflect.Slice, reflect.Array:
|
||||||
|
buf.WriteByte('[')
|
||||||
|
for i := 0; i < v.Len(); i++ {
|
||||||
|
if i > 0 {
|
||||||
|
buf.WriteByte(',')
|
||||||
|
}
|
||||||
|
e := v.Index(i)
|
||||||
|
buf.WriteString(f.pretty(e.Interface()))
|
||||||
|
}
|
||||||
|
buf.WriteByte(']')
|
||||||
|
return buf.String()
|
||||||
|
case reflect.Map:
|
||||||
|
buf.WriteByte('{')
|
||||||
|
// This does not sort the map keys, for best perf.
|
||||||
|
it := v.MapRange()
|
||||||
|
i := 0
|
||||||
|
for it.Next() {
|
||||||
|
if i > 0 {
|
||||||
|
buf.WriteByte(',')
|
||||||
|
}
|
||||||
|
// If a map key supports TextMarshaler, use it.
|
||||||
|
keystr := ""
|
||||||
|
if m, ok := it.Key().Interface().(encoding.TextMarshaler); ok {
|
||||||
|
txt, err := m.MarshalText()
|
||||||
|
if err != nil {
|
||||||
|
keystr = fmt.Sprintf("<error-MarshalText: %s>", err.Error())
|
||||||
|
} else {
|
||||||
|
keystr = string(txt)
|
||||||
|
}
|
||||||
|
keystr = prettyString(keystr)
|
||||||
|
} else {
|
||||||
|
// prettyWithFlags will produce already-escaped values
|
||||||
|
keystr = f.prettyWithFlags(it.Key().Interface(), 0)
|
||||||
|
if t.Key().Kind() != reflect.String {
|
||||||
|
// JSON only does string keys. Unlike Go's standard JSON, we'll
|
||||||
|
// convert just about anything to a string.
|
||||||
|
keystr = prettyString(keystr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf.WriteString(keystr)
|
||||||
|
buf.WriteByte(':')
|
||||||
|
buf.WriteString(f.pretty(it.Value().Interface()))
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
buf.WriteByte('}')
|
||||||
|
return buf.String()
|
||||||
|
case reflect.Ptr, reflect.Interface:
|
||||||
|
if v.IsNil() {
|
||||||
|
return "null"
|
||||||
|
}
|
||||||
|
return f.pretty(v.Elem().Interface())
|
||||||
|
}
|
||||||
|
return fmt.Sprintf(`"<unhandled-%s>"`, t.Kind().String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func prettyString(s string) string {
|
||||||
|
// Avoid escaping (which does allocations) if we can.
|
||||||
|
if needsEscape(s) {
|
||||||
|
return strconv.Quote(s)
|
||||||
|
}
|
||||||
|
b := bytes.NewBuffer(make([]byte, 0, 1024))
|
||||||
|
b.WriteByte('"')
|
||||||
|
b.WriteString(s)
|
||||||
|
b.WriteByte('"')
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// needsEscape determines whether the input string needs to be escaped or not,
|
||||||
|
// without doing any allocations.
|
||||||
|
func needsEscape(s string) bool {
|
||||||
|
for _, r := range s {
|
||||||
|
if !strconv.IsPrint(r) || r == '\\' || r == '"' {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func isEmpty(v reflect.Value) bool {
|
||||||
|
switch v.Kind() {
|
||||||
|
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
|
||||||
|
return v.Len() == 0
|
||||||
|
case reflect.Bool:
|
||||||
|
return !v.Bool()
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
return v.Int() == 0
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||||
|
return v.Uint() == 0
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
return v.Float() == 0
|
||||||
|
case reflect.Complex64, reflect.Complex128:
|
||||||
|
return v.Complex() == 0
|
||||||
|
case reflect.Interface, reflect.Ptr:
|
||||||
|
return v.IsNil()
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Caller represents the original call site for a log line, after considering
|
||||||
|
// logr.Logger.WithCallDepth and logr.Logger.WithCallStackHelper. The File and
|
||||||
|
// Line fields will always be provided, while the Func field is optional.
|
||||||
|
// Users can set the render hook fields in Options to examine logged key-value
|
||||||
|
// pairs, one of which will be {"caller", Caller} if the Options.LogCaller
|
||||||
|
// field is enabled for the given MessageClass.
|
||||||
|
type Caller struct {
|
||||||
|
// File is the basename of the file for this call site.
|
||||||
|
File string `json:"file"`
|
||||||
|
// Line is the line number in the file for this call site.
|
||||||
|
Line int `json:"line"`
|
||||||
|
// Func is the function name for this call site, or empty if
|
||||||
|
// Options.LogCallerFunc is not enabled.
|
||||||
|
Func string `json:"function,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f Formatter) caller() Caller {
|
||||||
|
// +1 for this frame, +1 for Info/Error.
|
||||||
|
pc, file, line, ok := runtime.Caller(f.depth + 2)
|
||||||
|
if !ok {
|
||||||
|
return Caller{"<unknown>", 0, ""}
|
||||||
|
}
|
||||||
|
fn := ""
|
||||||
|
if f.opts.LogCallerFunc {
|
||||||
|
if fp := runtime.FuncForPC(pc); fp != nil {
|
||||||
|
fn = fp.Name()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Caller{filepath.Base(file), line, fn}
|
||||||
|
}
|
||||||
|
|
||||||
|
const noValue = "<no-value>"
|
||||||
|
|
||||||
|
func (f Formatter) nonStringKey(v interface{}) string {
|
||||||
|
return fmt.Sprintf("<non-string-key: %s>", f.snippet(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// snippet produces a short snippet string of an arbitrary value.
|
||||||
|
func (f Formatter) snippet(v interface{}) string {
|
||||||
|
const snipLen = 16
|
||||||
|
|
||||||
|
snip := f.pretty(v)
|
||||||
|
if len(snip) > snipLen {
|
||||||
|
snip = snip[:snipLen]
|
||||||
|
}
|
||||||
|
return snip
|
||||||
|
}
|
||||||
|
|
||||||
|
// sanitize ensures that a list of key-value pairs has a value for every key
|
||||||
|
// (adding a value if needed) and that each key is a string (substituting a key
|
||||||
|
// if needed).
|
||||||
|
func (f Formatter) sanitize(kvList []interface{}) []interface{} {
|
||||||
|
if len(kvList)%2 != 0 {
|
||||||
|
kvList = append(kvList, noValue)
|
||||||
|
}
|
||||||
|
for i := 0; i < len(kvList); i += 2 {
|
||||||
|
_, ok := kvList[i].(string)
|
||||||
|
if !ok {
|
||||||
|
kvList[i] = f.nonStringKey(kvList[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return kvList
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init configures this Formatter from runtime info, such as the call depth
|
||||||
|
// imposed by logr itself.
|
||||||
|
// Note that this receiver is a pointer, so depth can be saved.
|
||||||
|
func (f *Formatter) Init(info logr.RuntimeInfo) {
|
||||||
|
f.depth += info.CallDepth
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enabled checks whether an info message at the given level should be logged.
|
||||||
|
func (f Formatter) Enabled(level int) bool {
|
||||||
|
return level <= f.opts.Verbosity
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDepth returns the current depth of this Formatter. This is useful for
|
||||||
|
// implementations which do their own caller attribution.
|
||||||
|
func (f Formatter) GetDepth() int {
|
||||||
|
return f.depth
|
||||||
|
}
|
||||||
|
|
||||||
|
// FormatInfo renders an Info log message into strings. The prefix will be
|
||||||
|
// empty when no names were set (via AddNames), or when the output is
|
||||||
|
// configured for JSON.
|
||||||
|
func (f Formatter) FormatInfo(level int, msg string, kvList []interface{}) (prefix, argsStr string) {
|
||||||
|
args := make([]interface{}, 0, 64) // using a constant here impacts perf
|
||||||
|
prefix = f.prefix
|
||||||
|
if f.outputFormat == outputJSON {
|
||||||
|
args = append(args, "logger", prefix)
|
||||||
|
prefix = ""
|
||||||
|
}
|
||||||
|
if f.opts.LogTimestamp {
|
||||||
|
args = append(args, "ts", time.Now().Format(f.opts.TimestampFormat))
|
||||||
|
}
|
||||||
|
if policy := f.opts.LogCaller; policy == All || policy == Info {
|
||||||
|
args = append(args, "caller", f.caller())
|
||||||
|
}
|
||||||
|
args = append(args, "level", level, "msg", msg)
|
||||||
|
return prefix, f.render(args, kvList)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FormatError renders an Error log message into strings. The prefix will be
|
||||||
|
// empty when no names were set (via AddNames), or when the output is
|
||||||
|
// configured for JSON.
|
||||||
|
func (f Formatter) FormatError(err error, msg string, kvList []interface{}) (prefix, argsStr string) {
|
||||||
|
args := make([]interface{}, 0, 64) // using a constant here impacts perf
|
||||||
|
prefix = f.prefix
|
||||||
|
if f.outputFormat == outputJSON {
|
||||||
|
args = append(args, "logger", prefix)
|
||||||
|
prefix = ""
|
||||||
|
}
|
||||||
|
if f.opts.LogTimestamp {
|
||||||
|
args = append(args, "ts", time.Now().Format(f.opts.TimestampFormat))
|
||||||
|
}
|
||||||
|
if policy := f.opts.LogCaller; policy == All || policy == Error {
|
||||||
|
args = append(args, "caller", f.caller())
|
||||||
|
}
|
||||||
|
args = append(args, "msg", msg)
|
||||||
|
var loggableErr interface{}
|
||||||
|
if err != nil {
|
||||||
|
loggableErr = err.Error()
|
||||||
|
}
|
||||||
|
args = append(args, "error", loggableErr)
|
||||||
|
return f.prefix, f.render(args, kvList)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddName appends the specified name. funcr uses '/' characters to separate
|
||||||
|
// name elements. Callers should not pass '/' in the provided name string, but
|
||||||
|
// this library does not actually enforce that.
|
||||||
|
func (f *Formatter) AddName(name string) {
|
||||||
|
if len(f.prefix) > 0 {
|
||||||
|
f.prefix += "/"
|
||||||
|
}
|
||||||
|
f.prefix += name
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddValues adds key-value pairs to the set of saved values to be logged with
|
||||||
|
// each log line.
|
||||||
|
func (f *Formatter) AddValues(kvList []interface{}) {
|
||||||
|
// Three slice args forces a copy.
|
||||||
|
n := len(f.values)
|
||||||
|
f.values = append(f.values[:n:n], kvList...)
|
||||||
|
|
||||||
|
vals := f.values
|
||||||
|
if hook := f.opts.RenderValuesHook; hook != nil {
|
||||||
|
vals = hook(f.sanitize(vals))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pre-render values, so we don't have to do it on each Info/Error call.
|
||||||
|
buf := bytes.NewBuffer(make([]byte, 0, 1024))
|
||||||
|
f.flatten(buf, vals, false, true) // escape user-provided keys
|
||||||
|
f.valuesStr = buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddCallDepth increases the number of stack-frames to skip when attributing
|
||||||
|
// the log line to a file and line.
|
||||||
|
func (f *Formatter) AddCallDepth(depth int) {
|
||||||
|
f.depth += depth
|
||||||
|
}
|
||||||
501
vendor/github.com/go-logr/logr/logr.go
generated
vendored
Normal file
501
vendor/github.com/go-logr/logr/logr.go
generated
vendored
Normal file
@@ -0,0 +1,501 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2019 The logr 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 design derives from Dave Cheney's blog:
|
||||||
|
// http://dave.cheney.net/2015/11/05/lets-talk-about-logging
|
||||||
|
|
||||||
|
// Package logr defines a general-purpose logging API and abstract interfaces
|
||||||
|
// to back that API. Packages in the Go ecosystem can depend on this package,
|
||||||
|
// while callers can implement logging with whatever backend is appropriate.
|
||||||
|
//
|
||||||
|
// Usage
|
||||||
|
//
|
||||||
|
// Logging is done using a Logger instance. Logger is a concrete type with
|
||||||
|
// methods, which defers the actual logging to a LogSink interface. The main
|
||||||
|
// methods of Logger are Info() and Error(). Arguments to Info() and Error()
|
||||||
|
// are key/value pairs rather than printf-style formatted strings, emphasizing
|
||||||
|
// "structured logging".
|
||||||
|
//
|
||||||
|
// With Go's standard log package, we might write:
|
||||||
|
// log.Printf("setting target value %s", targetValue)
|
||||||
|
//
|
||||||
|
// With logr's structured logging, we'd write:
|
||||||
|
// logger.Info("setting target", "value", targetValue)
|
||||||
|
//
|
||||||
|
// Errors are much the same. Instead of:
|
||||||
|
// log.Printf("failed to open the pod bay door for user %s: %v", user, err)
|
||||||
|
//
|
||||||
|
// We'd write:
|
||||||
|
// logger.Error(err, "failed to open the pod bay door", "user", user)
|
||||||
|
//
|
||||||
|
// Info() and Error() are very similar, but they are separate methods so that
|
||||||
|
// LogSink implementations can choose to do things like attach additional
|
||||||
|
// information (such as stack traces) on calls to Error(). Error() messages are
|
||||||
|
// always logged, regardless of the current verbosity. If there is no error
|
||||||
|
// instance available, passing nil is valid.
|
||||||
|
//
|
||||||
|
// Verbosity
|
||||||
|
//
|
||||||
|
// Often we want to log information only when the application in "verbose
|
||||||
|
// mode". To write log lines that are more verbose, Logger has a V() method.
|
||||||
|
// The higher the V-level of a log line, the less critical it is considered.
|
||||||
|
// Log-lines with V-levels that are not enabled (as per the LogSink) will not
|
||||||
|
// be written. Level V(0) is the default, and logger.V(0).Info() has the same
|
||||||
|
// meaning as logger.Info(). Negative V-levels have the same meaning as V(0).
|
||||||
|
// Error messages do not have a verbosity level and are always logged.
|
||||||
|
//
|
||||||
|
// Where we might have written:
|
||||||
|
// if flVerbose >= 2 {
|
||||||
|
// log.Printf("an unusual thing happened")
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// We can write:
|
||||||
|
// logger.V(2).Info("an unusual thing happened")
|
||||||
|
//
|
||||||
|
// Logger Names
|
||||||
|
//
|
||||||
|
// Logger instances can have name strings so that all messages logged through
|
||||||
|
// that instance have additional context. For example, you might want to add
|
||||||
|
// a subsystem name:
|
||||||
|
//
|
||||||
|
// logger.WithName("compactor").Info("started", "time", time.Now())
|
||||||
|
//
|
||||||
|
// The WithName() method returns a new Logger, which can be passed to
|
||||||
|
// constructors or other functions for further use. Repeated use of WithName()
|
||||||
|
// will accumulate name "segments". These name segments will be joined in some
|
||||||
|
// way by the LogSink implementation. It is strongly recommended that name
|
||||||
|
// segments contain simple identifiers (letters, digits, and hyphen), and do
|
||||||
|
// not contain characters that could muddle the log output or confuse the
|
||||||
|
// joining operation (e.g. whitespace, commas, periods, slashes, brackets,
|
||||||
|
// quotes, etc).
|
||||||
|
//
|
||||||
|
// Saved Values
|
||||||
|
//
|
||||||
|
// Logger instances can store any number of key/value pairs, which will be
|
||||||
|
// logged alongside all messages logged through that instance. For example,
|
||||||
|
// you might want to create a Logger instance per managed object:
|
||||||
|
//
|
||||||
|
// With the standard log package, we might write:
|
||||||
|
// log.Printf("decided to set field foo to value %q for object %s/%s",
|
||||||
|
// targetValue, object.Namespace, object.Name)
|
||||||
|
//
|
||||||
|
// With logr we'd write:
|
||||||
|
// // Elsewhere: set up the logger to log the object name.
|
||||||
|
// obj.logger = mainLogger.WithValues(
|
||||||
|
// "name", obj.name, "namespace", obj.namespace)
|
||||||
|
//
|
||||||
|
// // later on...
|
||||||
|
// obj.logger.Info("setting foo", "value", targetValue)
|
||||||
|
//
|
||||||
|
// Best Practices
|
||||||
|
//
|
||||||
|
// Logger has very few hard rules, with the goal that LogSink implementations
|
||||||
|
// might have a lot of freedom to differentiate. There are, however, some
|
||||||
|
// things to consider.
|
||||||
|
//
|
||||||
|
// The log message consists of a constant message attached to the log line.
|
||||||
|
// This should generally be a simple description of what's occurring, and should
|
||||||
|
// never be a format string. Variable information can then be attached using
|
||||||
|
// named values.
|
||||||
|
//
|
||||||
|
// Keys are arbitrary strings, but should generally be constant values. Values
|
||||||
|
// may be any Go value, but how the value is formatted is determined by the
|
||||||
|
// LogSink implementation.
|
||||||
|
//
|
||||||
|
// Key Naming Conventions
|
||||||
|
//
|
||||||
|
// Keys are not strictly required to conform to any specification or regex, but
|
||||||
|
// it is recommended that they:
|
||||||
|
// * be human-readable and meaningful (not auto-generated or simple ordinals)
|
||||||
|
// * be constant (not dependent on input data)
|
||||||
|
// * contain only printable characters
|
||||||
|
// * not contain whitespace or punctuation
|
||||||
|
// * use lower case for simple keys and lowerCamelCase for more complex ones
|
||||||
|
//
|
||||||
|
// These guidelines help ensure that log data is processed properly regardless
|
||||||
|
// of the log implementation. For example, log implementations will try to
|
||||||
|
// output JSON data or will store data for later database (e.g. SQL) queries.
|
||||||
|
//
|
||||||
|
// While users are generally free to use key names of their choice, it's
|
||||||
|
// generally best to avoid using the following keys, as they're frequently used
|
||||||
|
// by implementations:
|
||||||
|
// * "caller": the calling information (file/line) of a particular log line
|
||||||
|
// * "error": the underlying error value in the `Error` method
|
||||||
|
// * "level": the log level
|
||||||
|
// * "logger": the name of the associated logger
|
||||||
|
// * "msg": the log message
|
||||||
|
// * "stacktrace": the stack trace associated with a particular log line or
|
||||||
|
// error (often from the `Error` message)
|
||||||
|
// * "ts": the timestamp for a log line
|
||||||
|
//
|
||||||
|
// Implementations are encouraged to make use of these keys to represent the
|
||||||
|
// above concepts, when necessary (for example, in a pure-JSON output form, it
|
||||||
|
// would be necessary to represent at least message and timestamp as ordinary
|
||||||
|
// named values).
|
||||||
|
//
|
||||||
|
// Break Glass
|
||||||
|
//
|
||||||
|
// Implementations may choose to give callers access to the underlying
|
||||||
|
// logging implementation. The recommended pattern for this is:
|
||||||
|
// // Underlier exposes access to the underlying logging implementation.
|
||||||
|
// // Since callers only have a logr.Logger, they have to know which
|
||||||
|
// // implementation is in use, so this interface is less of an abstraction
|
||||||
|
// // and more of way to test type conversion.
|
||||||
|
// type Underlier interface {
|
||||||
|
// GetUnderlying() <underlying-type>
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Logger grants access to the sink to enable type assertions like this:
|
||||||
|
// func DoSomethingWithImpl(log logr.Logger) {
|
||||||
|
// if underlier, ok := log.GetSink()(impl.Underlier) {
|
||||||
|
// implLogger := underlier.GetUnderlying()
|
||||||
|
// ...
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Custom `With*` functions can be implemented by copying the complete
|
||||||
|
// Logger struct and replacing the sink in the copy:
|
||||||
|
// // WithFooBar changes the foobar parameter in the log sink and returns a
|
||||||
|
// // new logger with that modified sink. It does nothing for loggers where
|
||||||
|
// // the sink doesn't support that parameter.
|
||||||
|
// func WithFoobar(log logr.Logger, foobar int) logr.Logger {
|
||||||
|
// if foobarLogSink, ok := log.GetSink()(FoobarSink); ok {
|
||||||
|
// log = log.WithSink(foobarLogSink.WithFooBar(foobar))
|
||||||
|
// }
|
||||||
|
// return log
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Don't use New to construct a new Logger with a LogSink retrieved from an
|
||||||
|
// existing Logger. Source code attribution might not work correctly and
|
||||||
|
// unexported fields in Logger get lost.
|
||||||
|
//
|
||||||
|
// Beware that the same LogSink instance may be shared by different logger
|
||||||
|
// instances. Calling functions that modify the LogSink will affect all of
|
||||||
|
// those.
|
||||||
|
package logr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// New returns a new Logger instance. This is primarily used by libraries
|
||||||
|
// implementing LogSink, rather than end users.
|
||||||
|
func New(sink LogSink) Logger {
|
||||||
|
logger := Logger{}
|
||||||
|
logger.setSink(sink)
|
||||||
|
sink.Init(runtimeInfo)
|
||||||
|
return logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// setSink stores the sink and updates any related fields. It mutates the
|
||||||
|
// logger and thus is only safe to use for loggers that are not currently being
|
||||||
|
// used concurrently.
|
||||||
|
func (l *Logger) setSink(sink LogSink) {
|
||||||
|
l.sink = sink
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSink returns the stored sink.
|
||||||
|
func (l Logger) GetSink() LogSink {
|
||||||
|
return l.sink
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithSink returns a copy of the logger with the new sink.
|
||||||
|
func (l Logger) WithSink(sink LogSink) Logger {
|
||||||
|
l.setSink(sink)
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logger is an interface to an abstract logging implementation. This is a
|
||||||
|
// concrete type for performance reasons, but all the real work is passed on to
|
||||||
|
// a LogSink. Implementations of LogSink should provide their own constructors
|
||||||
|
// that return Logger, not LogSink.
|
||||||
|
//
|
||||||
|
// The underlying sink can be accessed through GetSink and be modified through
|
||||||
|
// WithSink. This enables the implementation of custom extensions (see "Break
|
||||||
|
// Glass" in the package documentation). Normally the sink should be used only
|
||||||
|
// indirectly.
|
||||||
|
type Logger struct {
|
||||||
|
sink LogSink
|
||||||
|
level int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enabled tests whether this Logger is enabled. For example, commandline
|
||||||
|
// flags might be used to set the logging verbosity and disable some info logs.
|
||||||
|
func (l Logger) Enabled() bool {
|
||||||
|
return l.sink.Enabled(l.level)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info logs a non-error message with the given key/value pairs as context.
|
||||||
|
//
|
||||||
|
// The msg argument should be used to add some constant description to the log
|
||||||
|
// line. The key/value pairs can then be used to add additional variable
|
||||||
|
// information. The key/value pairs must alternate string keys and arbitrary
|
||||||
|
// values.
|
||||||
|
func (l Logger) Info(msg string, keysAndValues ...interface{}) {
|
||||||
|
if l.Enabled() {
|
||||||
|
if withHelper, ok := l.sink.(CallStackHelperLogSink); ok {
|
||||||
|
withHelper.GetCallStackHelper()()
|
||||||
|
}
|
||||||
|
l.sink.Info(l.level, msg, keysAndValues...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error logs an error, with the given message and key/value pairs as context.
|
||||||
|
// It functions similarly to Info, but may have unique behavior, and should be
|
||||||
|
// preferred for logging errors (see the package documentations for more
|
||||||
|
// information). The log message will always be emitted, regardless of
|
||||||
|
// verbosity level.
|
||||||
|
//
|
||||||
|
// The msg argument should be used to add context to any underlying error,
|
||||||
|
// while the err argument should be used to attach the actual error that
|
||||||
|
// triggered this log line, if present. The err parameter is optional
|
||||||
|
// and nil may be passed instead of an error instance.
|
||||||
|
func (l Logger) Error(err error, msg string, keysAndValues ...interface{}) {
|
||||||
|
if withHelper, ok := l.sink.(CallStackHelperLogSink); ok {
|
||||||
|
withHelper.GetCallStackHelper()()
|
||||||
|
}
|
||||||
|
l.sink.Error(err, msg, keysAndValues...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// V returns a new Logger instance for a specific verbosity level, relative to
|
||||||
|
// this Logger. In other words, V-levels are additive. A higher verbosity
|
||||||
|
// level means a log message is less important. Negative V-levels are treated
|
||||||
|
// as 0.
|
||||||
|
func (l Logger) V(level int) Logger {
|
||||||
|
if level < 0 {
|
||||||
|
level = 0
|
||||||
|
}
|
||||||
|
l.level += level
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithValues returns a new Logger instance with additional key/value pairs.
|
||||||
|
// See Info for documentation on how key/value pairs work.
|
||||||
|
func (l Logger) WithValues(keysAndValues ...interface{}) Logger {
|
||||||
|
l.setSink(l.sink.WithValues(keysAndValues...))
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithName returns a new Logger instance with the specified name element added
|
||||||
|
// to the Logger's name. Successive calls with WithName append additional
|
||||||
|
// suffixes to the Logger's name. It's strongly recommended that name segments
|
||||||
|
// contain only letters, digits, and hyphens (see the package documentation for
|
||||||
|
// more information).
|
||||||
|
func (l Logger) WithName(name string) Logger {
|
||||||
|
l.setSink(l.sink.WithName(name))
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithCallDepth returns a Logger instance that offsets the call stack by the
|
||||||
|
// specified number of frames when logging call site information, if possible.
|
||||||
|
// This is useful for users who have helper functions between the "real" call
|
||||||
|
// site and the actual calls to Logger methods. If depth is 0 the attribution
|
||||||
|
// should be to the direct caller of this function. If depth is 1 the
|
||||||
|
// attribution should skip 1 call frame, and so on. Successive calls to this
|
||||||
|
// are additive.
|
||||||
|
//
|
||||||
|
// If the underlying log implementation supports a WithCallDepth(int) method,
|
||||||
|
// it will be called and the result returned. If the implementation does not
|
||||||
|
// support CallDepthLogSink, the original Logger will be returned.
|
||||||
|
//
|
||||||
|
// To skip one level, WithCallStackHelper() should be used instead of
|
||||||
|
// WithCallDepth(1) because it works with implementions that support the
|
||||||
|
// CallDepthLogSink and/or CallStackHelperLogSink interfaces.
|
||||||
|
func (l Logger) WithCallDepth(depth int) Logger {
|
||||||
|
if withCallDepth, ok := l.sink.(CallDepthLogSink); ok {
|
||||||
|
l.setSink(withCallDepth.WithCallDepth(depth))
|
||||||
|
}
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithCallStackHelper returns a new Logger instance that skips the direct
|
||||||
|
// caller when logging call site information, if possible. This is useful for
|
||||||
|
// users who have helper functions between the "real" call site and the actual
|
||||||
|
// calls to Logger methods and want to support loggers which depend on marking
|
||||||
|
// each individual helper function, like loggers based on testing.T.
|
||||||
|
//
|
||||||
|
// In addition to using that new logger instance, callers also must call the
|
||||||
|
// returned function.
|
||||||
|
//
|
||||||
|
// If the underlying log implementation supports a WithCallDepth(int) method,
|
||||||
|
// WithCallDepth(1) will be called to produce a new logger. If it supports a
|
||||||
|
// WithCallStackHelper() method, that will be also called. If the
|
||||||
|
// implementation does not support either of these, the original Logger will be
|
||||||
|
// returned.
|
||||||
|
func (l Logger) WithCallStackHelper() (func(), Logger) {
|
||||||
|
var helper func()
|
||||||
|
if withCallDepth, ok := l.sink.(CallDepthLogSink); ok {
|
||||||
|
l.setSink(withCallDepth.WithCallDepth(1))
|
||||||
|
}
|
||||||
|
if withHelper, ok := l.sink.(CallStackHelperLogSink); ok {
|
||||||
|
helper = withHelper.GetCallStackHelper()
|
||||||
|
} else {
|
||||||
|
helper = func() {}
|
||||||
|
}
|
||||||
|
return helper, l
|
||||||
|
}
|
||||||
|
|
||||||
|
// contextKey is how we find Loggers in a context.Context.
|
||||||
|
type contextKey struct{}
|
||||||
|
|
||||||
|
// FromContext returns a Logger from ctx or an error if no Logger is found.
|
||||||
|
func FromContext(ctx context.Context) (Logger, error) {
|
||||||
|
if v, ok := ctx.Value(contextKey{}).(Logger); ok {
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return Logger{}, notFoundError{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// notFoundError exists to carry an IsNotFound method.
|
||||||
|
type notFoundError struct{}
|
||||||
|
|
||||||
|
func (notFoundError) Error() string {
|
||||||
|
return "no logr.Logger was present"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (notFoundError) IsNotFound() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromContextOrDiscard returns a Logger from ctx. If no Logger is found, this
|
||||||
|
// returns a Logger that discards all log messages.
|
||||||
|
func FromContextOrDiscard(ctx context.Context) Logger {
|
||||||
|
if v, ok := ctx.Value(contextKey{}).(Logger); ok {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
return Discard()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewContext returns a new Context, derived from ctx, which carries the
|
||||||
|
// provided Logger.
|
||||||
|
func NewContext(ctx context.Context, logger Logger) context.Context {
|
||||||
|
return context.WithValue(ctx, contextKey{}, logger)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RuntimeInfo holds information that the logr "core" library knows which
|
||||||
|
// LogSinks might want to know.
|
||||||
|
type RuntimeInfo struct {
|
||||||
|
// CallDepth is the number of call frames the logr library adds between the
|
||||||
|
// end-user and the LogSink. LogSink implementations which choose to print
|
||||||
|
// the original logging site (e.g. file & line) should climb this many
|
||||||
|
// additional frames to find it.
|
||||||
|
CallDepth int
|
||||||
|
}
|
||||||
|
|
||||||
|
// runtimeInfo is a static global. It must not be changed at run time.
|
||||||
|
var runtimeInfo = RuntimeInfo{
|
||||||
|
CallDepth: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogSink represents a logging implementation. End-users will generally not
|
||||||
|
// interact with this type.
|
||||||
|
type LogSink interface {
|
||||||
|
// Init receives optional information about the logr library for LogSink
|
||||||
|
// implementations that need it.
|
||||||
|
Init(info RuntimeInfo)
|
||||||
|
|
||||||
|
// Enabled tests whether this LogSink is enabled at the specified V-level.
|
||||||
|
// For example, commandline flags might be used to set the logging
|
||||||
|
// verbosity and disable some info logs.
|
||||||
|
Enabled(level int) bool
|
||||||
|
|
||||||
|
// Info logs a non-error message with the given key/value pairs as context.
|
||||||
|
// The level argument is provided for optional logging. This method will
|
||||||
|
// only be called when Enabled(level) is true. See Logger.Info for more
|
||||||
|
// details.
|
||||||
|
Info(level int, msg string, keysAndValues ...interface{})
|
||||||
|
|
||||||
|
// Error logs an error, with the given message and key/value pairs as
|
||||||
|
// context. See Logger.Error for more details.
|
||||||
|
Error(err error, msg string, keysAndValues ...interface{})
|
||||||
|
|
||||||
|
// WithValues returns a new LogSink with additional key/value pairs. See
|
||||||
|
// Logger.WithValues for more details.
|
||||||
|
WithValues(keysAndValues ...interface{}) LogSink
|
||||||
|
|
||||||
|
// WithName returns a new LogSink with the specified name appended. See
|
||||||
|
// Logger.WithName for more details.
|
||||||
|
WithName(name string) LogSink
|
||||||
|
}
|
||||||
|
|
||||||
|
// CallDepthLogSink represents a Logger that knows how to climb the call stack
|
||||||
|
// to identify the original call site and can offset the depth by a specified
|
||||||
|
// number of frames. This is useful for users who have helper functions
|
||||||
|
// between the "real" call site and the actual calls to Logger methods.
|
||||||
|
// Implementations that log information about the call site (such as file,
|
||||||
|
// function, or line) would otherwise log information about the intermediate
|
||||||
|
// helper functions.
|
||||||
|
//
|
||||||
|
// This is an optional interface and implementations are not required to
|
||||||
|
// support it.
|
||||||
|
type CallDepthLogSink interface {
|
||||||
|
// WithCallDepth returns a LogSink that will offset the call
|
||||||
|
// stack by the specified number of frames when logging call
|
||||||
|
// site information.
|
||||||
|
//
|
||||||
|
// If depth is 0, the LogSink should skip exactly the number
|
||||||
|
// of call frames defined in RuntimeInfo.CallDepth when Info
|
||||||
|
// or Error are called, i.e. the attribution should be to the
|
||||||
|
// direct caller of Logger.Info or Logger.Error.
|
||||||
|
//
|
||||||
|
// If depth is 1 the attribution should skip 1 call frame, and so on.
|
||||||
|
// Successive calls to this are additive.
|
||||||
|
WithCallDepth(depth int) LogSink
|
||||||
|
}
|
||||||
|
|
||||||
|
// CallStackHelperLogSink represents a Logger that knows how to climb
|
||||||
|
// the call stack to identify the original call site and can skip
|
||||||
|
// intermediate helper functions if they mark themselves as
|
||||||
|
// helper. Go's testing package uses that approach.
|
||||||
|
//
|
||||||
|
// This is useful for users who have helper functions between the
|
||||||
|
// "real" call site and the actual calls to Logger methods.
|
||||||
|
// Implementations that log information about the call site (such as
|
||||||
|
// file, function, or line) would otherwise log information about the
|
||||||
|
// intermediate helper functions.
|
||||||
|
//
|
||||||
|
// This is an optional interface and implementations are not required
|
||||||
|
// to support it. Implementations that choose to support this must not
|
||||||
|
// simply implement it as WithCallDepth(1), because
|
||||||
|
// Logger.WithCallStackHelper will call both methods if they are
|
||||||
|
// present. This should only be implemented for LogSinks that actually
|
||||||
|
// need it, as with testing.T.
|
||||||
|
type CallStackHelperLogSink interface {
|
||||||
|
// GetCallStackHelper returns a function that must be called
|
||||||
|
// to mark the direct caller as helper function when logging
|
||||||
|
// call site information.
|
||||||
|
GetCallStackHelper() func()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshaler is an optional interface that logged values may choose to
|
||||||
|
// implement. Loggers with structured output, such as JSON, should
|
||||||
|
// log the object return by the MarshalLog method instead of the
|
||||||
|
// original value.
|
||||||
|
type Marshaler interface {
|
||||||
|
// MarshalLog can be used to:
|
||||||
|
// - ensure that structs are not logged as strings when the original
|
||||||
|
// value has a String method: return a different type without a
|
||||||
|
// String method
|
||||||
|
// - select which fields of a complex type should get logged:
|
||||||
|
// return a simpler struct with fewer fields
|
||||||
|
// - log unexported fields: return a different struct
|
||||||
|
// with exported fields
|
||||||
|
//
|
||||||
|
// It may return any value of any type.
|
||||||
|
MarshalLog() interface{}
|
||||||
|
}
|
||||||
201
vendor/github.com/go-logr/stdr/LICENSE
generated
vendored
Normal file
201
vendor/github.com/go-logr/stdr/LICENSE
generated
vendored
Normal file
@@ -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.
|
||||||
6
vendor/github.com/go-logr/stdr/README.md
generated
vendored
Normal file
6
vendor/github.com/go-logr/stdr/README.md
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# Minimal Go logging using logr and Go's standard library
|
||||||
|
|
||||||
|
[](https://pkg.go.dev/github.com/go-logr/stdr)
|
||||||
|
|
||||||
|
This package implements the [logr interface](https://github.com/go-logr/logr)
|
||||||
|
in terms of Go's standard log package(https://pkg.go.dev/log).
|
||||||
170
vendor/github.com/go-logr/stdr/stdr.go
generated
vendored
Normal file
170
vendor/github.com/go-logr/stdr/stdr.go
generated
vendored
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2019 The logr 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 stdr implements github.com/go-logr/logr.Logger in terms of
|
||||||
|
// Go's standard log package.
|
||||||
|
package stdr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/go-logr/logr"
|
||||||
|
"github.com/go-logr/logr/funcr"
|
||||||
|
)
|
||||||
|
|
||||||
|
// The global verbosity level. See SetVerbosity().
|
||||||
|
var globalVerbosity int
|
||||||
|
|
||||||
|
// SetVerbosity sets the global level against which all info logs will be
|
||||||
|
// compared. If this is greater than or equal to the "V" of the logger, the
|
||||||
|
// message will be logged. A higher value here means more logs will be written.
|
||||||
|
// The previous verbosity value is returned. This is not concurrent-safe -
|
||||||
|
// callers must be sure to call it from only one goroutine.
|
||||||
|
func SetVerbosity(v int) int {
|
||||||
|
old := globalVerbosity
|
||||||
|
globalVerbosity = v
|
||||||
|
return old
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a logr.Logger which is implemented by Go's standard log package,
|
||||||
|
// or something like it. If std is nil, this will use a default logger
|
||||||
|
// instead.
|
||||||
|
//
|
||||||
|
// Example: stdr.New(log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile)))
|
||||||
|
func New(std StdLogger) logr.Logger {
|
||||||
|
return NewWithOptions(std, Options{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewWithOptions returns a logr.Logger which is implemented by Go's standard
|
||||||
|
// log package, or something like it. See New for details.
|
||||||
|
func NewWithOptions(std StdLogger, opts Options) logr.Logger {
|
||||||
|
if std == nil {
|
||||||
|
// Go's log.Default() is only available in 1.16 and higher.
|
||||||
|
std = log.New(os.Stderr, "", log.LstdFlags)
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.Depth < 0 {
|
||||||
|
opts.Depth = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fopts := funcr.Options{
|
||||||
|
LogCaller: funcr.MessageClass(opts.LogCaller),
|
||||||
|
}
|
||||||
|
|
||||||
|
sl := &logger{
|
||||||
|
Formatter: funcr.NewFormatter(fopts),
|
||||||
|
std: std,
|
||||||
|
}
|
||||||
|
|
||||||
|
// For skipping our own logger.Info/Error.
|
||||||
|
sl.Formatter.AddCallDepth(1 + opts.Depth)
|
||||||
|
|
||||||
|
return logr.New(sl)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Options carries parameters which influence the way logs are generated.
|
||||||
|
type Options struct {
|
||||||
|
// Depth biases the assumed number of call frames to the "true" caller.
|
||||||
|
// This is useful when the calling code calls a function which then calls
|
||||||
|
// stdr (e.g. a logging shim to another API). Values less than zero will
|
||||||
|
// be treated as zero.
|
||||||
|
Depth int
|
||||||
|
|
||||||
|
// LogCaller tells stdr to add a "caller" key to some or all log lines.
|
||||||
|
// Go's log package has options to log this natively, too.
|
||||||
|
LogCaller MessageClass
|
||||||
|
|
||||||
|
// TODO: add an option to log the date/time
|
||||||
|
}
|
||||||
|
|
||||||
|
// MessageClass indicates which category or categories of messages to consider.
|
||||||
|
type MessageClass int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// None ignores all message classes.
|
||||||
|
None MessageClass = iota
|
||||||
|
// All considers all message classes.
|
||||||
|
All
|
||||||
|
// Info only considers info messages.
|
||||||
|
Info
|
||||||
|
// Error only considers error messages.
|
||||||
|
Error
|
||||||
|
)
|
||||||
|
|
||||||
|
// StdLogger is the subset of the Go stdlib log.Logger API that is needed for
|
||||||
|
// this adapter.
|
||||||
|
type StdLogger interface {
|
||||||
|
// Output is the same as log.Output and log.Logger.Output.
|
||||||
|
Output(calldepth int, logline string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type logger struct {
|
||||||
|
funcr.Formatter
|
||||||
|
std StdLogger
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ logr.LogSink = &logger{}
|
||||||
|
var _ logr.CallDepthLogSink = &logger{}
|
||||||
|
|
||||||
|
func (l logger) Enabled(level int) bool {
|
||||||
|
return globalVerbosity >= level
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l logger) Info(level int, msg string, kvList ...interface{}) {
|
||||||
|
prefix, args := l.FormatInfo(level, msg, kvList)
|
||||||
|
if prefix != "" {
|
||||||
|
args = prefix + ": " + args
|
||||||
|
}
|
||||||
|
_ = l.std.Output(l.Formatter.GetDepth()+1, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l logger) Error(err error, msg string, kvList ...interface{}) {
|
||||||
|
prefix, args := l.FormatError(err, msg, kvList)
|
||||||
|
if prefix != "" {
|
||||||
|
args = prefix + ": " + args
|
||||||
|
}
|
||||||
|
_ = l.std.Output(l.Formatter.GetDepth()+1, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l logger) WithName(name string) logr.LogSink {
|
||||||
|
l.Formatter.AddName(name)
|
||||||
|
return &l
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l logger) WithValues(kvList ...interface{}) logr.LogSink {
|
||||||
|
l.Formatter.AddValues(kvList)
|
||||||
|
return &l
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l logger) WithCallDepth(depth int) logr.LogSink {
|
||||||
|
l.Formatter.AddCallDepth(depth)
|
||||||
|
return &l
|
||||||
|
}
|
||||||
|
|
||||||
|
// Underlier exposes access to the underlying logging implementation. Since
|
||||||
|
// callers only have a logr.Logger, they have to know which implementation is
|
||||||
|
// in use, so this interface is less of an abstraction and more of way to test
|
||||||
|
// type conversion.
|
||||||
|
type Underlier interface {
|
||||||
|
GetUnderlying() StdLogger
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUnderlying returns the StdLogger underneath this logger. Since StdLogger
|
||||||
|
// is itself an interface, the result may or may not be a Go log.Logger.
|
||||||
|
func (l logger) GetUnderlying() StdLogger {
|
||||||
|
return l.std
|
||||||
|
}
|
||||||
324
vendor/github.com/golang/protobuf/proto/buffer.go
generated
vendored
324
vendor/github.com/golang/protobuf/proto/buffer.go
generated
vendored
@@ -1,324 +0,0 @@
|
|||||||
// Copyright 2019 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package proto
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"google.golang.org/protobuf/encoding/prototext"
|
|
||||||
"google.golang.org/protobuf/encoding/protowire"
|
|
||||||
"google.golang.org/protobuf/runtime/protoimpl"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
WireVarint = 0
|
|
||||||
WireFixed32 = 5
|
|
||||||
WireFixed64 = 1
|
|
||||||
WireBytes = 2
|
|
||||||
WireStartGroup = 3
|
|
||||||
WireEndGroup = 4
|
|
||||||
)
|
|
||||||
|
|
||||||
// EncodeVarint returns the varint encoded bytes of v.
|
|
||||||
func EncodeVarint(v uint64) []byte {
|
|
||||||
return protowire.AppendVarint(nil, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SizeVarint returns the length of the varint encoded bytes of v.
|
|
||||||
// This is equal to len(EncodeVarint(v)).
|
|
||||||
func SizeVarint(v uint64) int {
|
|
||||||
return protowire.SizeVarint(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeVarint parses a varint encoded integer from b,
|
|
||||||
// returning the integer value and the length of the varint.
|
|
||||||
// It returns (0, 0) if there is a parse error.
|
|
||||||
func DecodeVarint(b []byte) (uint64, int) {
|
|
||||||
v, n := protowire.ConsumeVarint(b)
|
|
||||||
if n < 0 {
|
|
||||||
return 0, 0
|
|
||||||
}
|
|
||||||
return v, n
|
|
||||||
}
|
|
||||||
|
|
||||||
// Buffer is a buffer for encoding and decoding the protobuf wire format.
|
|
||||||
// It may be reused between invocations to reduce memory usage.
|
|
||||||
type Buffer struct {
|
|
||||||
buf []byte
|
|
||||||
idx int
|
|
||||||
deterministic bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewBuffer allocates a new Buffer initialized with buf,
|
|
||||||
// where the contents of buf are considered the unread portion of the buffer.
|
|
||||||
func NewBuffer(buf []byte) *Buffer {
|
|
||||||
return &Buffer{buf: buf}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetDeterministic specifies whether to use deterministic serialization.
|
|
||||||
//
|
|
||||||
// Deterministic serialization guarantees that for a given binary, equal
|
|
||||||
// messages will always be serialized to the same bytes. This implies:
|
|
||||||
//
|
|
||||||
// - Repeated serialization of a message will return the same bytes.
|
|
||||||
// - Different processes of the same binary (which may be executing on
|
|
||||||
// different machines) will serialize equal messages to the same bytes.
|
|
||||||
//
|
|
||||||
// Note that the deterministic serialization is NOT canonical across
|
|
||||||
// languages. It is not guaranteed to remain stable over time. It is unstable
|
|
||||||
// across different builds with schema changes due to unknown fields.
|
|
||||||
// Users who need canonical serialization (e.g., persistent storage in a
|
|
||||||
// canonical form, fingerprinting, etc.) should define their own
|
|
||||||
// canonicalization specification and implement their own serializer rather
|
|
||||||
// than relying on this API.
|
|
||||||
//
|
|
||||||
// If deterministic serialization is requested, map entries will be sorted
|
|
||||||
// by keys in lexographical order. This is an implementation detail and
|
|
||||||
// subject to change.
|
|
||||||
func (b *Buffer) SetDeterministic(deterministic bool) {
|
|
||||||
b.deterministic = deterministic
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetBuf sets buf as the internal buffer,
|
|
||||||
// where the contents of buf are considered the unread portion of the buffer.
|
|
||||||
func (b *Buffer) SetBuf(buf []byte) {
|
|
||||||
b.buf = buf
|
|
||||||
b.idx = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset clears the internal buffer of all written and unread data.
|
|
||||||
func (b *Buffer) Reset() {
|
|
||||||
b.buf = b.buf[:0]
|
|
||||||
b.idx = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bytes returns the internal buffer.
|
|
||||||
func (b *Buffer) Bytes() []byte {
|
|
||||||
return b.buf
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unread returns the unread portion of the buffer.
|
|
||||||
func (b *Buffer) Unread() []byte {
|
|
||||||
return b.buf[b.idx:]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Marshal appends the wire-format encoding of m to the buffer.
|
|
||||||
func (b *Buffer) Marshal(m Message) error {
|
|
||||||
var err error
|
|
||||||
b.buf, err = marshalAppend(b.buf, m, b.deterministic)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unmarshal parses the wire-format message in the buffer and
|
|
||||||
// places the decoded results in m.
|
|
||||||
// It does not reset m before unmarshaling.
|
|
||||||
func (b *Buffer) Unmarshal(m Message) error {
|
|
||||||
err := UnmarshalMerge(b.Unread(), m)
|
|
||||||
b.idx = len(b.buf)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
type unknownFields struct{ XXX_unrecognized protoimpl.UnknownFields }
|
|
||||||
|
|
||||||
func (m *unknownFields) String() string { panic("not implemented") }
|
|
||||||
func (m *unknownFields) Reset() { panic("not implemented") }
|
|
||||||
func (m *unknownFields) ProtoMessage() { panic("not implemented") }
|
|
||||||
|
|
||||||
// DebugPrint dumps the encoded bytes of b with a header and footer including s
|
|
||||||
// to stdout. This is only intended for debugging.
|
|
||||||
func (*Buffer) DebugPrint(s string, b []byte) {
|
|
||||||
m := MessageReflect(new(unknownFields))
|
|
||||||
m.SetUnknown(b)
|
|
||||||
b, _ = prototext.MarshalOptions{AllowPartial: true, Indent: "\t"}.Marshal(m.Interface())
|
|
||||||
fmt.Printf("==== %s ====\n%s==== %s ====\n", s, b, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// EncodeVarint appends an unsigned varint encoding to the buffer.
|
|
||||||
func (b *Buffer) EncodeVarint(v uint64) error {
|
|
||||||
b.buf = protowire.AppendVarint(b.buf, v)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// EncodeZigzag32 appends a 32-bit zig-zag varint encoding to the buffer.
|
|
||||||
func (b *Buffer) EncodeZigzag32(v uint64) error {
|
|
||||||
return b.EncodeVarint(uint64((uint32(v) << 1) ^ uint32((int32(v) >> 31))))
|
|
||||||
}
|
|
||||||
|
|
||||||
// EncodeZigzag64 appends a 64-bit zig-zag varint encoding to the buffer.
|
|
||||||
func (b *Buffer) EncodeZigzag64(v uint64) error {
|
|
||||||
return b.EncodeVarint(uint64((uint64(v) << 1) ^ uint64((int64(v) >> 63))))
|
|
||||||
}
|
|
||||||
|
|
||||||
// EncodeFixed32 appends a 32-bit little-endian integer to the buffer.
|
|
||||||
func (b *Buffer) EncodeFixed32(v uint64) error {
|
|
||||||
b.buf = protowire.AppendFixed32(b.buf, uint32(v))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// EncodeFixed64 appends a 64-bit little-endian integer to the buffer.
|
|
||||||
func (b *Buffer) EncodeFixed64(v uint64) error {
|
|
||||||
b.buf = protowire.AppendFixed64(b.buf, uint64(v))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// EncodeRawBytes appends a length-prefixed raw bytes to the buffer.
|
|
||||||
func (b *Buffer) EncodeRawBytes(v []byte) error {
|
|
||||||
b.buf = protowire.AppendBytes(b.buf, v)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// EncodeStringBytes appends a length-prefixed raw bytes to the buffer.
|
|
||||||
// It does not validate whether v contains valid UTF-8.
|
|
||||||
func (b *Buffer) EncodeStringBytes(v string) error {
|
|
||||||
b.buf = protowire.AppendString(b.buf, v)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// EncodeMessage appends a length-prefixed encoded message to the buffer.
|
|
||||||
func (b *Buffer) EncodeMessage(m Message) error {
|
|
||||||
var err error
|
|
||||||
b.buf = protowire.AppendVarint(b.buf, uint64(Size(m)))
|
|
||||||
b.buf, err = marshalAppend(b.buf, m, b.deterministic)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeVarint consumes an encoded unsigned varint from the buffer.
|
|
||||||
func (b *Buffer) DecodeVarint() (uint64, error) {
|
|
||||||
v, n := protowire.ConsumeVarint(b.buf[b.idx:])
|
|
||||||
if n < 0 {
|
|
||||||
return 0, protowire.ParseError(n)
|
|
||||||
}
|
|
||||||
b.idx += n
|
|
||||||
return uint64(v), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeZigzag32 consumes an encoded 32-bit zig-zag varint from the buffer.
|
|
||||||
func (b *Buffer) DecodeZigzag32() (uint64, error) {
|
|
||||||
v, err := b.DecodeVarint()
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return uint64((uint32(v) >> 1) ^ uint32((int32(v&1)<<31)>>31)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeZigzag64 consumes an encoded 64-bit zig-zag varint from the buffer.
|
|
||||||
func (b *Buffer) DecodeZigzag64() (uint64, error) {
|
|
||||||
v, err := b.DecodeVarint()
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return uint64((uint64(v) >> 1) ^ uint64((int64(v&1)<<63)>>63)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeFixed32 consumes a 32-bit little-endian integer from the buffer.
|
|
||||||
func (b *Buffer) DecodeFixed32() (uint64, error) {
|
|
||||||
v, n := protowire.ConsumeFixed32(b.buf[b.idx:])
|
|
||||||
if n < 0 {
|
|
||||||
return 0, protowire.ParseError(n)
|
|
||||||
}
|
|
||||||
b.idx += n
|
|
||||||
return uint64(v), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeFixed64 consumes a 64-bit little-endian integer from the buffer.
|
|
||||||
func (b *Buffer) DecodeFixed64() (uint64, error) {
|
|
||||||
v, n := protowire.ConsumeFixed64(b.buf[b.idx:])
|
|
||||||
if n < 0 {
|
|
||||||
return 0, protowire.ParseError(n)
|
|
||||||
}
|
|
||||||
b.idx += n
|
|
||||||
return uint64(v), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeRawBytes consumes a length-prefixed raw bytes from the buffer.
|
|
||||||
// If alloc is specified, it returns a copy the raw bytes
|
|
||||||
// rather than a sub-slice of the buffer.
|
|
||||||
func (b *Buffer) DecodeRawBytes(alloc bool) ([]byte, error) {
|
|
||||||
v, n := protowire.ConsumeBytes(b.buf[b.idx:])
|
|
||||||
if n < 0 {
|
|
||||||
return nil, protowire.ParseError(n)
|
|
||||||
}
|
|
||||||
b.idx += n
|
|
||||||
if alloc {
|
|
||||||
v = append([]byte(nil), v...)
|
|
||||||
}
|
|
||||||
return v, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeStringBytes consumes a length-prefixed raw bytes from the buffer.
|
|
||||||
// It does not validate whether the raw bytes contain valid UTF-8.
|
|
||||||
func (b *Buffer) DecodeStringBytes() (string, error) {
|
|
||||||
v, n := protowire.ConsumeString(b.buf[b.idx:])
|
|
||||||
if n < 0 {
|
|
||||||
return "", protowire.ParseError(n)
|
|
||||||
}
|
|
||||||
b.idx += n
|
|
||||||
return v, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeMessage consumes a length-prefixed message from the buffer.
|
|
||||||
// It does not reset m before unmarshaling.
|
|
||||||
func (b *Buffer) DecodeMessage(m Message) error {
|
|
||||||
v, err := b.DecodeRawBytes(false)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return UnmarshalMerge(v, m)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeGroup consumes a message group from the buffer.
|
|
||||||
// It assumes that the start group marker has already been consumed and
|
|
||||||
// consumes all bytes until (and including the end group marker).
|
|
||||||
// It does not reset m before unmarshaling.
|
|
||||||
func (b *Buffer) DecodeGroup(m Message) error {
|
|
||||||
v, n, err := consumeGroup(b.buf[b.idx:])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
b.idx += n
|
|
||||||
return UnmarshalMerge(v, m)
|
|
||||||
}
|
|
||||||
|
|
||||||
// consumeGroup parses b until it finds an end group marker, returning
|
|
||||||
// the raw bytes of the message (excluding the end group marker) and the
|
|
||||||
// the total length of the message (including the end group marker).
|
|
||||||
func consumeGroup(b []byte) ([]byte, int, error) {
|
|
||||||
b0 := b
|
|
||||||
depth := 1 // assume this follows a start group marker
|
|
||||||
for {
|
|
||||||
_, wtyp, tagLen := protowire.ConsumeTag(b)
|
|
||||||
if tagLen < 0 {
|
|
||||||
return nil, 0, protowire.ParseError(tagLen)
|
|
||||||
}
|
|
||||||
b = b[tagLen:]
|
|
||||||
|
|
||||||
var valLen int
|
|
||||||
switch wtyp {
|
|
||||||
case protowire.VarintType:
|
|
||||||
_, valLen = protowire.ConsumeVarint(b)
|
|
||||||
case protowire.Fixed32Type:
|
|
||||||
_, valLen = protowire.ConsumeFixed32(b)
|
|
||||||
case protowire.Fixed64Type:
|
|
||||||
_, valLen = protowire.ConsumeFixed64(b)
|
|
||||||
case protowire.BytesType:
|
|
||||||
_, valLen = protowire.ConsumeBytes(b)
|
|
||||||
case protowire.StartGroupType:
|
|
||||||
depth++
|
|
||||||
case protowire.EndGroupType:
|
|
||||||
depth--
|
|
||||||
default:
|
|
||||||
return nil, 0, errors.New("proto: cannot parse reserved wire type")
|
|
||||||
}
|
|
||||||
if valLen < 0 {
|
|
||||||
return nil, 0, protowire.ParseError(valLen)
|
|
||||||
}
|
|
||||||
b = b[valLen:]
|
|
||||||
|
|
||||||
if depth == 0 {
|
|
||||||
return b0[:len(b0)-len(b)-tagLen], len(b0) - len(b), nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
63
vendor/github.com/golang/protobuf/proto/defaults.go
generated
vendored
63
vendor/github.com/golang/protobuf/proto/defaults.go
generated
vendored
@@ -1,63 +0,0 @@
|
|||||||
// Copyright 2019 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package proto
|
|
||||||
|
|
||||||
import (
|
|
||||||
"google.golang.org/protobuf/reflect/protoreflect"
|
|
||||||
)
|
|
||||||
|
|
||||||
// SetDefaults sets unpopulated scalar fields to their default values.
|
|
||||||
// Fields within a oneof are not set even if they have a default value.
|
|
||||||
// SetDefaults is recursively called upon any populated message fields.
|
|
||||||
func SetDefaults(m Message) {
|
|
||||||
if m != nil {
|
|
||||||
setDefaults(MessageReflect(m))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func setDefaults(m protoreflect.Message) {
|
|
||||||
fds := m.Descriptor().Fields()
|
|
||||||
for i := 0; i < fds.Len(); i++ {
|
|
||||||
fd := fds.Get(i)
|
|
||||||
if !m.Has(fd) {
|
|
||||||
if fd.HasDefault() && fd.ContainingOneof() == nil {
|
|
||||||
v := fd.Default()
|
|
||||||
if fd.Kind() == protoreflect.BytesKind {
|
|
||||||
v = protoreflect.ValueOf(append([]byte(nil), v.Bytes()...)) // copy the default bytes
|
|
||||||
}
|
|
||||||
m.Set(fd, v)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
|
|
||||||
switch {
|
|
||||||
// Handle singular message.
|
|
||||||
case fd.Cardinality() != protoreflect.Repeated:
|
|
||||||
if fd.Message() != nil {
|
|
||||||
setDefaults(m.Get(fd).Message())
|
|
||||||
}
|
|
||||||
// Handle list of messages.
|
|
||||||
case fd.IsList():
|
|
||||||
if fd.Message() != nil {
|
|
||||||
ls := m.Get(fd).List()
|
|
||||||
for i := 0; i < ls.Len(); i++ {
|
|
||||||
setDefaults(ls.Get(i).Message())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Handle map of messages.
|
|
||||||
case fd.IsMap():
|
|
||||||
if fd.MapValue().Message() != nil {
|
|
||||||
ms := m.Get(fd).Map()
|
|
||||||
ms.Range(func(_ protoreflect.MapKey, v protoreflect.Value) bool {
|
|
||||||
setDefaults(v.Message())
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
113
vendor/github.com/golang/protobuf/proto/deprecated.go
generated
vendored
113
vendor/github.com/golang/protobuf/proto/deprecated.go
generated
vendored
@@ -1,113 +0,0 @@
|
|||||||
// Copyright 2018 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package proto
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
protoV2 "google.golang.org/protobuf/proto"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// Deprecated: No longer returned.
|
|
||||||
ErrNil = errors.New("proto: Marshal called with nil")
|
|
||||||
|
|
||||||
// Deprecated: No longer returned.
|
|
||||||
ErrTooLarge = errors.New("proto: message encodes to over 2 GB")
|
|
||||||
|
|
||||||
// Deprecated: No longer returned.
|
|
||||||
ErrInternalBadWireType = errors.New("proto: internal error: bad wiretype for oneof")
|
|
||||||
)
|
|
||||||
|
|
||||||
// Deprecated: Do not use.
|
|
||||||
type Stats struct{ Emalloc, Dmalloc, Encode, Decode, Chit, Cmiss, Size uint64 }
|
|
||||||
|
|
||||||
// Deprecated: Do not use.
|
|
||||||
func GetStats() Stats { return Stats{} }
|
|
||||||
|
|
||||||
// Deprecated: Do not use.
|
|
||||||
func MarshalMessageSet(interface{}) ([]byte, error) {
|
|
||||||
return nil, errors.New("proto: not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Do not use.
|
|
||||||
func UnmarshalMessageSet([]byte, interface{}) error {
|
|
||||||
return errors.New("proto: not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Do not use.
|
|
||||||
func MarshalMessageSetJSON(interface{}) ([]byte, error) {
|
|
||||||
return nil, errors.New("proto: not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Do not use.
|
|
||||||
func UnmarshalMessageSetJSON([]byte, interface{}) error {
|
|
||||||
return errors.New("proto: not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Do not use.
|
|
||||||
func RegisterMessageSetType(Message, int32, string) {}
|
|
||||||
|
|
||||||
// Deprecated: Do not use.
|
|
||||||
func EnumName(m map[int32]string, v int32) string {
|
|
||||||
s, ok := m[v]
|
|
||||||
if ok {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
return strconv.Itoa(int(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Do not use.
|
|
||||||
func UnmarshalJSONEnum(m map[string]int32, data []byte, enumName string) (int32, error) {
|
|
||||||
if data[0] == '"' {
|
|
||||||
// New style: enums are strings.
|
|
||||||
var repr string
|
|
||||||
if err := json.Unmarshal(data, &repr); err != nil {
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
val, ok := m[repr]
|
|
||||||
if !ok {
|
|
||||||
return 0, fmt.Errorf("unrecognized enum %s value %q", enumName, repr)
|
|
||||||
}
|
|
||||||
return val, nil
|
|
||||||
}
|
|
||||||
// Old style: enums are ints.
|
|
||||||
var val int32
|
|
||||||
if err := json.Unmarshal(data, &val); err != nil {
|
|
||||||
return 0, fmt.Errorf("cannot unmarshal %#q into enum %s", data, enumName)
|
|
||||||
}
|
|
||||||
return val, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Do not use; this type existed for intenal-use only.
|
|
||||||
type InternalMessageInfo struct{}
|
|
||||||
|
|
||||||
// Deprecated: Do not use; this method existed for intenal-use only.
|
|
||||||
func (*InternalMessageInfo) DiscardUnknown(m Message) {
|
|
||||||
DiscardUnknown(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Do not use; this method existed for intenal-use only.
|
|
||||||
func (*InternalMessageInfo) Marshal(b []byte, m Message, deterministic bool) ([]byte, error) {
|
|
||||||
return protoV2.MarshalOptions{Deterministic: deterministic}.MarshalAppend(b, MessageV2(m))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Do not use; this method existed for intenal-use only.
|
|
||||||
func (*InternalMessageInfo) Merge(dst, src Message) {
|
|
||||||
protoV2.Merge(MessageV2(dst), MessageV2(src))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Do not use; this method existed for intenal-use only.
|
|
||||||
func (*InternalMessageInfo) Size(m Message) int {
|
|
||||||
return protoV2.Size(MessageV2(m))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Do not use; this method existed for intenal-use only.
|
|
||||||
func (*InternalMessageInfo) Unmarshal(m Message, b []byte) error {
|
|
||||||
return protoV2.UnmarshalOptions{Merge: true}.Unmarshal(b, MessageV2(m))
|
|
||||||
}
|
|
||||||
58
vendor/github.com/golang/protobuf/proto/discard.go
generated
vendored
58
vendor/github.com/golang/protobuf/proto/discard.go
generated
vendored
@@ -1,58 +0,0 @@
|
|||||||
// Copyright 2019 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package proto
|
|
||||||
|
|
||||||
import (
|
|
||||||
"google.golang.org/protobuf/reflect/protoreflect"
|
|
||||||
)
|
|
||||||
|
|
||||||
// DiscardUnknown recursively discards all unknown fields from this message
|
|
||||||
// and all embedded messages.
|
|
||||||
//
|
|
||||||
// When unmarshaling a message with unrecognized fields, the tags and values
|
|
||||||
// of such fields are preserved in the Message. This allows a later call to
|
|
||||||
// marshal to be able to produce a message that continues to have those
|
|
||||||
// unrecognized fields. To avoid this, DiscardUnknown is used to
|
|
||||||
// explicitly clear the unknown fields after unmarshaling.
|
|
||||||
func DiscardUnknown(m Message) {
|
|
||||||
if m != nil {
|
|
||||||
discardUnknown(MessageReflect(m))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func discardUnknown(m protoreflect.Message) {
|
|
||||||
m.Range(func(fd protoreflect.FieldDescriptor, val protoreflect.Value) bool {
|
|
||||||
switch {
|
|
||||||
// Handle singular message.
|
|
||||||
case fd.Cardinality() != protoreflect.Repeated:
|
|
||||||
if fd.Message() != nil {
|
|
||||||
discardUnknown(m.Get(fd).Message())
|
|
||||||
}
|
|
||||||
// Handle list of messages.
|
|
||||||
case fd.IsList():
|
|
||||||
if fd.Message() != nil {
|
|
||||||
ls := m.Get(fd).List()
|
|
||||||
for i := 0; i < ls.Len(); i++ {
|
|
||||||
discardUnknown(ls.Get(i).Message())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Handle map of messages.
|
|
||||||
case fd.IsMap():
|
|
||||||
if fd.MapValue().Message() != nil {
|
|
||||||
ms := m.Get(fd).Map()
|
|
||||||
ms.Range(func(_ protoreflect.MapKey, v protoreflect.Value) bool {
|
|
||||||
discardUnknown(v.Message())
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
|
|
||||||
// Discard unknown fields.
|
|
||||||
if len(m.GetUnknown()) > 0 {
|
|
||||||
m.SetUnknown(nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
356
vendor/github.com/golang/protobuf/proto/extensions.go
generated
vendored
356
vendor/github.com/golang/protobuf/proto/extensions.go
generated
vendored
@@ -1,356 +0,0 @@
|
|||||||
// Copyright 2010 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package proto
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
|
|
||||||
"google.golang.org/protobuf/encoding/protowire"
|
|
||||||
"google.golang.org/protobuf/proto"
|
|
||||||
"google.golang.org/protobuf/reflect/protoreflect"
|
|
||||||
"google.golang.org/protobuf/reflect/protoregistry"
|
|
||||||
"google.golang.org/protobuf/runtime/protoiface"
|
|
||||||
"google.golang.org/protobuf/runtime/protoimpl"
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
|
||||||
// ExtensionDesc represents an extension descriptor and
|
|
||||||
// is used to interact with an extension field in a message.
|
|
||||||
//
|
|
||||||
// Variables of this type are generated in code by protoc-gen-go.
|
|
||||||
ExtensionDesc = protoimpl.ExtensionInfo
|
|
||||||
|
|
||||||
// ExtensionRange represents a range of message extensions.
|
|
||||||
// Used in code generated by protoc-gen-go.
|
|
||||||
ExtensionRange = protoiface.ExtensionRangeV1
|
|
||||||
|
|
||||||
// Deprecated: Do not use; this is an internal type.
|
|
||||||
Extension = protoimpl.ExtensionFieldV1
|
|
||||||
|
|
||||||
// Deprecated: Do not use; this is an internal type.
|
|
||||||
XXX_InternalExtensions = protoimpl.ExtensionFields
|
|
||||||
)
|
|
||||||
|
|
||||||
// ErrMissingExtension reports whether the extension was not present.
|
|
||||||
var ErrMissingExtension = errors.New("proto: missing extension")
|
|
||||||
|
|
||||||
var errNotExtendable = errors.New("proto: not an extendable proto.Message")
|
|
||||||
|
|
||||||
// HasExtension reports whether the extension field is present in m
|
|
||||||
// either as an explicitly populated field or as an unknown field.
|
|
||||||
func HasExtension(m Message, xt *ExtensionDesc) (has bool) {
|
|
||||||
mr := MessageReflect(m)
|
|
||||||
if mr == nil || !mr.IsValid() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check whether any populated known field matches the field number.
|
|
||||||
xtd := xt.TypeDescriptor()
|
|
||||||
if isValidExtension(mr.Descriptor(), xtd) {
|
|
||||||
has = mr.Has(xtd)
|
|
||||||
} else {
|
|
||||||
mr.Range(func(fd protoreflect.FieldDescriptor, _ protoreflect.Value) bool {
|
|
||||||
has = int32(fd.Number()) == xt.Field
|
|
||||||
return !has
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check whether any unknown field matches the field number.
|
|
||||||
for b := mr.GetUnknown(); !has && len(b) > 0; {
|
|
||||||
num, _, n := protowire.ConsumeField(b)
|
|
||||||
has = int32(num) == xt.Field
|
|
||||||
b = b[n:]
|
|
||||||
}
|
|
||||||
return has
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClearExtension removes the extension field from m
|
|
||||||
// either as an explicitly populated field or as an unknown field.
|
|
||||||
func ClearExtension(m Message, xt *ExtensionDesc) {
|
|
||||||
mr := MessageReflect(m)
|
|
||||||
if mr == nil || !mr.IsValid() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
xtd := xt.TypeDescriptor()
|
|
||||||
if isValidExtension(mr.Descriptor(), xtd) {
|
|
||||||
mr.Clear(xtd)
|
|
||||||
} else {
|
|
||||||
mr.Range(func(fd protoreflect.FieldDescriptor, _ protoreflect.Value) bool {
|
|
||||||
if int32(fd.Number()) == xt.Field {
|
|
||||||
mr.Clear(fd)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
clearUnknown(mr, fieldNum(xt.Field))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClearAllExtensions clears all extensions from m.
|
|
||||||
// This includes populated fields and unknown fields in the extension range.
|
|
||||||
func ClearAllExtensions(m Message) {
|
|
||||||
mr := MessageReflect(m)
|
|
||||||
if mr == nil || !mr.IsValid() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
mr.Range(func(fd protoreflect.FieldDescriptor, _ protoreflect.Value) bool {
|
|
||||||
if fd.IsExtension() {
|
|
||||||
mr.Clear(fd)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
clearUnknown(mr, mr.Descriptor().ExtensionRanges())
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetExtension retrieves a proto2 extended field from m.
|
|
||||||
//
|
|
||||||
// If the descriptor is type complete (i.e., ExtensionDesc.ExtensionType is non-nil),
|
|
||||||
// then GetExtension parses the encoded field and returns a Go value of the specified type.
|
|
||||||
// If the field is not present, then the default value is returned (if one is specified),
|
|
||||||
// otherwise ErrMissingExtension is reported.
|
|
||||||
//
|
|
||||||
// If the descriptor is type incomplete (i.e., ExtensionDesc.ExtensionType is nil),
|
|
||||||
// then GetExtension returns the raw encoded bytes for the extension field.
|
|
||||||
func GetExtension(m Message, xt *ExtensionDesc) (interface{}, error) {
|
|
||||||
mr := MessageReflect(m)
|
|
||||||
if mr == nil || !mr.IsValid() || mr.Descriptor().ExtensionRanges().Len() == 0 {
|
|
||||||
return nil, errNotExtendable
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieve the unknown fields for this extension field.
|
|
||||||
var bo protoreflect.RawFields
|
|
||||||
for bi := mr.GetUnknown(); len(bi) > 0; {
|
|
||||||
num, _, n := protowire.ConsumeField(bi)
|
|
||||||
if int32(num) == xt.Field {
|
|
||||||
bo = append(bo, bi[:n]...)
|
|
||||||
}
|
|
||||||
bi = bi[n:]
|
|
||||||
}
|
|
||||||
|
|
||||||
// For type incomplete descriptors, only retrieve the unknown fields.
|
|
||||||
if xt.ExtensionType == nil {
|
|
||||||
return []byte(bo), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the extension field only exists as unknown fields, unmarshal it.
|
|
||||||
// This is rarely done since proto.Unmarshal eagerly unmarshals extensions.
|
|
||||||
xtd := xt.TypeDescriptor()
|
|
||||||
if !isValidExtension(mr.Descriptor(), xtd) {
|
|
||||||
return nil, fmt.Errorf("proto: bad extended type; %T does not extend %T", xt.ExtendedType, m)
|
|
||||||
}
|
|
||||||
if !mr.Has(xtd) && len(bo) > 0 {
|
|
||||||
m2 := mr.New()
|
|
||||||
if err := (proto.UnmarshalOptions{
|
|
||||||
Resolver: extensionResolver{xt},
|
|
||||||
}.Unmarshal(bo, m2.Interface())); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if m2.Has(xtd) {
|
|
||||||
mr.Set(xtd, m2.Get(xtd))
|
|
||||||
clearUnknown(mr, fieldNum(xt.Field))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check whether the message has the extension field set or a default.
|
|
||||||
var pv protoreflect.Value
|
|
||||||
switch {
|
|
||||||
case mr.Has(xtd):
|
|
||||||
pv = mr.Get(xtd)
|
|
||||||
case xtd.HasDefault():
|
|
||||||
pv = xtd.Default()
|
|
||||||
default:
|
|
||||||
return nil, ErrMissingExtension
|
|
||||||
}
|
|
||||||
|
|
||||||
v := xt.InterfaceOf(pv)
|
|
||||||
rv := reflect.ValueOf(v)
|
|
||||||
if isScalarKind(rv.Kind()) {
|
|
||||||
rv2 := reflect.New(rv.Type())
|
|
||||||
rv2.Elem().Set(rv)
|
|
||||||
v = rv2.Interface()
|
|
||||||
}
|
|
||||||
return v, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// extensionResolver is a custom extension resolver that stores a single
|
|
||||||
// extension type that takes precedence over the global registry.
|
|
||||||
type extensionResolver struct{ xt protoreflect.ExtensionType }
|
|
||||||
|
|
||||||
func (r extensionResolver) FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error) {
|
|
||||||
if xtd := r.xt.TypeDescriptor(); xtd.FullName() == field {
|
|
||||||
return r.xt, nil
|
|
||||||
}
|
|
||||||
return protoregistry.GlobalTypes.FindExtensionByName(field)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r extensionResolver) FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error) {
|
|
||||||
if xtd := r.xt.TypeDescriptor(); xtd.ContainingMessage().FullName() == message && xtd.Number() == field {
|
|
||||||
return r.xt, nil
|
|
||||||
}
|
|
||||||
return protoregistry.GlobalTypes.FindExtensionByNumber(message, field)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetExtensions returns a list of the extensions values present in m,
|
|
||||||
// corresponding with the provided list of extension descriptors, xts.
|
|
||||||
// If an extension is missing in m, the corresponding value is nil.
|
|
||||||
func GetExtensions(m Message, xts []*ExtensionDesc) ([]interface{}, error) {
|
|
||||||
mr := MessageReflect(m)
|
|
||||||
if mr == nil || !mr.IsValid() {
|
|
||||||
return nil, errNotExtendable
|
|
||||||
}
|
|
||||||
|
|
||||||
vs := make([]interface{}, len(xts))
|
|
||||||
for i, xt := range xts {
|
|
||||||
v, err := GetExtension(m, xt)
|
|
||||||
if err != nil {
|
|
||||||
if err == ErrMissingExtension {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return vs, err
|
|
||||||
}
|
|
||||||
vs[i] = v
|
|
||||||
}
|
|
||||||
return vs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetExtension sets an extension field in m to the provided value.
|
|
||||||
func SetExtension(m Message, xt *ExtensionDesc, v interface{}) error {
|
|
||||||
mr := MessageReflect(m)
|
|
||||||
if mr == nil || !mr.IsValid() || mr.Descriptor().ExtensionRanges().Len() == 0 {
|
|
||||||
return errNotExtendable
|
|
||||||
}
|
|
||||||
|
|
||||||
rv := reflect.ValueOf(v)
|
|
||||||
if reflect.TypeOf(v) != reflect.TypeOf(xt.ExtensionType) {
|
|
||||||
return fmt.Errorf("proto: bad extension value type. got: %T, want: %T", v, xt.ExtensionType)
|
|
||||||
}
|
|
||||||
if rv.Kind() == reflect.Ptr {
|
|
||||||
if rv.IsNil() {
|
|
||||||
return fmt.Errorf("proto: SetExtension called with nil value of type %T", v)
|
|
||||||
}
|
|
||||||
if isScalarKind(rv.Elem().Kind()) {
|
|
||||||
v = rv.Elem().Interface()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
xtd := xt.TypeDescriptor()
|
|
||||||
if !isValidExtension(mr.Descriptor(), xtd) {
|
|
||||||
return fmt.Errorf("proto: bad extended type; %T does not extend %T", xt.ExtendedType, m)
|
|
||||||
}
|
|
||||||
mr.Set(xtd, xt.ValueOf(v))
|
|
||||||
clearUnknown(mr, fieldNum(xt.Field))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetRawExtension inserts b into the unknown fields of m.
|
|
||||||
//
|
|
||||||
// Deprecated: Use Message.ProtoReflect.SetUnknown instead.
|
|
||||||
func SetRawExtension(m Message, fnum int32, b []byte) {
|
|
||||||
mr := MessageReflect(m)
|
|
||||||
if mr == nil || !mr.IsValid() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify that the raw field is valid.
|
|
||||||
for b0 := b; len(b0) > 0; {
|
|
||||||
num, _, n := protowire.ConsumeField(b0)
|
|
||||||
if int32(num) != fnum {
|
|
||||||
panic(fmt.Sprintf("mismatching field number: got %d, want %d", num, fnum))
|
|
||||||
}
|
|
||||||
b0 = b0[n:]
|
|
||||||
}
|
|
||||||
|
|
||||||
ClearExtension(m, &ExtensionDesc{Field: fnum})
|
|
||||||
mr.SetUnknown(append(mr.GetUnknown(), b...))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExtensionDescs returns a list of extension descriptors found in m,
|
|
||||||
// containing descriptors for both populated extension fields in m and
|
|
||||||
// also unknown fields of m that are in the extension range.
|
|
||||||
// For the later case, an type incomplete descriptor is provided where only
|
|
||||||
// the ExtensionDesc.Field field is populated.
|
|
||||||
// The order of the extension descriptors is undefined.
|
|
||||||
func ExtensionDescs(m Message) ([]*ExtensionDesc, error) {
|
|
||||||
mr := MessageReflect(m)
|
|
||||||
if mr == nil || !mr.IsValid() || mr.Descriptor().ExtensionRanges().Len() == 0 {
|
|
||||||
return nil, errNotExtendable
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collect a set of known extension descriptors.
|
|
||||||
extDescs := make(map[protoreflect.FieldNumber]*ExtensionDesc)
|
|
||||||
mr.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
|
|
||||||
if fd.IsExtension() {
|
|
||||||
xt := fd.(protoreflect.ExtensionTypeDescriptor)
|
|
||||||
if xd, ok := xt.Type().(*ExtensionDesc); ok {
|
|
||||||
extDescs[fd.Number()] = xd
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
|
|
||||||
// Collect a set of unknown extension descriptors.
|
|
||||||
extRanges := mr.Descriptor().ExtensionRanges()
|
|
||||||
for b := mr.GetUnknown(); len(b) > 0; {
|
|
||||||
num, _, n := protowire.ConsumeField(b)
|
|
||||||
if extRanges.Has(num) && extDescs[num] == nil {
|
|
||||||
extDescs[num] = nil
|
|
||||||
}
|
|
||||||
b = b[n:]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transpose the set of descriptors into a list.
|
|
||||||
var xts []*ExtensionDesc
|
|
||||||
for num, xt := range extDescs {
|
|
||||||
if xt == nil {
|
|
||||||
xt = &ExtensionDesc{Field: int32(num)}
|
|
||||||
}
|
|
||||||
xts = append(xts, xt)
|
|
||||||
}
|
|
||||||
return xts, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// isValidExtension reports whether xtd is a valid extension descriptor for md.
|
|
||||||
func isValidExtension(md protoreflect.MessageDescriptor, xtd protoreflect.ExtensionTypeDescriptor) bool {
|
|
||||||
return xtd.ContainingMessage() == md && md.ExtensionRanges().Has(xtd.Number())
|
|
||||||
}
|
|
||||||
|
|
||||||
// isScalarKind reports whether k is a protobuf scalar kind (except bytes).
|
|
||||||
// This function exists for historical reasons since the representation of
|
|
||||||
// scalars differs between v1 and v2, where v1 uses *T and v2 uses T.
|
|
||||||
func isScalarKind(k reflect.Kind) bool {
|
|
||||||
switch k {
|
|
||||||
case reflect.Bool, reflect.Int32, reflect.Int64, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64, reflect.String:
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// clearUnknown removes unknown fields from m where remover.Has reports true.
|
|
||||||
func clearUnknown(m protoreflect.Message, remover interface {
|
|
||||||
Has(protoreflect.FieldNumber) bool
|
|
||||||
}) {
|
|
||||||
var bo protoreflect.RawFields
|
|
||||||
for bi := m.GetUnknown(); len(bi) > 0; {
|
|
||||||
num, _, n := protowire.ConsumeField(bi)
|
|
||||||
if !remover.Has(num) {
|
|
||||||
bo = append(bo, bi[:n]...)
|
|
||||||
}
|
|
||||||
bi = bi[n:]
|
|
||||||
}
|
|
||||||
if bi := m.GetUnknown(); len(bi) != len(bo) {
|
|
||||||
m.SetUnknown(bo)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type fieldNum protoreflect.FieldNumber
|
|
||||||
|
|
||||||
func (n1 fieldNum) Has(n2 protoreflect.FieldNumber) bool {
|
|
||||||
return protoreflect.FieldNumber(n1) == n2
|
|
||||||
}
|
|
||||||
306
vendor/github.com/golang/protobuf/proto/properties.go
generated
vendored
306
vendor/github.com/golang/protobuf/proto/properties.go
generated
vendored
@@ -1,306 +0,0 @@
|
|||||||
// Copyright 2010 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package proto
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"google.golang.org/protobuf/reflect/protoreflect"
|
|
||||||
"google.golang.org/protobuf/runtime/protoimpl"
|
|
||||||
)
|
|
||||||
|
|
||||||
// StructProperties represents protocol buffer type information for a
|
|
||||||
// generated protobuf message in the open-struct API.
|
|
||||||
//
|
|
||||||
// Deprecated: Do not use.
|
|
||||||
type StructProperties struct {
|
|
||||||
// Prop are the properties for each field.
|
|
||||||
//
|
|
||||||
// Fields belonging to a oneof are stored in OneofTypes instead, with a
|
|
||||||
// single Properties representing the parent oneof held here.
|
|
||||||
//
|
|
||||||
// The order of Prop matches the order of fields in the Go struct.
|
|
||||||
// Struct fields that are not related to protobufs have a "XXX_" prefix
|
|
||||||
// in the Properties.Name and must be ignored by the user.
|
|
||||||
Prop []*Properties
|
|
||||||
|
|
||||||
// OneofTypes contains information about the oneof fields in this message.
|
|
||||||
// It is keyed by the protobuf field name.
|
|
||||||
OneofTypes map[string]*OneofProperties
|
|
||||||
}
|
|
||||||
|
|
||||||
// Properties represents the type information for a protobuf message field.
|
|
||||||
//
|
|
||||||
// Deprecated: Do not use.
|
|
||||||
type Properties struct {
|
|
||||||
// Name is a placeholder name with little meaningful semantic value.
|
|
||||||
// If the name has an "XXX_" prefix, the entire Properties must be ignored.
|
|
||||||
Name string
|
|
||||||
// OrigName is the protobuf field name or oneof name.
|
|
||||||
OrigName string
|
|
||||||
// JSONName is the JSON name for the protobuf field.
|
|
||||||
JSONName string
|
|
||||||
// Enum is a placeholder name for enums.
|
|
||||||
// For historical reasons, this is neither the Go name for the enum,
|
|
||||||
// nor the protobuf name for the enum.
|
|
||||||
Enum string // Deprecated: Do not use.
|
|
||||||
// Weak contains the full name of the weakly referenced message.
|
|
||||||
Weak string
|
|
||||||
// Wire is a string representation of the wire type.
|
|
||||||
Wire string
|
|
||||||
// WireType is the protobuf wire type for the field.
|
|
||||||
WireType int
|
|
||||||
// Tag is the protobuf field number.
|
|
||||||
Tag int
|
|
||||||
// Required reports whether this is a required field.
|
|
||||||
Required bool
|
|
||||||
// Optional reports whether this is a optional field.
|
|
||||||
Optional bool
|
|
||||||
// Repeated reports whether this is a repeated field.
|
|
||||||
Repeated bool
|
|
||||||
// Packed reports whether this is a packed repeated field of scalars.
|
|
||||||
Packed bool
|
|
||||||
// Proto3 reports whether this field operates under the proto3 syntax.
|
|
||||||
Proto3 bool
|
|
||||||
// Oneof reports whether this field belongs within a oneof.
|
|
||||||
Oneof bool
|
|
||||||
|
|
||||||
// Default is the default value in string form.
|
|
||||||
Default string
|
|
||||||
// HasDefault reports whether the field has a default value.
|
|
||||||
HasDefault bool
|
|
||||||
|
|
||||||
// MapKeyProp is the properties for the key field for a map field.
|
|
||||||
MapKeyProp *Properties
|
|
||||||
// MapValProp is the properties for the value field for a map field.
|
|
||||||
MapValProp *Properties
|
|
||||||
}
|
|
||||||
|
|
||||||
// OneofProperties represents the type information for a protobuf oneof.
|
|
||||||
//
|
|
||||||
// Deprecated: Do not use.
|
|
||||||
type OneofProperties struct {
|
|
||||||
// Type is a pointer to the generated wrapper type for the field value.
|
|
||||||
// This is nil for messages that are not in the open-struct API.
|
|
||||||
Type reflect.Type
|
|
||||||
// Field is the index into StructProperties.Prop for the containing oneof.
|
|
||||||
Field int
|
|
||||||
// Prop is the properties for the field.
|
|
||||||
Prop *Properties
|
|
||||||
}
|
|
||||||
|
|
||||||
// String formats the properties in the protobuf struct field tag style.
|
|
||||||
func (p *Properties) String() string {
|
|
||||||
s := p.Wire
|
|
||||||
s += "," + strconv.Itoa(p.Tag)
|
|
||||||
if p.Required {
|
|
||||||
s += ",req"
|
|
||||||
}
|
|
||||||
if p.Optional {
|
|
||||||
s += ",opt"
|
|
||||||
}
|
|
||||||
if p.Repeated {
|
|
||||||
s += ",rep"
|
|
||||||
}
|
|
||||||
if p.Packed {
|
|
||||||
s += ",packed"
|
|
||||||
}
|
|
||||||
s += ",name=" + p.OrigName
|
|
||||||
if p.JSONName != "" {
|
|
||||||
s += ",json=" + p.JSONName
|
|
||||||
}
|
|
||||||
if len(p.Enum) > 0 {
|
|
||||||
s += ",enum=" + p.Enum
|
|
||||||
}
|
|
||||||
if len(p.Weak) > 0 {
|
|
||||||
s += ",weak=" + p.Weak
|
|
||||||
}
|
|
||||||
if p.Proto3 {
|
|
||||||
s += ",proto3"
|
|
||||||
}
|
|
||||||
if p.Oneof {
|
|
||||||
s += ",oneof"
|
|
||||||
}
|
|
||||||
if p.HasDefault {
|
|
||||||
s += ",def=" + p.Default
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse populates p by parsing a string in the protobuf struct field tag style.
|
|
||||||
func (p *Properties) Parse(tag string) {
|
|
||||||
// For example: "bytes,49,opt,name=foo,def=hello!"
|
|
||||||
for len(tag) > 0 {
|
|
||||||
i := strings.IndexByte(tag, ',')
|
|
||||||
if i < 0 {
|
|
||||||
i = len(tag)
|
|
||||||
}
|
|
||||||
switch s := tag[:i]; {
|
|
||||||
case strings.HasPrefix(s, "name="):
|
|
||||||
p.OrigName = s[len("name="):]
|
|
||||||
case strings.HasPrefix(s, "json="):
|
|
||||||
p.JSONName = s[len("json="):]
|
|
||||||
case strings.HasPrefix(s, "enum="):
|
|
||||||
p.Enum = s[len("enum="):]
|
|
||||||
case strings.HasPrefix(s, "weak="):
|
|
||||||
p.Weak = s[len("weak="):]
|
|
||||||
case strings.Trim(s, "0123456789") == "":
|
|
||||||
n, _ := strconv.ParseUint(s, 10, 32)
|
|
||||||
p.Tag = int(n)
|
|
||||||
case s == "opt":
|
|
||||||
p.Optional = true
|
|
||||||
case s == "req":
|
|
||||||
p.Required = true
|
|
||||||
case s == "rep":
|
|
||||||
p.Repeated = true
|
|
||||||
case s == "varint" || s == "zigzag32" || s == "zigzag64":
|
|
||||||
p.Wire = s
|
|
||||||
p.WireType = WireVarint
|
|
||||||
case s == "fixed32":
|
|
||||||
p.Wire = s
|
|
||||||
p.WireType = WireFixed32
|
|
||||||
case s == "fixed64":
|
|
||||||
p.Wire = s
|
|
||||||
p.WireType = WireFixed64
|
|
||||||
case s == "bytes":
|
|
||||||
p.Wire = s
|
|
||||||
p.WireType = WireBytes
|
|
||||||
case s == "group":
|
|
||||||
p.Wire = s
|
|
||||||
p.WireType = WireStartGroup
|
|
||||||
case s == "packed":
|
|
||||||
p.Packed = true
|
|
||||||
case s == "proto3":
|
|
||||||
p.Proto3 = true
|
|
||||||
case s == "oneof":
|
|
||||||
p.Oneof = true
|
|
||||||
case strings.HasPrefix(s, "def="):
|
|
||||||
// The default tag is special in that everything afterwards is the
|
|
||||||
// default regardless of the presence of commas.
|
|
||||||
p.HasDefault = true
|
|
||||||
p.Default, i = tag[len("def="):], len(tag)
|
|
||||||
}
|
|
||||||
tag = strings.TrimPrefix(tag[i:], ",")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init populates the properties from a protocol buffer struct tag.
|
|
||||||
//
|
|
||||||
// Deprecated: Do not use.
|
|
||||||
func (p *Properties) Init(typ reflect.Type, name, tag string, f *reflect.StructField) {
|
|
||||||
p.Name = name
|
|
||||||
p.OrigName = name
|
|
||||||
if tag == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
p.Parse(tag)
|
|
||||||
|
|
||||||
if typ != nil && typ.Kind() == reflect.Map {
|
|
||||||
p.MapKeyProp = new(Properties)
|
|
||||||
p.MapKeyProp.Init(nil, "Key", f.Tag.Get("protobuf_key"), nil)
|
|
||||||
p.MapValProp = new(Properties)
|
|
||||||
p.MapValProp.Init(nil, "Value", f.Tag.Get("protobuf_val"), nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var propertiesCache sync.Map // map[reflect.Type]*StructProperties
|
|
||||||
|
|
||||||
// GetProperties returns the list of properties for the type represented by t,
|
|
||||||
// which must be a generated protocol buffer message in the open-struct API,
|
|
||||||
// where protobuf message fields are represented by exported Go struct fields.
|
|
||||||
//
|
|
||||||
// Deprecated: Use protobuf reflection instead.
|
|
||||||
func GetProperties(t reflect.Type) *StructProperties {
|
|
||||||
if p, ok := propertiesCache.Load(t); ok {
|
|
||||||
return p.(*StructProperties)
|
|
||||||
}
|
|
||||||
p, _ := propertiesCache.LoadOrStore(t, newProperties(t))
|
|
||||||
return p.(*StructProperties)
|
|
||||||
}
|
|
||||||
|
|
||||||
func newProperties(t reflect.Type) *StructProperties {
|
|
||||||
if t.Kind() != reflect.Struct {
|
|
||||||
panic(fmt.Sprintf("%v is not a generated message in the open-struct API", t))
|
|
||||||
}
|
|
||||||
|
|
||||||
var hasOneof bool
|
|
||||||
prop := new(StructProperties)
|
|
||||||
|
|
||||||
// Construct a list of properties for each field in the struct.
|
|
||||||
for i := 0; i < t.NumField(); i++ {
|
|
||||||
p := new(Properties)
|
|
||||||
f := t.Field(i)
|
|
||||||
tagField := f.Tag.Get("protobuf")
|
|
||||||
p.Init(f.Type, f.Name, tagField, &f)
|
|
||||||
|
|
||||||
tagOneof := f.Tag.Get("protobuf_oneof")
|
|
||||||
if tagOneof != "" {
|
|
||||||
hasOneof = true
|
|
||||||
p.OrigName = tagOneof
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rename unrelated struct fields with the "XXX_" prefix since so much
|
|
||||||
// user code simply checks for this to exclude special fields.
|
|
||||||
if tagField == "" && tagOneof == "" && !strings.HasPrefix(p.Name, "XXX_") {
|
|
||||||
p.Name = "XXX_" + p.Name
|
|
||||||
p.OrigName = "XXX_" + p.OrigName
|
|
||||||
} else if p.Weak != "" {
|
|
||||||
p.Name = p.OrigName // avoid possible "XXX_" prefix on weak field
|
|
||||||
}
|
|
||||||
|
|
||||||
prop.Prop = append(prop.Prop, p)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Construct a mapping of oneof field names to properties.
|
|
||||||
if hasOneof {
|
|
||||||
var oneofWrappers []interface{}
|
|
||||||
if fn, ok := reflect.PtrTo(t).MethodByName("XXX_OneofFuncs"); ok {
|
|
||||||
oneofWrappers = fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))})[3].Interface().([]interface{})
|
|
||||||
}
|
|
||||||
if fn, ok := reflect.PtrTo(t).MethodByName("XXX_OneofWrappers"); ok {
|
|
||||||
oneofWrappers = fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))})[0].Interface().([]interface{})
|
|
||||||
}
|
|
||||||
if m, ok := reflect.Zero(reflect.PtrTo(t)).Interface().(protoreflect.ProtoMessage); ok {
|
|
||||||
if m, ok := m.ProtoReflect().(interface{ ProtoMessageInfo() *protoimpl.MessageInfo }); ok {
|
|
||||||
oneofWrappers = m.ProtoMessageInfo().OneofWrappers
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
prop.OneofTypes = make(map[string]*OneofProperties)
|
|
||||||
for _, wrapper := range oneofWrappers {
|
|
||||||
p := &OneofProperties{
|
|
||||||
Type: reflect.ValueOf(wrapper).Type(), // *T
|
|
||||||
Prop: new(Properties),
|
|
||||||
}
|
|
||||||
f := p.Type.Elem().Field(0)
|
|
||||||
p.Prop.Name = f.Name
|
|
||||||
p.Prop.Parse(f.Tag.Get("protobuf"))
|
|
||||||
|
|
||||||
// Determine the struct field that contains this oneof.
|
|
||||||
// Each wrapper is assignable to exactly one parent field.
|
|
||||||
var foundOneof bool
|
|
||||||
for i := 0; i < t.NumField() && !foundOneof; i++ {
|
|
||||||
if p.Type.AssignableTo(t.Field(i).Type) {
|
|
||||||
p.Field = i
|
|
||||||
foundOneof = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !foundOneof {
|
|
||||||
panic(fmt.Sprintf("%v is not a generated message in the open-struct API", t))
|
|
||||||
}
|
|
||||||
prop.OneofTypes[p.Prop.OrigName] = p
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return prop
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sp *StructProperties) Len() int { return len(sp.Prop) }
|
|
||||||
func (sp *StructProperties) Less(i, j int) bool { return false }
|
|
||||||
func (sp *StructProperties) Swap(i, j int) { return }
|
|
||||||
167
vendor/github.com/golang/protobuf/proto/proto.go
generated
vendored
167
vendor/github.com/golang/protobuf/proto/proto.go
generated
vendored
@@ -1,167 +0,0 @@
|
|||||||
// Copyright 2019 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// Package proto provides functionality for handling protocol buffer messages.
|
|
||||||
// In particular, it provides marshaling and unmarshaling between a protobuf
|
|
||||||
// message and the binary wire format.
|
|
||||||
//
|
|
||||||
// See https://developers.google.com/protocol-buffers/docs/gotutorial for
|
|
||||||
// more information.
|
|
||||||
//
|
|
||||||
// Deprecated: Use the "google.golang.org/protobuf/proto" package instead.
|
|
||||||
package proto
|
|
||||||
|
|
||||||
import (
|
|
||||||
protoV2 "google.golang.org/protobuf/proto"
|
|
||||||
"google.golang.org/protobuf/reflect/protoreflect"
|
|
||||||
"google.golang.org/protobuf/runtime/protoiface"
|
|
||||||
"google.golang.org/protobuf/runtime/protoimpl"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
ProtoPackageIsVersion1 = true
|
|
||||||
ProtoPackageIsVersion2 = true
|
|
||||||
ProtoPackageIsVersion3 = true
|
|
||||||
ProtoPackageIsVersion4 = true
|
|
||||||
)
|
|
||||||
|
|
||||||
// GeneratedEnum is any enum type generated by protoc-gen-go
|
|
||||||
// which is a named int32 kind.
|
|
||||||
// This type exists for documentation purposes.
|
|
||||||
type GeneratedEnum interface{}
|
|
||||||
|
|
||||||
// GeneratedMessage is any message type generated by protoc-gen-go
|
|
||||||
// which is a pointer to a named struct kind.
|
|
||||||
// This type exists for documentation purposes.
|
|
||||||
type GeneratedMessage interface{}
|
|
||||||
|
|
||||||
// Message is a protocol buffer message.
|
|
||||||
//
|
|
||||||
// This is the v1 version of the message interface and is marginally better
|
|
||||||
// than an empty interface as it lacks any method to programatically interact
|
|
||||||
// with the contents of the message.
|
|
||||||
//
|
|
||||||
// A v2 message is declared in "google.golang.org/protobuf/proto".Message and
|
|
||||||
// exposes protobuf reflection as a first-class feature of the interface.
|
|
||||||
//
|
|
||||||
// To convert a v1 message to a v2 message, use the MessageV2 function.
|
|
||||||
// To convert a v2 message to a v1 message, use the MessageV1 function.
|
|
||||||
type Message = protoiface.MessageV1
|
|
||||||
|
|
||||||
// MessageV1 converts either a v1 or v2 message to a v1 message.
|
|
||||||
// It returns nil if m is nil.
|
|
||||||
func MessageV1(m GeneratedMessage) protoiface.MessageV1 {
|
|
||||||
return protoimpl.X.ProtoMessageV1Of(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MessageV2 converts either a v1 or v2 message to a v2 message.
|
|
||||||
// It returns nil if m is nil.
|
|
||||||
func MessageV2(m GeneratedMessage) protoV2.Message {
|
|
||||||
return protoimpl.X.ProtoMessageV2Of(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MessageReflect returns a reflective view for a message.
|
|
||||||
// It returns nil if m is nil.
|
|
||||||
func MessageReflect(m Message) protoreflect.Message {
|
|
||||||
return protoimpl.X.MessageOf(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Marshaler is implemented by messages that can marshal themselves.
|
|
||||||
// This interface is used by the following functions: Size, Marshal,
|
|
||||||
// Buffer.Marshal, and Buffer.EncodeMessage.
|
|
||||||
//
|
|
||||||
// Deprecated: Do not implement.
|
|
||||||
type Marshaler interface {
|
|
||||||
// Marshal formats the encoded bytes of the message.
|
|
||||||
// It should be deterministic and emit valid protobuf wire data.
|
|
||||||
// The caller takes ownership of the returned buffer.
|
|
||||||
Marshal() ([]byte, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unmarshaler is implemented by messages that can unmarshal themselves.
|
|
||||||
// This interface is used by the following functions: Unmarshal, UnmarshalMerge,
|
|
||||||
// Buffer.Unmarshal, Buffer.DecodeMessage, and Buffer.DecodeGroup.
|
|
||||||
//
|
|
||||||
// Deprecated: Do not implement.
|
|
||||||
type Unmarshaler interface {
|
|
||||||
// Unmarshal parses the encoded bytes of the protobuf wire input.
|
|
||||||
// The provided buffer is only valid for during method call.
|
|
||||||
// It should not reset the receiver message.
|
|
||||||
Unmarshal([]byte) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Merger is implemented by messages that can merge themselves.
|
|
||||||
// This interface is used by the following functions: Clone and Merge.
|
|
||||||
//
|
|
||||||
// Deprecated: Do not implement.
|
|
||||||
type Merger interface {
|
|
||||||
// Merge merges the contents of src into the receiver message.
|
|
||||||
// It clones all data structures in src such that it aliases no mutable
|
|
||||||
// memory referenced by src.
|
|
||||||
Merge(src Message)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RequiredNotSetError is an error type returned when
|
|
||||||
// marshaling or unmarshaling a message with missing required fields.
|
|
||||||
type RequiredNotSetError struct {
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *RequiredNotSetError) Error() string {
|
|
||||||
if e.err != nil {
|
|
||||||
return e.err.Error()
|
|
||||||
}
|
|
||||||
return "proto: required field not set"
|
|
||||||
}
|
|
||||||
func (e *RequiredNotSetError) RequiredNotSet() bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkRequiredNotSet(m protoV2.Message) error {
|
|
||||||
if err := protoV2.CheckInitialized(m); err != nil {
|
|
||||||
return &RequiredNotSetError{err: err}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clone returns a deep copy of src.
|
|
||||||
func Clone(src Message) Message {
|
|
||||||
return MessageV1(protoV2.Clone(MessageV2(src)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Merge merges src into dst, which must be messages of the same type.
|
|
||||||
//
|
|
||||||
// Populated scalar fields in src are copied to dst, while populated
|
|
||||||
// singular messages in src are merged into dst by recursively calling Merge.
|
|
||||||
// The elements of every list field in src is appended to the corresponded
|
|
||||||
// list fields in dst. The entries of every map field in src is copied into
|
|
||||||
// the corresponding map field in dst, possibly replacing existing entries.
|
|
||||||
// The unknown fields of src are appended to the unknown fields of dst.
|
|
||||||
func Merge(dst, src Message) {
|
|
||||||
protoV2.Merge(MessageV2(dst), MessageV2(src))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Equal reports whether two messages are equal.
|
|
||||||
// If two messages marshal to the same bytes under deterministic serialization,
|
|
||||||
// then Equal is guaranteed to report true.
|
|
||||||
//
|
|
||||||
// Two messages are equal if they are the same protobuf message type,
|
|
||||||
// have the same set of populated known and extension field values,
|
|
||||||
// and the same set of unknown fields values.
|
|
||||||
//
|
|
||||||
// Scalar values are compared with the equivalent of the == operator in Go,
|
|
||||||
// except bytes values which are compared using bytes.Equal and
|
|
||||||
// floating point values which specially treat NaNs as equal.
|
|
||||||
// Message values are compared by recursively calling Equal.
|
|
||||||
// Lists are equal if each element value is also equal.
|
|
||||||
// Maps are equal if they have the same set of keys, where the pair of values
|
|
||||||
// for each key is also equal.
|
|
||||||
func Equal(x, y Message) bool {
|
|
||||||
return protoV2.Equal(MessageV2(x), MessageV2(y))
|
|
||||||
}
|
|
||||||
|
|
||||||
func isMessageSet(md protoreflect.MessageDescriptor) bool {
|
|
||||||
ms, ok := md.(interface{ IsMessageSet() bool })
|
|
||||||
return ok && ms.IsMessageSet()
|
|
||||||
}
|
|
||||||
317
vendor/github.com/golang/protobuf/proto/registry.go
generated
vendored
317
vendor/github.com/golang/protobuf/proto/registry.go
generated
vendored
@@ -1,317 +0,0 @@
|
|||||||
// Copyright 2019 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package proto
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"compress/gzip"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"google.golang.org/protobuf/reflect/protodesc"
|
|
||||||
"google.golang.org/protobuf/reflect/protoreflect"
|
|
||||||
"google.golang.org/protobuf/reflect/protoregistry"
|
|
||||||
"google.golang.org/protobuf/runtime/protoimpl"
|
|
||||||
)
|
|
||||||
|
|
||||||
// filePath is the path to the proto source file.
|
|
||||||
type filePath = string // e.g., "google/protobuf/descriptor.proto"
|
|
||||||
|
|
||||||
// fileDescGZIP is the compressed contents of the encoded FileDescriptorProto.
|
|
||||||
type fileDescGZIP = []byte
|
|
||||||
|
|
||||||
var fileCache sync.Map // map[filePath]fileDescGZIP
|
|
||||||
|
|
||||||
// RegisterFile is called from generated code to register the compressed
|
|
||||||
// FileDescriptorProto with the file path for a proto source file.
|
|
||||||
//
|
|
||||||
// Deprecated: Use protoregistry.GlobalFiles.RegisterFile instead.
|
|
||||||
func RegisterFile(s filePath, d fileDescGZIP) {
|
|
||||||
// Decompress the descriptor.
|
|
||||||
zr, err := gzip.NewReader(bytes.NewReader(d))
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Sprintf("proto: invalid compressed file descriptor: %v", err))
|
|
||||||
}
|
|
||||||
b, err := ioutil.ReadAll(zr)
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Sprintf("proto: invalid compressed file descriptor: %v", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Construct a protoreflect.FileDescriptor from the raw descriptor.
|
|
||||||
// Note that DescBuilder.Build automatically registers the constructed
|
|
||||||
// file descriptor with the v2 registry.
|
|
||||||
protoimpl.DescBuilder{RawDescriptor: b}.Build()
|
|
||||||
|
|
||||||
// Locally cache the raw descriptor form for the file.
|
|
||||||
fileCache.Store(s, d)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FileDescriptor returns the compressed FileDescriptorProto given the file path
|
|
||||||
// for a proto source file. It returns nil if not found.
|
|
||||||
//
|
|
||||||
// Deprecated: Use protoregistry.GlobalFiles.FindFileByPath instead.
|
|
||||||
func FileDescriptor(s filePath) fileDescGZIP {
|
|
||||||
if v, ok := fileCache.Load(s); ok {
|
|
||||||
return v.(fileDescGZIP)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the descriptor in the v2 registry.
|
|
||||||
var b []byte
|
|
||||||
if fd, _ := protoregistry.GlobalFiles.FindFileByPath(s); fd != nil {
|
|
||||||
b, _ = Marshal(protodesc.ToFileDescriptorProto(fd))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Locally cache the raw descriptor form for the file.
|
|
||||||
if len(b) > 0 {
|
|
||||||
v, _ := fileCache.LoadOrStore(s, protoimpl.X.CompressGZIP(b))
|
|
||||||
return v.(fileDescGZIP)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// enumName is the name of an enum. For historical reasons, the enum name is
|
|
||||||
// neither the full Go name nor the full protobuf name of the enum.
|
|
||||||
// The name is the dot-separated combination of just the proto package that the
|
|
||||||
// enum is declared within followed by the Go type name of the generated enum.
|
|
||||||
type enumName = string // e.g., "my.proto.package.GoMessage_GoEnum"
|
|
||||||
|
|
||||||
// enumsByName maps enum values by name to their numeric counterpart.
|
|
||||||
type enumsByName = map[string]int32
|
|
||||||
|
|
||||||
// enumsByNumber maps enum values by number to their name counterpart.
|
|
||||||
type enumsByNumber = map[int32]string
|
|
||||||
|
|
||||||
var enumCache sync.Map // map[enumName]enumsByName
|
|
||||||
var numFilesCache sync.Map // map[protoreflect.FullName]int
|
|
||||||
|
|
||||||
// RegisterEnum is called from the generated code to register the mapping of
|
|
||||||
// enum value names to enum numbers for the enum identified by s.
|
|
||||||
//
|
|
||||||
// Deprecated: Use protoregistry.GlobalTypes.RegisterEnum instead.
|
|
||||||
func RegisterEnum(s enumName, _ enumsByNumber, m enumsByName) {
|
|
||||||
if _, ok := enumCache.Load(s); ok {
|
|
||||||
panic("proto: duplicate enum registered: " + s)
|
|
||||||
}
|
|
||||||
enumCache.Store(s, m)
|
|
||||||
|
|
||||||
// This does not forward registration to the v2 registry since this API
|
|
||||||
// lacks sufficient information to construct a complete v2 enum descriptor.
|
|
||||||
}
|
|
||||||
|
|
||||||
// EnumValueMap returns the mapping from enum value names to enum numbers for
|
|
||||||
// the enum of the given name. It returns nil if not found.
|
|
||||||
//
|
|
||||||
// Deprecated: Use protoregistry.GlobalTypes.FindEnumByName instead.
|
|
||||||
func EnumValueMap(s enumName) enumsByName {
|
|
||||||
if v, ok := enumCache.Load(s); ok {
|
|
||||||
return v.(enumsByName)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check whether the cache is stale. If the number of files in the current
|
|
||||||
// package differs, then it means that some enums may have been recently
|
|
||||||
// registered upstream that we do not know about.
|
|
||||||
var protoPkg protoreflect.FullName
|
|
||||||
if i := strings.LastIndexByte(s, '.'); i >= 0 {
|
|
||||||
protoPkg = protoreflect.FullName(s[:i])
|
|
||||||
}
|
|
||||||
v, _ := numFilesCache.Load(protoPkg)
|
|
||||||
numFiles, _ := v.(int)
|
|
||||||
if protoregistry.GlobalFiles.NumFilesByPackage(protoPkg) == numFiles {
|
|
||||||
return nil // cache is up-to-date; was not found earlier
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the enum cache for all enums declared in the given proto package.
|
|
||||||
numFiles = 0
|
|
||||||
protoregistry.GlobalFiles.RangeFilesByPackage(protoPkg, func(fd protoreflect.FileDescriptor) bool {
|
|
||||||
walkEnums(fd, func(ed protoreflect.EnumDescriptor) {
|
|
||||||
name := protoimpl.X.LegacyEnumName(ed)
|
|
||||||
if _, ok := enumCache.Load(name); !ok {
|
|
||||||
m := make(enumsByName)
|
|
||||||
evs := ed.Values()
|
|
||||||
for i := evs.Len() - 1; i >= 0; i-- {
|
|
||||||
ev := evs.Get(i)
|
|
||||||
m[string(ev.Name())] = int32(ev.Number())
|
|
||||||
}
|
|
||||||
enumCache.LoadOrStore(name, m)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
numFiles++
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
numFilesCache.Store(protoPkg, numFiles)
|
|
||||||
|
|
||||||
// Check cache again for enum map.
|
|
||||||
if v, ok := enumCache.Load(s); ok {
|
|
||||||
return v.(enumsByName)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// walkEnums recursively walks all enums declared in d.
|
|
||||||
func walkEnums(d interface {
|
|
||||||
Enums() protoreflect.EnumDescriptors
|
|
||||||
Messages() protoreflect.MessageDescriptors
|
|
||||||
}, f func(protoreflect.EnumDescriptor)) {
|
|
||||||
eds := d.Enums()
|
|
||||||
for i := eds.Len() - 1; i >= 0; i-- {
|
|
||||||
f(eds.Get(i))
|
|
||||||
}
|
|
||||||
mds := d.Messages()
|
|
||||||
for i := mds.Len() - 1; i >= 0; i-- {
|
|
||||||
walkEnums(mds.Get(i), f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// messageName is the full name of protobuf message.
|
|
||||||
type messageName = string
|
|
||||||
|
|
||||||
var messageTypeCache sync.Map // map[messageName]reflect.Type
|
|
||||||
|
|
||||||
// RegisterType is called from generated code to register the message Go type
|
|
||||||
// for a message of the given name.
|
|
||||||
//
|
|
||||||
// Deprecated: Use protoregistry.GlobalTypes.RegisterMessage instead.
|
|
||||||
func RegisterType(m Message, s messageName) {
|
|
||||||
mt := protoimpl.X.LegacyMessageTypeOf(m, protoreflect.FullName(s))
|
|
||||||
if err := protoregistry.GlobalTypes.RegisterMessage(mt); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
messageTypeCache.Store(s, reflect.TypeOf(m))
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterMapType is called from generated code to register the Go map type
|
|
||||||
// for a protobuf message representing a map entry.
|
|
||||||
//
|
|
||||||
// Deprecated: Do not use.
|
|
||||||
func RegisterMapType(m interface{}, s messageName) {
|
|
||||||
t := reflect.TypeOf(m)
|
|
||||||
if t.Kind() != reflect.Map {
|
|
||||||
panic(fmt.Sprintf("invalid map kind: %v", t))
|
|
||||||
}
|
|
||||||
if _, ok := messageTypeCache.Load(s); ok {
|
|
||||||
panic(fmt.Errorf("proto: duplicate proto message registered: %s", s))
|
|
||||||
}
|
|
||||||
messageTypeCache.Store(s, t)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MessageType returns the message type for a named message.
|
|
||||||
// It returns nil if not found.
|
|
||||||
//
|
|
||||||
// Deprecated: Use protoregistry.GlobalTypes.FindMessageByName instead.
|
|
||||||
func MessageType(s messageName) reflect.Type {
|
|
||||||
if v, ok := messageTypeCache.Load(s); ok {
|
|
||||||
return v.(reflect.Type)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Derive the message type from the v2 registry.
|
|
||||||
var t reflect.Type
|
|
||||||
if mt, _ := protoregistry.GlobalTypes.FindMessageByName(protoreflect.FullName(s)); mt != nil {
|
|
||||||
t = messageGoType(mt)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we could not get a concrete type, it is possible that it is a
|
|
||||||
// pseudo-message for a map entry.
|
|
||||||
if t == nil {
|
|
||||||
d, _ := protoregistry.GlobalFiles.FindDescriptorByName(protoreflect.FullName(s))
|
|
||||||
if md, _ := d.(protoreflect.MessageDescriptor); md != nil && md.IsMapEntry() {
|
|
||||||
kt := goTypeForField(md.Fields().ByNumber(1))
|
|
||||||
vt := goTypeForField(md.Fields().ByNumber(2))
|
|
||||||
t = reflect.MapOf(kt, vt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Locally cache the message type for the given name.
|
|
||||||
if t != nil {
|
|
||||||
v, _ := messageTypeCache.LoadOrStore(s, t)
|
|
||||||
return v.(reflect.Type)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func goTypeForField(fd protoreflect.FieldDescriptor) reflect.Type {
|
|
||||||
switch k := fd.Kind(); k {
|
|
||||||
case protoreflect.EnumKind:
|
|
||||||
if et, _ := protoregistry.GlobalTypes.FindEnumByName(fd.Enum().FullName()); et != nil {
|
|
||||||
return enumGoType(et)
|
|
||||||
}
|
|
||||||
return reflect.TypeOf(protoreflect.EnumNumber(0))
|
|
||||||
case protoreflect.MessageKind, protoreflect.GroupKind:
|
|
||||||
if mt, _ := protoregistry.GlobalTypes.FindMessageByName(fd.Message().FullName()); mt != nil {
|
|
||||||
return messageGoType(mt)
|
|
||||||
}
|
|
||||||
return reflect.TypeOf((*protoreflect.Message)(nil)).Elem()
|
|
||||||
default:
|
|
||||||
return reflect.TypeOf(fd.Default().Interface())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func enumGoType(et protoreflect.EnumType) reflect.Type {
|
|
||||||
return reflect.TypeOf(et.New(0))
|
|
||||||
}
|
|
||||||
|
|
||||||
func messageGoType(mt protoreflect.MessageType) reflect.Type {
|
|
||||||
return reflect.TypeOf(MessageV1(mt.Zero().Interface()))
|
|
||||||
}
|
|
||||||
|
|
||||||
// MessageName returns the full protobuf name for the given message type.
|
|
||||||
//
|
|
||||||
// Deprecated: Use protoreflect.MessageDescriptor.FullName instead.
|
|
||||||
func MessageName(m Message) messageName {
|
|
||||||
if m == nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
if m, ok := m.(interface{ XXX_MessageName() messageName }); ok {
|
|
||||||
return m.XXX_MessageName()
|
|
||||||
}
|
|
||||||
return messageName(protoimpl.X.MessageDescriptorOf(m).FullName())
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterExtension is called from the generated code to register
|
|
||||||
// the extension descriptor.
|
|
||||||
//
|
|
||||||
// Deprecated: Use protoregistry.GlobalTypes.RegisterExtension instead.
|
|
||||||
func RegisterExtension(d *ExtensionDesc) {
|
|
||||||
if err := protoregistry.GlobalTypes.RegisterExtension(d); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type extensionsByNumber = map[int32]*ExtensionDesc
|
|
||||||
|
|
||||||
var extensionCache sync.Map // map[messageName]extensionsByNumber
|
|
||||||
|
|
||||||
// RegisteredExtensions returns a map of the registered extensions for the
|
|
||||||
// provided protobuf message, indexed by the extension field number.
|
|
||||||
//
|
|
||||||
// Deprecated: Use protoregistry.GlobalTypes.RangeExtensionsByMessage instead.
|
|
||||||
func RegisteredExtensions(m Message) extensionsByNumber {
|
|
||||||
// Check whether the cache is stale. If the number of extensions for
|
|
||||||
// the given message differs, then it means that some extensions were
|
|
||||||
// recently registered upstream that we do not know about.
|
|
||||||
s := MessageName(m)
|
|
||||||
v, _ := extensionCache.Load(s)
|
|
||||||
xs, _ := v.(extensionsByNumber)
|
|
||||||
if protoregistry.GlobalTypes.NumExtensionsByMessage(protoreflect.FullName(s)) == len(xs) {
|
|
||||||
return xs // cache is up-to-date
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cache is stale, re-compute the extensions map.
|
|
||||||
xs = make(extensionsByNumber)
|
|
||||||
protoregistry.GlobalTypes.RangeExtensionsByMessage(protoreflect.FullName(s), func(xt protoreflect.ExtensionType) bool {
|
|
||||||
if xd, ok := xt.(*ExtensionDesc); ok {
|
|
||||||
xs[int32(xt.TypeDescriptor().Number())] = xd
|
|
||||||
} else {
|
|
||||||
// TODO: This implies that the protoreflect.ExtensionType is a
|
|
||||||
// custom type not generated by protoc-gen-go. We could try and
|
|
||||||
// convert the type to an ExtensionDesc.
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
extensionCache.Store(s, xs)
|
|
||||||
return xs
|
|
||||||
}
|
|
||||||
801
vendor/github.com/golang/protobuf/proto/text_decode.go
generated
vendored
801
vendor/github.com/golang/protobuf/proto/text_decode.go
generated
vendored
@@ -1,801 +0,0 @@
|
|||||||
// Copyright 2010 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package proto
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"unicode/utf8"
|
|
||||||
|
|
||||||
"google.golang.org/protobuf/encoding/prototext"
|
|
||||||
protoV2 "google.golang.org/protobuf/proto"
|
|
||||||
"google.golang.org/protobuf/reflect/protoreflect"
|
|
||||||
"google.golang.org/protobuf/reflect/protoregistry"
|
|
||||||
)
|
|
||||||
|
|
||||||
const wrapTextUnmarshalV2 = false
|
|
||||||
|
|
||||||
// ParseError is returned by UnmarshalText.
|
|
||||||
type ParseError struct {
|
|
||||||
Message string
|
|
||||||
|
|
||||||
// Deprecated: Do not use.
|
|
||||||
Line, Offset int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *ParseError) Error() string {
|
|
||||||
if wrapTextUnmarshalV2 {
|
|
||||||
return e.Message
|
|
||||||
}
|
|
||||||
if e.Line == 1 {
|
|
||||||
return fmt.Sprintf("line 1.%d: %v", e.Offset, e.Message)
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("line %d: %v", e.Line, e.Message)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalText parses a proto text formatted string into m.
|
|
||||||
func UnmarshalText(s string, m Message) error {
|
|
||||||
if u, ok := m.(encoding.TextUnmarshaler); ok {
|
|
||||||
return u.UnmarshalText([]byte(s))
|
|
||||||
}
|
|
||||||
|
|
||||||
m.Reset()
|
|
||||||
mi := MessageV2(m)
|
|
||||||
|
|
||||||
if wrapTextUnmarshalV2 {
|
|
||||||
err := prototext.UnmarshalOptions{
|
|
||||||
AllowPartial: true,
|
|
||||||
}.Unmarshal([]byte(s), mi)
|
|
||||||
if err != nil {
|
|
||||||
return &ParseError{Message: err.Error()}
|
|
||||||
}
|
|
||||||
return checkRequiredNotSet(mi)
|
|
||||||
} else {
|
|
||||||
if err := newTextParser(s).unmarshalMessage(mi.ProtoReflect(), ""); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return checkRequiredNotSet(mi)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type textParser struct {
|
|
||||||
s string // remaining input
|
|
||||||
done bool // whether the parsing is finished (success or error)
|
|
||||||
backed bool // whether back() was called
|
|
||||||
offset, line int
|
|
||||||
cur token
|
|
||||||
}
|
|
||||||
|
|
||||||
type token struct {
|
|
||||||
value string
|
|
||||||
err *ParseError
|
|
||||||
line int // line number
|
|
||||||
offset int // byte number from start of input, not start of line
|
|
||||||
unquoted string // the unquoted version of value, if it was a quoted string
|
|
||||||
}
|
|
||||||
|
|
||||||
func newTextParser(s string) *textParser {
|
|
||||||
p := new(textParser)
|
|
||||||
p.s = s
|
|
||||||
p.line = 1
|
|
||||||
p.cur.line = 1
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *textParser) unmarshalMessage(m protoreflect.Message, terminator string) (err error) {
|
|
||||||
md := m.Descriptor()
|
|
||||||
fds := md.Fields()
|
|
||||||
|
|
||||||
// A struct is a sequence of "name: value", terminated by one of
|
|
||||||
// '>' or '}', or the end of the input. A name may also be
|
|
||||||
// "[extension]" or "[type/url]".
|
|
||||||
//
|
|
||||||
// The whole struct can also be an expanded Any message, like:
|
|
||||||
// [type/url] < ... struct contents ... >
|
|
||||||
seen := make(map[protoreflect.FieldNumber]bool)
|
|
||||||
for {
|
|
||||||
tok := p.next()
|
|
||||||
if tok.err != nil {
|
|
||||||
return tok.err
|
|
||||||
}
|
|
||||||
if tok.value == terminator {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if tok.value == "[" {
|
|
||||||
if err := p.unmarshalExtensionOrAny(m, seen); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is a normal, non-extension field.
|
|
||||||
name := protoreflect.Name(tok.value)
|
|
||||||
fd := fds.ByName(name)
|
|
||||||
switch {
|
|
||||||
case fd == nil:
|
|
||||||
gd := fds.ByName(protoreflect.Name(strings.ToLower(string(name))))
|
|
||||||
if gd != nil && gd.Kind() == protoreflect.GroupKind && gd.Message().Name() == name {
|
|
||||||
fd = gd
|
|
||||||
}
|
|
||||||
case fd.Kind() == protoreflect.GroupKind && fd.Message().Name() != name:
|
|
||||||
fd = nil
|
|
||||||
case fd.IsWeak() && fd.Message().IsPlaceholder():
|
|
||||||
fd = nil
|
|
||||||
}
|
|
||||||
if fd == nil {
|
|
||||||
typeName := string(md.FullName())
|
|
||||||
if m, ok := m.Interface().(Message); ok {
|
|
||||||
t := reflect.TypeOf(m)
|
|
||||||
if t.Kind() == reflect.Ptr {
|
|
||||||
typeName = t.Elem().String()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return p.errorf("unknown field name %q in %v", name, typeName)
|
|
||||||
}
|
|
||||||
if od := fd.ContainingOneof(); od != nil && m.WhichOneof(od) != nil {
|
|
||||||
return p.errorf("field '%s' would overwrite already parsed oneof '%s'", name, od.Name())
|
|
||||||
}
|
|
||||||
if fd.Cardinality() != protoreflect.Repeated && seen[fd.Number()] {
|
|
||||||
return p.errorf("non-repeated field %q was repeated", fd.Name())
|
|
||||||
}
|
|
||||||
seen[fd.Number()] = true
|
|
||||||
|
|
||||||
// Consume any colon.
|
|
||||||
if err := p.checkForColon(fd); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse into the field.
|
|
||||||
v := m.Get(fd)
|
|
||||||
if !m.Has(fd) && (fd.IsList() || fd.IsMap() || fd.Message() != nil) {
|
|
||||||
v = m.Mutable(fd)
|
|
||||||
}
|
|
||||||
if v, err = p.unmarshalValue(v, fd); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
m.Set(fd, v)
|
|
||||||
|
|
||||||
if err := p.consumeOptionalSeparator(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *textParser) unmarshalExtensionOrAny(m protoreflect.Message, seen map[protoreflect.FieldNumber]bool) error {
|
|
||||||
name, err := p.consumeExtensionOrAnyName()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// If it contains a slash, it's an Any type URL.
|
|
||||||
if slashIdx := strings.LastIndex(name, "/"); slashIdx >= 0 {
|
|
||||||
tok := p.next()
|
|
||||||
if tok.err != nil {
|
|
||||||
return tok.err
|
|
||||||
}
|
|
||||||
// consume an optional colon
|
|
||||||
if tok.value == ":" {
|
|
||||||
tok = p.next()
|
|
||||||
if tok.err != nil {
|
|
||||||
return tok.err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var terminator string
|
|
||||||
switch tok.value {
|
|
||||||
case "<":
|
|
||||||
terminator = ">"
|
|
||||||
case "{":
|
|
||||||
terminator = "}"
|
|
||||||
default:
|
|
||||||
return p.errorf("expected '{' or '<', found %q", tok.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
mt, err := protoregistry.GlobalTypes.FindMessageByURL(name)
|
|
||||||
if err != nil {
|
|
||||||
return p.errorf("unrecognized message %q in google.protobuf.Any", name[slashIdx+len("/"):])
|
|
||||||
}
|
|
||||||
m2 := mt.New()
|
|
||||||
if err := p.unmarshalMessage(m2, terminator); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
b, err := protoV2.Marshal(m2.Interface())
|
|
||||||
if err != nil {
|
|
||||||
return p.errorf("failed to marshal message of type %q: %v", name[slashIdx+len("/"):], err)
|
|
||||||
}
|
|
||||||
|
|
||||||
urlFD := m.Descriptor().Fields().ByName("type_url")
|
|
||||||
valFD := m.Descriptor().Fields().ByName("value")
|
|
||||||
if seen[urlFD.Number()] {
|
|
||||||
return p.errorf("Any message unpacked multiple times, or %q already set", urlFD.Name())
|
|
||||||
}
|
|
||||||
if seen[valFD.Number()] {
|
|
||||||
return p.errorf("Any message unpacked multiple times, or %q already set", valFD.Name())
|
|
||||||
}
|
|
||||||
m.Set(urlFD, protoreflect.ValueOfString(name))
|
|
||||||
m.Set(valFD, protoreflect.ValueOfBytes(b))
|
|
||||||
seen[urlFD.Number()] = true
|
|
||||||
seen[valFD.Number()] = true
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
xname := protoreflect.FullName(name)
|
|
||||||
xt, _ := protoregistry.GlobalTypes.FindExtensionByName(xname)
|
|
||||||
if xt == nil && isMessageSet(m.Descriptor()) {
|
|
||||||
xt, _ = protoregistry.GlobalTypes.FindExtensionByName(xname.Append("message_set_extension"))
|
|
||||||
}
|
|
||||||
if xt == nil {
|
|
||||||
return p.errorf("unrecognized extension %q", name)
|
|
||||||
}
|
|
||||||
fd := xt.TypeDescriptor()
|
|
||||||
if fd.ContainingMessage().FullName() != m.Descriptor().FullName() {
|
|
||||||
return p.errorf("extension field %q does not extend message %q", name, m.Descriptor().FullName())
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := p.checkForColon(fd); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
v := m.Get(fd)
|
|
||||||
if !m.Has(fd) && (fd.IsList() || fd.IsMap() || fd.Message() != nil) {
|
|
||||||
v = m.Mutable(fd)
|
|
||||||
}
|
|
||||||
v, err = p.unmarshalValue(v, fd)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
m.Set(fd, v)
|
|
||||||
return p.consumeOptionalSeparator()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *textParser) unmarshalValue(v protoreflect.Value, fd protoreflect.FieldDescriptor) (protoreflect.Value, error) {
|
|
||||||
tok := p.next()
|
|
||||||
if tok.err != nil {
|
|
||||||
return v, tok.err
|
|
||||||
}
|
|
||||||
if tok.value == "" {
|
|
||||||
return v, p.errorf("unexpected EOF")
|
|
||||||
}
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case fd.IsList():
|
|
||||||
lv := v.List()
|
|
||||||
var err error
|
|
||||||
if tok.value == "[" {
|
|
||||||
// Repeated field with list notation, like [1,2,3].
|
|
||||||
for {
|
|
||||||
vv := lv.NewElement()
|
|
||||||
vv, err = p.unmarshalSingularValue(vv, fd)
|
|
||||||
if err != nil {
|
|
||||||
return v, err
|
|
||||||
}
|
|
||||||
lv.Append(vv)
|
|
||||||
|
|
||||||
tok := p.next()
|
|
||||||
if tok.err != nil {
|
|
||||||
return v, tok.err
|
|
||||||
}
|
|
||||||
if tok.value == "]" {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if tok.value != "," {
|
|
||||||
return v, p.errorf("Expected ']' or ',' found %q", tok.value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return v, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// One value of the repeated field.
|
|
||||||
p.back()
|
|
||||||
vv := lv.NewElement()
|
|
||||||
vv, err = p.unmarshalSingularValue(vv, fd)
|
|
||||||
if err != nil {
|
|
||||||
return v, err
|
|
||||||
}
|
|
||||||
lv.Append(vv)
|
|
||||||
return v, nil
|
|
||||||
case fd.IsMap():
|
|
||||||
// The map entry should be this sequence of tokens:
|
|
||||||
// < key : KEY value : VALUE >
|
|
||||||
// However, implementations may omit key or value, and technically
|
|
||||||
// we should support them in any order.
|
|
||||||
var terminator string
|
|
||||||
switch tok.value {
|
|
||||||
case "<":
|
|
||||||
terminator = ">"
|
|
||||||
case "{":
|
|
||||||
terminator = "}"
|
|
||||||
default:
|
|
||||||
return v, p.errorf("expected '{' or '<', found %q", tok.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
keyFD := fd.MapKey()
|
|
||||||
valFD := fd.MapValue()
|
|
||||||
|
|
||||||
mv := v.Map()
|
|
||||||
kv := keyFD.Default()
|
|
||||||
vv := mv.NewValue()
|
|
||||||
for {
|
|
||||||
tok := p.next()
|
|
||||||
if tok.err != nil {
|
|
||||||
return v, tok.err
|
|
||||||
}
|
|
||||||
if tok.value == terminator {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
var err error
|
|
||||||
switch tok.value {
|
|
||||||
case "key":
|
|
||||||
if err := p.consumeToken(":"); err != nil {
|
|
||||||
return v, err
|
|
||||||
}
|
|
||||||
if kv, err = p.unmarshalSingularValue(kv, keyFD); err != nil {
|
|
||||||
return v, err
|
|
||||||
}
|
|
||||||
if err := p.consumeOptionalSeparator(); err != nil {
|
|
||||||
return v, err
|
|
||||||
}
|
|
||||||
case "value":
|
|
||||||
if err := p.checkForColon(valFD); err != nil {
|
|
||||||
return v, err
|
|
||||||
}
|
|
||||||
if vv, err = p.unmarshalSingularValue(vv, valFD); err != nil {
|
|
||||||
return v, err
|
|
||||||
}
|
|
||||||
if err := p.consumeOptionalSeparator(); err != nil {
|
|
||||||
return v, err
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
p.back()
|
|
||||||
return v, p.errorf(`expected "key", "value", or %q, found %q`, terminator, tok.value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mv.Set(kv.MapKey(), vv)
|
|
||||||
return v, nil
|
|
||||||
default:
|
|
||||||
p.back()
|
|
||||||
return p.unmarshalSingularValue(v, fd)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *textParser) unmarshalSingularValue(v protoreflect.Value, fd protoreflect.FieldDescriptor) (protoreflect.Value, error) {
|
|
||||||
tok := p.next()
|
|
||||||
if tok.err != nil {
|
|
||||||
return v, tok.err
|
|
||||||
}
|
|
||||||
if tok.value == "" {
|
|
||||||
return v, p.errorf("unexpected EOF")
|
|
||||||
}
|
|
||||||
|
|
||||||
switch fd.Kind() {
|
|
||||||
case protoreflect.BoolKind:
|
|
||||||
switch tok.value {
|
|
||||||
case "true", "1", "t", "True":
|
|
||||||
return protoreflect.ValueOfBool(true), nil
|
|
||||||
case "false", "0", "f", "False":
|
|
||||||
return protoreflect.ValueOfBool(false), nil
|
|
||||||
}
|
|
||||||
case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind:
|
|
||||||
if x, err := strconv.ParseInt(tok.value, 0, 32); err == nil {
|
|
||||||
return protoreflect.ValueOfInt32(int32(x)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// The C++ parser accepts large positive hex numbers that uses
|
|
||||||
// two's complement arithmetic to represent negative numbers.
|
|
||||||
// This feature is here for backwards compatibility with C++.
|
|
||||||
if strings.HasPrefix(tok.value, "0x") {
|
|
||||||
if x, err := strconv.ParseUint(tok.value, 0, 32); err == nil {
|
|
||||||
return protoreflect.ValueOfInt32(int32(-(int64(^x) + 1))), nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind:
|
|
||||||
if x, err := strconv.ParseInt(tok.value, 0, 64); err == nil {
|
|
||||||
return protoreflect.ValueOfInt64(int64(x)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// The C++ parser accepts large positive hex numbers that uses
|
|
||||||
// two's complement arithmetic to represent negative numbers.
|
|
||||||
// This feature is here for backwards compatibility with C++.
|
|
||||||
if strings.HasPrefix(tok.value, "0x") {
|
|
||||||
if x, err := strconv.ParseUint(tok.value, 0, 64); err == nil {
|
|
||||||
return protoreflect.ValueOfInt64(int64(-(int64(^x) + 1))), nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case protoreflect.Uint32Kind, protoreflect.Fixed32Kind:
|
|
||||||
if x, err := strconv.ParseUint(tok.value, 0, 32); err == nil {
|
|
||||||
return protoreflect.ValueOfUint32(uint32(x)), nil
|
|
||||||
}
|
|
||||||
case protoreflect.Uint64Kind, protoreflect.Fixed64Kind:
|
|
||||||
if x, err := strconv.ParseUint(tok.value, 0, 64); err == nil {
|
|
||||||
return protoreflect.ValueOfUint64(uint64(x)), nil
|
|
||||||
}
|
|
||||||
case protoreflect.FloatKind:
|
|
||||||
// Ignore 'f' for compatibility with output generated by C++,
|
|
||||||
// but don't remove 'f' when the value is "-inf" or "inf".
|
|
||||||
v := tok.value
|
|
||||||
if strings.HasSuffix(v, "f") && v != "-inf" && v != "inf" {
|
|
||||||
v = v[:len(v)-len("f")]
|
|
||||||
}
|
|
||||||
if x, err := strconv.ParseFloat(v, 32); err == nil {
|
|
||||||
return protoreflect.ValueOfFloat32(float32(x)), nil
|
|
||||||
}
|
|
||||||
case protoreflect.DoubleKind:
|
|
||||||
// Ignore 'f' for compatibility with output generated by C++,
|
|
||||||
// but don't remove 'f' when the value is "-inf" or "inf".
|
|
||||||
v := tok.value
|
|
||||||
if strings.HasSuffix(v, "f") && v != "-inf" && v != "inf" {
|
|
||||||
v = v[:len(v)-len("f")]
|
|
||||||
}
|
|
||||||
if x, err := strconv.ParseFloat(v, 64); err == nil {
|
|
||||||
return protoreflect.ValueOfFloat64(float64(x)), nil
|
|
||||||
}
|
|
||||||
case protoreflect.StringKind:
|
|
||||||
if isQuote(tok.value[0]) {
|
|
||||||
return protoreflect.ValueOfString(tok.unquoted), nil
|
|
||||||
}
|
|
||||||
case protoreflect.BytesKind:
|
|
||||||
if isQuote(tok.value[0]) {
|
|
||||||
return protoreflect.ValueOfBytes([]byte(tok.unquoted)), nil
|
|
||||||
}
|
|
||||||
case protoreflect.EnumKind:
|
|
||||||
if x, err := strconv.ParseInt(tok.value, 0, 32); err == nil {
|
|
||||||
return protoreflect.ValueOfEnum(protoreflect.EnumNumber(x)), nil
|
|
||||||
}
|
|
||||||
vd := fd.Enum().Values().ByName(protoreflect.Name(tok.value))
|
|
||||||
if vd != nil {
|
|
||||||
return protoreflect.ValueOfEnum(vd.Number()), nil
|
|
||||||
}
|
|
||||||
case protoreflect.MessageKind, protoreflect.GroupKind:
|
|
||||||
var terminator string
|
|
||||||
switch tok.value {
|
|
||||||
case "{":
|
|
||||||
terminator = "}"
|
|
||||||
case "<":
|
|
||||||
terminator = ">"
|
|
||||||
default:
|
|
||||||
return v, p.errorf("expected '{' or '<', found %q", tok.value)
|
|
||||||
}
|
|
||||||
err := p.unmarshalMessage(v.Message(), terminator)
|
|
||||||
return v, err
|
|
||||||
default:
|
|
||||||
panic(fmt.Sprintf("invalid kind %v", fd.Kind()))
|
|
||||||
}
|
|
||||||
return v, p.errorf("invalid %v: %v", fd.Kind(), tok.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Consume a ':' from the input stream (if the next token is a colon),
|
|
||||||
// returning an error if a colon is needed but not present.
|
|
||||||
func (p *textParser) checkForColon(fd protoreflect.FieldDescriptor) *ParseError {
|
|
||||||
tok := p.next()
|
|
||||||
if tok.err != nil {
|
|
||||||
return tok.err
|
|
||||||
}
|
|
||||||
if tok.value != ":" {
|
|
||||||
if fd.Message() == nil {
|
|
||||||
return p.errorf("expected ':', found %q", tok.value)
|
|
||||||
}
|
|
||||||
p.back()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// consumeExtensionOrAnyName consumes an extension name or an Any type URL and
|
|
||||||
// the following ']'. It returns the name or URL consumed.
|
|
||||||
func (p *textParser) consumeExtensionOrAnyName() (string, error) {
|
|
||||||
tok := p.next()
|
|
||||||
if tok.err != nil {
|
|
||||||
return "", tok.err
|
|
||||||
}
|
|
||||||
|
|
||||||
// If extension name or type url is quoted, it's a single token.
|
|
||||||
if len(tok.value) > 2 && isQuote(tok.value[0]) && tok.value[len(tok.value)-1] == tok.value[0] {
|
|
||||||
name, err := unquoteC(tok.value[1:len(tok.value)-1], rune(tok.value[0]))
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return name, p.consumeToken("]")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Consume everything up to "]"
|
|
||||||
var parts []string
|
|
||||||
for tok.value != "]" {
|
|
||||||
parts = append(parts, tok.value)
|
|
||||||
tok = p.next()
|
|
||||||
if tok.err != nil {
|
|
||||||
return "", p.errorf("unrecognized type_url or extension name: %s", tok.err)
|
|
||||||
}
|
|
||||||
if p.done && tok.value != "]" {
|
|
||||||
return "", p.errorf("unclosed type_url or extension name")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return strings.Join(parts, ""), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// consumeOptionalSeparator consumes an optional semicolon or comma.
|
|
||||||
// It is used in unmarshalMessage to provide backward compatibility.
|
|
||||||
func (p *textParser) consumeOptionalSeparator() error {
|
|
||||||
tok := p.next()
|
|
||||||
if tok.err != nil {
|
|
||||||
return tok.err
|
|
||||||
}
|
|
||||||
if tok.value != ";" && tok.value != "," {
|
|
||||||
p.back()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *textParser) errorf(format string, a ...interface{}) *ParseError {
|
|
||||||
pe := &ParseError{fmt.Sprintf(format, a...), p.cur.line, p.cur.offset}
|
|
||||||
p.cur.err = pe
|
|
||||||
p.done = true
|
|
||||||
return pe
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *textParser) skipWhitespace() {
|
|
||||||
i := 0
|
|
||||||
for i < len(p.s) && (isWhitespace(p.s[i]) || p.s[i] == '#') {
|
|
||||||
if p.s[i] == '#' {
|
|
||||||
// comment; skip to end of line or input
|
|
||||||
for i < len(p.s) && p.s[i] != '\n' {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
if i == len(p.s) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if p.s[i] == '\n' {
|
|
||||||
p.line++
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
p.offset += i
|
|
||||||
p.s = p.s[i:len(p.s)]
|
|
||||||
if len(p.s) == 0 {
|
|
||||||
p.done = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *textParser) advance() {
|
|
||||||
// Skip whitespace
|
|
||||||
p.skipWhitespace()
|
|
||||||
if p.done {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start of non-whitespace
|
|
||||||
p.cur.err = nil
|
|
||||||
p.cur.offset, p.cur.line = p.offset, p.line
|
|
||||||
p.cur.unquoted = ""
|
|
||||||
switch p.s[0] {
|
|
||||||
case '<', '>', '{', '}', ':', '[', ']', ';', ',', '/':
|
|
||||||
// Single symbol
|
|
||||||
p.cur.value, p.s = p.s[0:1], p.s[1:len(p.s)]
|
|
||||||
case '"', '\'':
|
|
||||||
// Quoted string
|
|
||||||
i := 1
|
|
||||||
for i < len(p.s) && p.s[i] != p.s[0] && p.s[i] != '\n' {
|
|
||||||
if p.s[i] == '\\' && i+1 < len(p.s) {
|
|
||||||
// skip escaped char
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
if i >= len(p.s) || p.s[i] != p.s[0] {
|
|
||||||
p.errorf("unmatched quote")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
unq, err := unquoteC(p.s[1:i], rune(p.s[0]))
|
|
||||||
if err != nil {
|
|
||||||
p.errorf("invalid quoted string %s: %v", p.s[0:i+1], err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
p.cur.value, p.s = p.s[0:i+1], p.s[i+1:len(p.s)]
|
|
||||||
p.cur.unquoted = unq
|
|
||||||
default:
|
|
||||||
i := 0
|
|
||||||
for i < len(p.s) && isIdentOrNumberChar(p.s[i]) {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
if i == 0 {
|
|
||||||
p.errorf("unexpected byte %#x", p.s[0])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
p.cur.value, p.s = p.s[0:i], p.s[i:len(p.s)]
|
|
||||||
}
|
|
||||||
p.offset += len(p.cur.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Back off the parser by one token. Can only be done between calls to next().
|
|
||||||
// It makes the next advance() a no-op.
|
|
||||||
func (p *textParser) back() { p.backed = true }
|
|
||||||
|
|
||||||
// Advances the parser and returns the new current token.
|
|
||||||
func (p *textParser) next() *token {
|
|
||||||
if p.backed || p.done {
|
|
||||||
p.backed = false
|
|
||||||
return &p.cur
|
|
||||||
}
|
|
||||||
p.advance()
|
|
||||||
if p.done {
|
|
||||||
p.cur.value = ""
|
|
||||||
} else if len(p.cur.value) > 0 && isQuote(p.cur.value[0]) {
|
|
||||||
// Look for multiple quoted strings separated by whitespace,
|
|
||||||
// and concatenate them.
|
|
||||||
cat := p.cur
|
|
||||||
for {
|
|
||||||
p.skipWhitespace()
|
|
||||||
if p.done || !isQuote(p.s[0]) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
p.advance()
|
|
||||||
if p.cur.err != nil {
|
|
||||||
return &p.cur
|
|
||||||
}
|
|
||||||
cat.value += " " + p.cur.value
|
|
||||||
cat.unquoted += p.cur.unquoted
|
|
||||||
}
|
|
||||||
p.done = false // parser may have seen EOF, but we want to return cat
|
|
||||||
p.cur = cat
|
|
||||||
}
|
|
||||||
return &p.cur
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *textParser) consumeToken(s string) error {
|
|
||||||
tok := p.next()
|
|
||||||
if tok.err != nil {
|
|
||||||
return tok.err
|
|
||||||
}
|
|
||||||
if tok.value != s {
|
|
||||||
p.back()
|
|
||||||
return p.errorf("expected %q, found %q", s, tok.value)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var errBadUTF8 = errors.New("proto: bad UTF-8")
|
|
||||||
|
|
||||||
func unquoteC(s string, quote rune) (string, error) {
|
|
||||||
// This is based on C++'s tokenizer.cc.
|
|
||||||
// Despite its name, this is *not* parsing C syntax.
|
|
||||||
// For instance, "\0" is an invalid quoted string.
|
|
||||||
|
|
||||||
// Avoid allocation in trivial cases.
|
|
||||||
simple := true
|
|
||||||
for _, r := range s {
|
|
||||||
if r == '\\' || r == quote {
|
|
||||||
simple = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if simple {
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
buf := make([]byte, 0, 3*len(s)/2)
|
|
||||||
for len(s) > 0 {
|
|
||||||
r, n := utf8.DecodeRuneInString(s)
|
|
||||||
if r == utf8.RuneError && n == 1 {
|
|
||||||
return "", errBadUTF8
|
|
||||||
}
|
|
||||||
s = s[n:]
|
|
||||||
if r != '\\' {
|
|
||||||
if r < utf8.RuneSelf {
|
|
||||||
buf = append(buf, byte(r))
|
|
||||||
} else {
|
|
||||||
buf = append(buf, string(r)...)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
ch, tail, err := unescape(s)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
buf = append(buf, ch...)
|
|
||||||
s = tail
|
|
||||||
}
|
|
||||||
return string(buf), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func unescape(s string) (ch string, tail string, err error) {
|
|
||||||
r, n := utf8.DecodeRuneInString(s)
|
|
||||||
if r == utf8.RuneError && n == 1 {
|
|
||||||
return "", "", errBadUTF8
|
|
||||||
}
|
|
||||||
s = s[n:]
|
|
||||||
switch r {
|
|
||||||
case 'a':
|
|
||||||
return "\a", s, nil
|
|
||||||
case 'b':
|
|
||||||
return "\b", s, nil
|
|
||||||
case 'f':
|
|
||||||
return "\f", s, nil
|
|
||||||
case 'n':
|
|
||||||
return "\n", s, nil
|
|
||||||
case 'r':
|
|
||||||
return "\r", s, nil
|
|
||||||
case 't':
|
|
||||||
return "\t", s, nil
|
|
||||||
case 'v':
|
|
||||||
return "\v", s, nil
|
|
||||||
case '?':
|
|
||||||
return "?", s, nil // trigraph workaround
|
|
||||||
case '\'', '"', '\\':
|
|
||||||
return string(r), s, nil
|
|
||||||
case '0', '1', '2', '3', '4', '5', '6', '7':
|
|
||||||
if len(s) < 2 {
|
|
||||||
return "", "", fmt.Errorf(`\%c requires 2 following digits`, r)
|
|
||||||
}
|
|
||||||
ss := string(r) + s[:2]
|
|
||||||
s = s[2:]
|
|
||||||
i, err := strconv.ParseUint(ss, 8, 8)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", fmt.Errorf(`\%s contains non-octal digits`, ss)
|
|
||||||
}
|
|
||||||
return string([]byte{byte(i)}), s, nil
|
|
||||||
case 'x', 'X', 'u', 'U':
|
|
||||||
var n int
|
|
||||||
switch r {
|
|
||||||
case 'x', 'X':
|
|
||||||
n = 2
|
|
||||||
case 'u':
|
|
||||||
n = 4
|
|
||||||
case 'U':
|
|
||||||
n = 8
|
|
||||||
}
|
|
||||||
if len(s) < n {
|
|
||||||
return "", "", fmt.Errorf(`\%c requires %d following digits`, r, n)
|
|
||||||
}
|
|
||||||
ss := s[:n]
|
|
||||||
s = s[n:]
|
|
||||||
i, err := strconv.ParseUint(ss, 16, 64)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", fmt.Errorf(`\%c%s contains non-hexadecimal digits`, r, ss)
|
|
||||||
}
|
|
||||||
if r == 'x' || r == 'X' {
|
|
||||||
return string([]byte{byte(i)}), s, nil
|
|
||||||
}
|
|
||||||
if i > utf8.MaxRune {
|
|
||||||
return "", "", fmt.Errorf(`\%c%s is not a valid Unicode code point`, r, ss)
|
|
||||||
}
|
|
||||||
return string(rune(i)), s, nil
|
|
||||||
}
|
|
||||||
return "", "", fmt.Errorf(`unknown escape \%c`, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
func isIdentOrNumberChar(c byte) bool {
|
|
||||||
switch {
|
|
||||||
case 'A' <= c && c <= 'Z', 'a' <= c && c <= 'z':
|
|
||||||
return true
|
|
||||||
case '0' <= c && c <= '9':
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
switch c {
|
|
||||||
case '-', '+', '.', '_':
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func isWhitespace(c byte) bool {
|
|
||||||
switch c {
|
|
||||||
case ' ', '\t', '\n', '\r':
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func isQuote(c byte) bool {
|
|
||||||
switch c {
|
|
||||||
case '"', '\'':
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
560
vendor/github.com/golang/protobuf/proto/text_encode.go
generated
vendored
560
vendor/github.com/golang/protobuf/proto/text_encode.go
generated
vendored
@@ -1,560 +0,0 @@
|
|||||||
// Copyright 2010 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package proto
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"math"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"google.golang.org/protobuf/encoding/prototext"
|
|
||||||
"google.golang.org/protobuf/encoding/protowire"
|
|
||||||
"google.golang.org/protobuf/proto"
|
|
||||||
"google.golang.org/protobuf/reflect/protoreflect"
|
|
||||||
"google.golang.org/protobuf/reflect/protoregistry"
|
|
||||||
)
|
|
||||||
|
|
||||||
const wrapTextMarshalV2 = false
|
|
||||||
|
|
||||||
// TextMarshaler is a configurable text format marshaler.
|
|
||||||
type TextMarshaler struct {
|
|
||||||
Compact bool // use compact text format (one line)
|
|
||||||
ExpandAny bool // expand google.protobuf.Any messages of known types
|
|
||||||
}
|
|
||||||
|
|
||||||
// Marshal writes the proto text format of m to w.
|
|
||||||
func (tm *TextMarshaler) Marshal(w io.Writer, m Message) error {
|
|
||||||
b, err := tm.marshal(m)
|
|
||||||
if len(b) > 0 {
|
|
||||||
if _, err := w.Write(b); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Text returns a proto text formatted string of m.
|
|
||||||
func (tm *TextMarshaler) Text(m Message) string {
|
|
||||||
b, _ := tm.marshal(m)
|
|
||||||
return string(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tm *TextMarshaler) marshal(m Message) ([]byte, error) {
|
|
||||||
mr := MessageReflect(m)
|
|
||||||
if mr == nil || !mr.IsValid() {
|
|
||||||
return []byte("<nil>"), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if wrapTextMarshalV2 {
|
|
||||||
if m, ok := m.(encoding.TextMarshaler); ok {
|
|
||||||
return m.MarshalText()
|
|
||||||
}
|
|
||||||
|
|
||||||
opts := prototext.MarshalOptions{
|
|
||||||
AllowPartial: true,
|
|
||||||
EmitUnknown: true,
|
|
||||||
}
|
|
||||||
if !tm.Compact {
|
|
||||||
opts.Indent = " "
|
|
||||||
}
|
|
||||||
if !tm.ExpandAny {
|
|
||||||
opts.Resolver = (*protoregistry.Types)(nil)
|
|
||||||
}
|
|
||||||
return opts.Marshal(mr.Interface())
|
|
||||||
} else {
|
|
||||||
w := &textWriter{
|
|
||||||
compact: tm.Compact,
|
|
||||||
expandAny: tm.ExpandAny,
|
|
||||||
complete: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
if m, ok := m.(encoding.TextMarshaler); ok {
|
|
||||||
b, err := m.MarshalText()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
w.Write(b)
|
|
||||||
return w.buf, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
err := w.writeMessage(mr)
|
|
||||||
return w.buf, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
defaultTextMarshaler = TextMarshaler{}
|
|
||||||
compactTextMarshaler = TextMarshaler{Compact: true}
|
|
||||||
)
|
|
||||||
|
|
||||||
// MarshalText writes the proto text format of m to w.
|
|
||||||
func MarshalText(w io.Writer, m Message) error { return defaultTextMarshaler.Marshal(w, m) }
|
|
||||||
|
|
||||||
// MarshalTextString returns a proto text formatted string of m.
|
|
||||||
func MarshalTextString(m Message) string { return defaultTextMarshaler.Text(m) }
|
|
||||||
|
|
||||||
// CompactText writes the compact proto text format of m to w.
|
|
||||||
func CompactText(w io.Writer, m Message) error { return compactTextMarshaler.Marshal(w, m) }
|
|
||||||
|
|
||||||
// CompactTextString returns a compact proto text formatted string of m.
|
|
||||||
func CompactTextString(m Message) string { return compactTextMarshaler.Text(m) }
|
|
||||||
|
|
||||||
var (
|
|
||||||
newline = []byte("\n")
|
|
||||||
endBraceNewline = []byte("}\n")
|
|
||||||
posInf = []byte("inf")
|
|
||||||
negInf = []byte("-inf")
|
|
||||||
nan = []byte("nan")
|
|
||||||
)
|
|
||||||
|
|
||||||
// textWriter is an io.Writer that tracks its indentation level.
|
|
||||||
type textWriter struct {
|
|
||||||
compact bool // same as TextMarshaler.Compact
|
|
||||||
expandAny bool // same as TextMarshaler.ExpandAny
|
|
||||||
complete bool // whether the current position is a complete line
|
|
||||||
indent int // indentation level; never negative
|
|
||||||
buf []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *textWriter) Write(p []byte) (n int, _ error) {
|
|
||||||
newlines := bytes.Count(p, newline)
|
|
||||||
if newlines == 0 {
|
|
||||||
if !w.compact && w.complete {
|
|
||||||
w.writeIndent()
|
|
||||||
}
|
|
||||||
w.buf = append(w.buf, p...)
|
|
||||||
w.complete = false
|
|
||||||
return len(p), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
frags := bytes.SplitN(p, newline, newlines+1)
|
|
||||||
if w.compact {
|
|
||||||
for i, frag := range frags {
|
|
||||||
if i > 0 {
|
|
||||||
w.buf = append(w.buf, ' ')
|
|
||||||
n++
|
|
||||||
}
|
|
||||||
w.buf = append(w.buf, frag...)
|
|
||||||
n += len(frag)
|
|
||||||
}
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, frag := range frags {
|
|
||||||
if w.complete {
|
|
||||||
w.writeIndent()
|
|
||||||
}
|
|
||||||
w.buf = append(w.buf, frag...)
|
|
||||||
n += len(frag)
|
|
||||||
if i+1 < len(frags) {
|
|
||||||
w.buf = append(w.buf, '\n')
|
|
||||||
n++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
w.complete = len(frags[len(frags)-1]) == 0
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *textWriter) WriteByte(c byte) error {
|
|
||||||
if w.compact && c == '\n' {
|
|
||||||
c = ' '
|
|
||||||
}
|
|
||||||
if !w.compact && w.complete {
|
|
||||||
w.writeIndent()
|
|
||||||
}
|
|
||||||
w.buf = append(w.buf, c)
|
|
||||||
w.complete = c == '\n'
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *textWriter) writeName(fd protoreflect.FieldDescriptor) {
|
|
||||||
if !w.compact && w.complete {
|
|
||||||
w.writeIndent()
|
|
||||||
}
|
|
||||||
w.complete = false
|
|
||||||
|
|
||||||
if fd.Kind() != protoreflect.GroupKind {
|
|
||||||
w.buf = append(w.buf, fd.Name()...)
|
|
||||||
w.WriteByte(':')
|
|
||||||
} else {
|
|
||||||
// Use message type name for group field name.
|
|
||||||
w.buf = append(w.buf, fd.Message().Name()...)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !w.compact {
|
|
||||||
w.WriteByte(' ')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func requiresQuotes(u string) bool {
|
|
||||||
// When type URL contains any characters except [0-9A-Za-z./\-]*, it must be quoted.
|
|
||||||
for _, ch := range u {
|
|
||||||
switch {
|
|
||||||
case ch == '.' || ch == '/' || ch == '_':
|
|
||||||
continue
|
|
||||||
case '0' <= ch && ch <= '9':
|
|
||||||
continue
|
|
||||||
case 'A' <= ch && ch <= 'Z':
|
|
||||||
continue
|
|
||||||
case 'a' <= ch && ch <= 'z':
|
|
||||||
continue
|
|
||||||
default:
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// writeProto3Any writes an expanded google.protobuf.Any message.
|
|
||||||
//
|
|
||||||
// It returns (false, nil) if sv value can't be unmarshaled (e.g. because
|
|
||||||
// required messages are not linked in).
|
|
||||||
//
|
|
||||||
// It returns (true, error) when sv was written in expanded format or an error
|
|
||||||
// was encountered.
|
|
||||||
func (w *textWriter) writeProto3Any(m protoreflect.Message) (bool, error) {
|
|
||||||
md := m.Descriptor()
|
|
||||||
fdURL := md.Fields().ByName("type_url")
|
|
||||||
fdVal := md.Fields().ByName("value")
|
|
||||||
|
|
||||||
url := m.Get(fdURL).String()
|
|
||||||
mt, err := protoregistry.GlobalTypes.FindMessageByURL(url)
|
|
||||||
if err != nil {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
b := m.Get(fdVal).Bytes()
|
|
||||||
m2 := mt.New()
|
|
||||||
if err := proto.Unmarshal(b, m2.Interface()); err != nil {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
w.Write([]byte("["))
|
|
||||||
if requiresQuotes(url) {
|
|
||||||
w.writeQuotedString(url)
|
|
||||||
} else {
|
|
||||||
w.Write([]byte(url))
|
|
||||||
}
|
|
||||||
if w.compact {
|
|
||||||
w.Write([]byte("]:<"))
|
|
||||||
} else {
|
|
||||||
w.Write([]byte("]: <\n"))
|
|
||||||
w.indent++
|
|
||||||
}
|
|
||||||
if err := w.writeMessage(m2); err != nil {
|
|
||||||
return true, err
|
|
||||||
}
|
|
||||||
if w.compact {
|
|
||||||
w.Write([]byte("> "))
|
|
||||||
} else {
|
|
||||||
w.indent--
|
|
||||||
w.Write([]byte(">\n"))
|
|
||||||
}
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *textWriter) writeMessage(m protoreflect.Message) error {
|
|
||||||
md := m.Descriptor()
|
|
||||||
if w.expandAny && md.FullName() == "google.protobuf.Any" {
|
|
||||||
if canExpand, err := w.writeProto3Any(m); canExpand {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fds := md.Fields()
|
|
||||||
for i := 0; i < fds.Len(); {
|
|
||||||
fd := fds.Get(i)
|
|
||||||
if od := fd.ContainingOneof(); od != nil {
|
|
||||||
fd = m.WhichOneof(od)
|
|
||||||
i += od.Fields().Len()
|
|
||||||
} else {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
if fd == nil || !m.Has(fd) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case fd.IsList():
|
|
||||||
lv := m.Get(fd).List()
|
|
||||||
for j := 0; j < lv.Len(); j++ {
|
|
||||||
w.writeName(fd)
|
|
||||||
v := lv.Get(j)
|
|
||||||
if err := w.writeSingularValue(v, fd); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
w.WriteByte('\n')
|
|
||||||
}
|
|
||||||
case fd.IsMap():
|
|
||||||
kfd := fd.MapKey()
|
|
||||||
vfd := fd.MapValue()
|
|
||||||
mv := m.Get(fd).Map()
|
|
||||||
|
|
||||||
type entry struct{ key, val protoreflect.Value }
|
|
||||||
var entries []entry
|
|
||||||
mv.Range(func(k protoreflect.MapKey, v protoreflect.Value) bool {
|
|
||||||
entries = append(entries, entry{k.Value(), v})
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
sort.Slice(entries, func(i, j int) bool {
|
|
||||||
switch kfd.Kind() {
|
|
||||||
case protoreflect.BoolKind:
|
|
||||||
return !entries[i].key.Bool() && entries[j].key.Bool()
|
|
||||||
case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind, protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind:
|
|
||||||
return entries[i].key.Int() < entries[j].key.Int()
|
|
||||||
case protoreflect.Uint32Kind, protoreflect.Fixed32Kind, protoreflect.Uint64Kind, protoreflect.Fixed64Kind:
|
|
||||||
return entries[i].key.Uint() < entries[j].key.Uint()
|
|
||||||
case protoreflect.StringKind:
|
|
||||||
return entries[i].key.String() < entries[j].key.String()
|
|
||||||
default:
|
|
||||||
panic("invalid kind")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
for _, entry := range entries {
|
|
||||||
w.writeName(fd)
|
|
||||||
w.WriteByte('<')
|
|
||||||
if !w.compact {
|
|
||||||
w.WriteByte('\n')
|
|
||||||
}
|
|
||||||
w.indent++
|
|
||||||
w.writeName(kfd)
|
|
||||||
if err := w.writeSingularValue(entry.key, kfd); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
w.WriteByte('\n')
|
|
||||||
w.writeName(vfd)
|
|
||||||
if err := w.writeSingularValue(entry.val, vfd); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
w.WriteByte('\n')
|
|
||||||
w.indent--
|
|
||||||
w.WriteByte('>')
|
|
||||||
w.WriteByte('\n')
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
w.writeName(fd)
|
|
||||||
if err := w.writeSingularValue(m.Get(fd), fd); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
w.WriteByte('\n')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if b := m.GetUnknown(); len(b) > 0 {
|
|
||||||
w.writeUnknownFields(b)
|
|
||||||
}
|
|
||||||
return w.writeExtensions(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *textWriter) writeSingularValue(v protoreflect.Value, fd protoreflect.FieldDescriptor) error {
|
|
||||||
switch fd.Kind() {
|
|
||||||
case protoreflect.FloatKind, protoreflect.DoubleKind:
|
|
||||||
switch vf := v.Float(); {
|
|
||||||
case math.IsInf(vf, +1):
|
|
||||||
w.Write(posInf)
|
|
||||||
case math.IsInf(vf, -1):
|
|
||||||
w.Write(negInf)
|
|
||||||
case math.IsNaN(vf):
|
|
||||||
w.Write(nan)
|
|
||||||
default:
|
|
||||||
fmt.Fprint(w, v.Interface())
|
|
||||||
}
|
|
||||||
case protoreflect.StringKind:
|
|
||||||
// NOTE: This does not validate UTF-8 for historical reasons.
|
|
||||||
w.writeQuotedString(string(v.String()))
|
|
||||||
case protoreflect.BytesKind:
|
|
||||||
w.writeQuotedString(string(v.Bytes()))
|
|
||||||
case protoreflect.MessageKind, protoreflect.GroupKind:
|
|
||||||
var bra, ket byte = '<', '>'
|
|
||||||
if fd.Kind() == protoreflect.GroupKind {
|
|
||||||
bra, ket = '{', '}'
|
|
||||||
}
|
|
||||||
w.WriteByte(bra)
|
|
||||||
if !w.compact {
|
|
||||||
w.WriteByte('\n')
|
|
||||||
}
|
|
||||||
w.indent++
|
|
||||||
m := v.Message()
|
|
||||||
if m2, ok := m.Interface().(encoding.TextMarshaler); ok {
|
|
||||||
b, err := m2.MarshalText()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
w.Write(b)
|
|
||||||
} else {
|
|
||||||
w.writeMessage(m)
|
|
||||||
}
|
|
||||||
w.indent--
|
|
||||||
w.WriteByte(ket)
|
|
||||||
case protoreflect.EnumKind:
|
|
||||||
if ev := fd.Enum().Values().ByNumber(v.Enum()); ev != nil {
|
|
||||||
fmt.Fprint(w, ev.Name())
|
|
||||||
} else {
|
|
||||||
fmt.Fprint(w, v.Enum())
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
fmt.Fprint(w, v.Interface())
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// writeQuotedString writes a quoted string in the protocol buffer text format.
|
|
||||||
func (w *textWriter) writeQuotedString(s string) {
|
|
||||||
w.WriteByte('"')
|
|
||||||
for i := 0; i < len(s); i++ {
|
|
||||||
switch c := s[i]; c {
|
|
||||||
case '\n':
|
|
||||||
w.buf = append(w.buf, `\n`...)
|
|
||||||
case '\r':
|
|
||||||
w.buf = append(w.buf, `\r`...)
|
|
||||||
case '\t':
|
|
||||||
w.buf = append(w.buf, `\t`...)
|
|
||||||
case '"':
|
|
||||||
w.buf = append(w.buf, `\"`...)
|
|
||||||
case '\\':
|
|
||||||
w.buf = append(w.buf, `\\`...)
|
|
||||||
default:
|
|
||||||
if isPrint := c >= 0x20 && c < 0x7f; isPrint {
|
|
||||||
w.buf = append(w.buf, c)
|
|
||||||
} else {
|
|
||||||
w.buf = append(w.buf, fmt.Sprintf(`\%03o`, c)...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
w.WriteByte('"')
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *textWriter) writeUnknownFields(b []byte) {
|
|
||||||
if !w.compact {
|
|
||||||
fmt.Fprintf(w, "/* %d unknown bytes */\n", len(b))
|
|
||||||
}
|
|
||||||
|
|
||||||
for len(b) > 0 {
|
|
||||||
num, wtyp, n := protowire.ConsumeTag(b)
|
|
||||||
if n < 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
b = b[n:]
|
|
||||||
|
|
||||||
if wtyp == protowire.EndGroupType {
|
|
||||||
w.indent--
|
|
||||||
w.Write(endBraceNewline)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
fmt.Fprint(w, num)
|
|
||||||
if wtyp != protowire.StartGroupType {
|
|
||||||
w.WriteByte(':')
|
|
||||||
}
|
|
||||||
if !w.compact || wtyp == protowire.StartGroupType {
|
|
||||||
w.WriteByte(' ')
|
|
||||||
}
|
|
||||||
switch wtyp {
|
|
||||||
case protowire.VarintType:
|
|
||||||
v, n := protowire.ConsumeVarint(b)
|
|
||||||
if n < 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
b = b[n:]
|
|
||||||
fmt.Fprint(w, v)
|
|
||||||
case protowire.Fixed32Type:
|
|
||||||
v, n := protowire.ConsumeFixed32(b)
|
|
||||||
if n < 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
b = b[n:]
|
|
||||||
fmt.Fprint(w, v)
|
|
||||||
case protowire.Fixed64Type:
|
|
||||||
v, n := protowire.ConsumeFixed64(b)
|
|
||||||
if n < 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
b = b[n:]
|
|
||||||
fmt.Fprint(w, v)
|
|
||||||
case protowire.BytesType:
|
|
||||||
v, n := protowire.ConsumeBytes(b)
|
|
||||||
if n < 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
b = b[n:]
|
|
||||||
fmt.Fprintf(w, "%q", v)
|
|
||||||
case protowire.StartGroupType:
|
|
||||||
w.WriteByte('{')
|
|
||||||
w.indent++
|
|
||||||
default:
|
|
||||||
fmt.Fprintf(w, "/* unknown wire type %d */", wtyp)
|
|
||||||
}
|
|
||||||
w.WriteByte('\n')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// writeExtensions writes all the extensions in m.
|
|
||||||
func (w *textWriter) writeExtensions(m protoreflect.Message) error {
|
|
||||||
md := m.Descriptor()
|
|
||||||
if md.ExtensionRanges().Len() == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type ext struct {
|
|
||||||
desc protoreflect.FieldDescriptor
|
|
||||||
val protoreflect.Value
|
|
||||||
}
|
|
||||||
var exts []ext
|
|
||||||
m.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
|
|
||||||
if fd.IsExtension() {
|
|
||||||
exts = append(exts, ext{fd, v})
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
sort.Slice(exts, func(i, j int) bool {
|
|
||||||
return exts[i].desc.Number() < exts[j].desc.Number()
|
|
||||||
})
|
|
||||||
|
|
||||||
for _, ext := range exts {
|
|
||||||
// For message set, use the name of the message as the extension name.
|
|
||||||
name := string(ext.desc.FullName())
|
|
||||||
if isMessageSet(ext.desc.ContainingMessage()) {
|
|
||||||
name = strings.TrimSuffix(name, ".message_set_extension")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !ext.desc.IsList() {
|
|
||||||
if err := w.writeSingularExtension(name, ext.val, ext.desc); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
lv := ext.val.List()
|
|
||||||
for i := 0; i < lv.Len(); i++ {
|
|
||||||
if err := w.writeSingularExtension(name, lv.Get(i), ext.desc); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *textWriter) writeSingularExtension(name string, v protoreflect.Value, fd protoreflect.FieldDescriptor) error {
|
|
||||||
fmt.Fprintf(w, "[%s]:", name)
|
|
||||||
if !w.compact {
|
|
||||||
w.WriteByte(' ')
|
|
||||||
}
|
|
||||||
if err := w.writeSingularValue(v, fd); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
w.WriteByte('\n')
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *textWriter) writeIndent() {
|
|
||||||
if !w.complete {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for i := 0; i < w.indent*2; i++ {
|
|
||||||
w.buf = append(w.buf, ' ')
|
|
||||||
}
|
|
||||||
w.complete = false
|
|
||||||
}
|
|
||||||
78
vendor/github.com/golang/protobuf/proto/wire.go
generated
vendored
78
vendor/github.com/golang/protobuf/proto/wire.go
generated
vendored
@@ -1,78 +0,0 @@
|
|||||||
// Copyright 2019 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package proto
|
|
||||||
|
|
||||||
import (
|
|
||||||
protoV2 "google.golang.org/protobuf/proto"
|
|
||||||
"google.golang.org/protobuf/runtime/protoiface"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Size returns the size in bytes of the wire-format encoding of m.
|
|
||||||
func Size(m Message) int {
|
|
||||||
if m == nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
mi := MessageV2(m)
|
|
||||||
return protoV2.Size(mi)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Marshal returns the wire-format encoding of m.
|
|
||||||
func Marshal(m Message) ([]byte, error) {
|
|
||||||
b, err := marshalAppend(nil, m, false)
|
|
||||||
if b == nil {
|
|
||||||
b = zeroBytes
|
|
||||||
}
|
|
||||||
return b, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var zeroBytes = make([]byte, 0, 0)
|
|
||||||
|
|
||||||
func marshalAppend(buf []byte, m Message, deterministic bool) ([]byte, error) {
|
|
||||||
if m == nil {
|
|
||||||
return nil, ErrNil
|
|
||||||
}
|
|
||||||
mi := MessageV2(m)
|
|
||||||
nbuf, err := protoV2.MarshalOptions{
|
|
||||||
Deterministic: deterministic,
|
|
||||||
AllowPartial: true,
|
|
||||||
}.MarshalAppend(buf, mi)
|
|
||||||
if err != nil {
|
|
||||||
return buf, err
|
|
||||||
}
|
|
||||||
if len(buf) == len(nbuf) {
|
|
||||||
if !mi.ProtoReflect().IsValid() {
|
|
||||||
return buf, ErrNil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nbuf, checkRequiredNotSet(mi)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unmarshal parses a wire-format message in b and places the decoded results in m.
|
|
||||||
//
|
|
||||||
// Unmarshal resets m before starting to unmarshal, so any existing data in m is always
|
|
||||||
// removed. Use UnmarshalMerge to preserve and append to existing data.
|
|
||||||
func Unmarshal(b []byte, m Message) error {
|
|
||||||
m.Reset()
|
|
||||||
return UnmarshalMerge(b, m)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalMerge parses a wire-format message in b and places the decoded results in m.
|
|
||||||
func UnmarshalMerge(b []byte, m Message) error {
|
|
||||||
mi := MessageV2(m)
|
|
||||||
out, err := protoV2.UnmarshalOptions{
|
|
||||||
AllowPartial: true,
|
|
||||||
Merge: true,
|
|
||||||
}.UnmarshalState(protoiface.UnmarshalInput{
|
|
||||||
Buf: b,
|
|
||||||
Message: mi.ProtoReflect(),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if out.Flags&protoiface.UnmarshalInitialized > 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return checkRequiredNotSet(mi)
|
|
||||||
}
|
|
||||||
34
vendor/github.com/golang/protobuf/proto/wrappers.go
generated
vendored
34
vendor/github.com/golang/protobuf/proto/wrappers.go
generated
vendored
@@ -1,34 +0,0 @@
|
|||||||
// Copyright 2019 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package proto
|
|
||||||
|
|
||||||
// Bool stores v in a new bool value and returns a pointer to it.
|
|
||||||
func Bool(v bool) *bool { return &v }
|
|
||||||
|
|
||||||
// Int stores v in a new int32 value and returns a pointer to it.
|
|
||||||
//
|
|
||||||
// Deprecated: Use Int32 instead.
|
|
||||||
func Int(v int) *int32 { return Int32(int32(v)) }
|
|
||||||
|
|
||||||
// Int32 stores v in a new int32 value and returns a pointer to it.
|
|
||||||
func Int32(v int32) *int32 { return &v }
|
|
||||||
|
|
||||||
// Int64 stores v in a new int64 value and returns a pointer to it.
|
|
||||||
func Int64(v int64) *int64 { return &v }
|
|
||||||
|
|
||||||
// Uint32 stores v in a new uint32 value and returns a pointer to it.
|
|
||||||
func Uint32(v uint32) *uint32 { return &v }
|
|
||||||
|
|
||||||
// Uint64 stores v in a new uint64 value and returns a pointer to it.
|
|
||||||
func Uint64(v uint64) *uint64 { return &v }
|
|
||||||
|
|
||||||
// Float32 stores v in a new float32 value and returns a pointer to it.
|
|
||||||
func Float32(v float32) *float32 { return &v }
|
|
||||||
|
|
||||||
// Float64 stores v in a new float64 value and returns a pointer to it.
|
|
||||||
func Float64(v float64) *float64 { return &v }
|
|
||||||
|
|
||||||
// String stores v in a new string value and returns a pointer to it.
|
|
||||||
func String(v string) *string { return &v }
|
|
||||||
3
vendor/go.opentelemetry.io/otel/.gitattributes
generated
vendored
Normal file
3
vendor/go.opentelemetry.io/otel/.gitattributes
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
* text=auto eol=lf
|
||||||
|
*.{cmd,[cC][mM][dD]} text eol=crlf
|
||||||
|
*.{bat,[bB][aA][tT]} text eol=crlf
|
||||||
21
vendor/go.opentelemetry.io/otel/.gitignore
generated
vendored
Normal file
21
vendor/go.opentelemetry.io/otel/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
.tools/
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
*.iml
|
||||||
|
*.so
|
||||||
|
coverage.*
|
||||||
|
|
||||||
|
gen/
|
||||||
|
|
||||||
|
/example/fib/fib
|
||||||
|
/example/jaeger/jaeger
|
||||||
|
/example/namedtracer/namedtracer
|
||||||
|
/example/opencensus/opencensus
|
||||||
|
/example/passthrough/passthrough
|
||||||
|
/example/prometheus/prometheus
|
||||||
|
/example/prom-collector/prom-collector
|
||||||
|
/example/zipkin/zipkin
|
||||||
|
/example/otel-collector/otel-collector
|
||||||
3
vendor/go.opentelemetry.io/otel/.gitmodules
generated
vendored
Normal file
3
vendor/go.opentelemetry.io/otel/.gitmodules
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule "opentelemetry-proto"]
|
||||||
|
path = exporters/otlp/internal/opentelemetry-proto
|
||||||
|
url = https://github.com/open-telemetry/opentelemetry-proto
|
||||||
32
vendor/go.opentelemetry.io/otel/.golangci.yml
generated
vendored
Normal file
32
vendor/go.opentelemetry.io/otel/.golangci.yml
generated
vendored
Normal file
@@ -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
|
||||||
|
- revive
|
||||||
|
- 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:
|
||||||
|
- revive
|
||||||
|
# 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:
|
||||||
|
- revive
|
||||||
|
|
||||||
|
linters-settings:
|
||||||
|
misspell:
|
||||||
|
locale: US
|
||||||
|
ignore-words:
|
||||||
|
- cancelled
|
||||||
|
goimports:
|
||||||
|
local-prefixes: go.opentelemetry.io
|
||||||
16
vendor/go.opentelemetry.io/otel/.markdown-link.json
generated
vendored
Normal file
16
vendor/go.opentelemetry.io/otel/.markdown-link.json
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"ignorePatterns": [
|
||||||
|
{
|
||||||
|
"pattern": "^http(s)?://localhost"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"replacementPatterns": [
|
||||||
|
{
|
||||||
|
"pattern": "^/registry",
|
||||||
|
"replacement": "https://opentelemetry.io/registry"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"retryOn429": true,
|
||||||
|
"retryCount": 5,
|
||||||
|
"fallbackRetryDelay": "30s"
|
||||||
|
}
|
||||||
29
vendor/go.opentelemetry.io/otel/.markdownlint.yaml
generated
vendored
Normal file
29
vendor/go.opentelemetry.io/otel/.markdownlint.yaml
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# Default state for all rules
|
||||||
|
default: true
|
||||||
|
|
||||||
|
# ul-style
|
||||||
|
MD004: false
|
||||||
|
|
||||||
|
# hard-tabs
|
||||||
|
MD010: false
|
||||||
|
|
||||||
|
# line-length
|
||||||
|
MD013: false
|
||||||
|
|
||||||
|
# no-duplicate-header
|
||||||
|
MD024:
|
||||||
|
siblings_only: true
|
||||||
|
|
||||||
|
#single-title
|
||||||
|
MD025: false
|
||||||
|
|
||||||
|
# ol-prefix
|
||||||
|
MD029:
|
||||||
|
style: ordered
|
||||||
|
|
||||||
|
# no-inline-html
|
||||||
|
MD033: false
|
||||||
|
|
||||||
|
# fenced-code-language
|
||||||
|
MD040: false
|
||||||
|
|
||||||
1681
vendor/go.opentelemetry.io/otel/CHANGELOG.md
generated
vendored
Normal file
1681
vendor/go.opentelemetry.io/otel/CHANGELOG.md
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
17
vendor/go.opentelemetry.io/otel/CODEOWNERS
generated
vendored
Normal file
17
vendor/go.opentelemetry.io/otel/CODEOWNERS
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#####################################################
|
||||||
|
#
|
||||||
|
# List of approvers for this repository
|
||||||
|
#
|
||||||
|
#####################################################
|
||||||
|
#
|
||||||
|
# Learn about membership in OpenTelemetry community:
|
||||||
|
# https://github.com/open-telemetry/community/blob/main/community-membership.md
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Learn about CODEOWNERS file format:
|
||||||
|
# https://help.github.com/en/articles/about-code-owners
|
||||||
|
#
|
||||||
|
|
||||||
|
* @jmacd @MrAlias @Aneurysm9 @evantorrie @XSAM @dashpole @paivagustavo @MadVikingGod @pellared
|
||||||
|
|
||||||
|
CODEOWNERS @MrAlias @Aneurysm9 @MadVikingGod
|
||||||
495
vendor/go.opentelemetry.io/otel/CONTRIBUTING.md
generated
vendored
Normal file
495
vendor/go.opentelemetry.io/otel/CONTRIBUTING.md
generated
vendored
Normal file
@@ -0,0 +1,495 @@
|
|||||||
|
# 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
|
||||||
|
[Slack](https://cloud-native.slack.com/archives/C01NPAXACKT).
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
You can view and edit the source code by cloning this repository:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
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.
|
||||||
|
* Feedback has been addressed.
|
||||||
|
* Any substantive changes to your PR will require that you clear any prior
|
||||||
|
Approval reviews, this includes changes resulting from other feedback. Unless
|
||||||
|
the approver explicitly stated that their approval will persist across
|
||||||
|
changes it should be assumed that the PR needs their review again. Other
|
||||||
|
project members (e.g. approvers, maintainers) can help with this if there are
|
||||||
|
any questions or if you forget to clear reviews.
|
||||||
|
* 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.
|
||||||
|
* `README.md` has been updated if necessary.
|
||||||
|
* 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/main/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
|
||||||
|
[this](https://github.com/open-telemetry/opentelemetry-specification/issues/165).
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
Each non-example Go Module should have its own `README.md` containing:
|
||||||
|
|
||||||
|
- A pkg.go.dev badge which can be generated [here](https://pkg.go.dev/badge/).
|
||||||
|
- Brief description.
|
||||||
|
- Installation instructions (and requirements if applicable).
|
||||||
|
- Hyperlink to an example. Depending on the component the example can be:
|
||||||
|
- An `example_test.go` like [here](exporters/stdout/stdouttrace/example_test.go).
|
||||||
|
- A sample Go application with its own `README.md`, like [here](example/zipkin).
|
||||||
|
- Additional documentation sections such us:
|
||||||
|
- Configuration,
|
||||||
|
- Contributing,
|
||||||
|
- References.
|
||||||
|
|
||||||
|
[Here](exporters/jaeger/README.md) is an example of a concise `README.md`.
|
||||||
|
|
||||||
|
Moreover, it should be possible to navigate to any `README.md` from the
|
||||||
|
root `README.md`.
|
||||||
|
|
||||||
|
## 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 `type T 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 type must contain configuration options.
|
||||||
|
|
||||||
|
```go
|
||||||
|
// config contains configuration options for a thing.
|
||||||
|
type config struct {
|
||||||
|
// options ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In general the `config` type 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 internal `config` are not shared across package boundaries.
|
||||||
|
Meaning a `config` from one package should not be directly used by another. The
|
||||||
|
one exception is the API packages. The configs from the base API, eg.
|
||||||
|
`go.opentelemetry.io/otel/trace.TracerConfig` and
|
||||||
|
`go.opentelemetry.io/otel/metric.InstrumentConfig`, are intended to be consumed
|
||||||
|
by the SDK therefor it is expected that these are exported.
|
||||||
|
|
||||||
|
When a config is exported we want to maintain forward and backward
|
||||||
|
compatibility, to achieve this no fields should be exported but should
|
||||||
|
instead be accessed by methods.
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Having `apply` unexported makes sure that it will not be used externally.
|
||||||
|
Moreover, the interface becomes sealed so the user cannot easily implement
|
||||||
|
the interface on its own.
|
||||||
|
|
||||||
|
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}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Functional Options
|
||||||
|
|
||||||
|
```go
|
||||||
|
type optionFunc func(*config)
|
||||||
|
|
||||||
|
func (fn optionFunc) apply(c *config) {
|
||||||
|
fn(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithMyType sets t as MyType.
|
||||||
|
func WithMyType(t MyType) Option {
|
||||||
|
return optionFunc(func(c *config) {
|
||||||
|
c.MyType = t
|
||||||
|
})
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Instantiation
|
||||||
|
|
||||||
|
Using this configuration pattern to configure instantiation with a `NewT`
|
||||||
|
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 {…}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Interfaces
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
#### Interface Stability
|
||||||
|
|
||||||
|
All exported stable interfaces that include the following warning in their
|
||||||
|
doumentation are allowed to be extended with additional methods.
|
||||||
|
|
||||||
|
> Warning: methods may be added to this interface in minor releases.
|
||||||
|
|
||||||
|
Otherwise, stable interfaces MUST NOT be modified.
|
||||||
|
|
||||||
|
If new functionality is needed for an interface that cannot be changed it MUST
|
||||||
|
be added by including an additional interface. That added interface can be a
|
||||||
|
simple interface for the specific functionality that you want to add or it can
|
||||||
|
be a super-set of the original interface. For example, if you wanted to a
|
||||||
|
`Close` method to the `Exporter` interface:
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Exporter interface {
|
||||||
|
Export()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
A new interface, `Closer`, can be added:
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Closer interface {
|
||||||
|
Close()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Code that is passed the `Exporter` interface can now check to see if the passed
|
||||||
|
value also satisfies the new interface. E.g.
|
||||||
|
|
||||||
|
```go
|
||||||
|
func caller(e Exporter) {
|
||||||
|
/* ... */
|
||||||
|
if c, ok := e.(Closer); ok {
|
||||||
|
c.Close()
|
||||||
|
}
|
||||||
|
/* ... */
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternatively, a new type that is the super-set of an `Exporter` can be created.
|
||||||
|
|
||||||
|
```go
|
||||||
|
type ClosingExporter struct {
|
||||||
|
Exporter
|
||||||
|
Close()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This new type can be used similar to the simple interface above in that a
|
||||||
|
passed `Exporter` type can be asserted to satisfy the `ClosingExporter` type
|
||||||
|
and the `Close` method called.
|
||||||
|
|
||||||
|
This super-set approach can be useful if there is explicit behavior that needs
|
||||||
|
to be coupled with the original type and passed as a unified type to a new
|
||||||
|
function, but, because of this coupling, it also limits the applicability of
|
||||||
|
the added functionality. If there exist other interfaces where this
|
||||||
|
functionality should be added, each one will need their own super-set
|
||||||
|
interfaces and will duplicate the pattern. For this reason, the simple targeted
|
||||||
|
interface that defines the specific functionality should be preferred.
|
||||||
|
|
||||||
|
## Approvers and Maintainers
|
||||||
|
|
||||||
|
Approvers:
|
||||||
|
|
||||||
|
- [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
|
||||||
|
- [Gustavo Silva Paiva](https://github.com/paivagustavo), LightStep
|
||||||
|
- [Robert Pająk](https://github.com/pellared), Splunk
|
||||||
|
|
||||||
|
Maintainers:
|
||||||
|
|
||||||
|
- [Aaron Clawson](https://github.com/MadVikingGod), LightStep
|
||||||
|
- [Anthony Mirabella](https://github.com/Aneurysm9), AWS
|
||||||
|
- [Tyler Yahn](https://github.com/MrAlias), Splunk
|
||||||
|
|
||||||
|
### Become an Approver or a Maintainer
|
||||||
|
|
||||||
|
See the [community membership document in OpenTelemetry community
|
||||||
|
repo](https://github.com/open-telemetry/community/blob/main/community-membership.md).
|
||||||
201
vendor/go.opentelemetry.io/otel/LICENSE
generated
vendored
Normal file
201
vendor/go.opentelemetry.io/otel/LICENSE
generated
vendored
Normal file
@@ -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.
|
||||||
207
vendor/go.opentelemetry.io/otel/Makefile
generated
vendored
Normal file
207
vendor/go.opentelemetry.io/otel/Makefile
generated
vendored
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
# 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)
|
||||||
|
|
||||||
|
GO = go
|
||||||
|
TIMEOUT = 60
|
||||||
|
|
||||||
|
.DEFAULT_GOAL := precommit
|
||||||
|
|
||||||
|
.PHONY: precommit ci
|
||||||
|
precommit: dependabot-check license-check lint build examples test-default
|
||||||
|
ci: precommit check-clean-work-tree test-coverage
|
||||||
|
|
||||||
|
# Tools
|
||||||
|
|
||||||
|
TOOLS = $(CURDIR)/.tools
|
||||||
|
|
||||||
|
$(TOOLS):
|
||||||
|
@mkdir -p $@
|
||||||
|
$(TOOLS)/%: | $(TOOLS)
|
||||||
|
cd $(TOOLS_MOD_DIR) && \
|
||||||
|
$(GO) build -o $@ $(PACKAGE)
|
||||||
|
|
||||||
|
MULTIMOD = $(TOOLS)/multimod
|
||||||
|
$(TOOLS)/multimod: PACKAGE=go.opentelemetry.io/build-tools/multimod
|
||||||
|
|
||||||
|
SEMCONVGEN = $(TOOLS)/semconvgen
|
||||||
|
$(TOOLS)/semconvgen: PACKAGE=go.opentelemetry.io/build-tools/semconvgen
|
||||||
|
|
||||||
|
CROSSLINK = $(TOOLS)/crosslink
|
||||||
|
$(TOOLS)/crosslink: PACKAGE=go.opentelemetry.io/otel/$(TOOLS_MOD_DIR)/crosslink
|
||||||
|
|
||||||
|
GOLANGCI_LINT = $(TOOLS)/golangci-lint
|
||||||
|
$(TOOLS)/golangci-lint: PACKAGE=github.com/golangci/golangci-lint/cmd/golangci-lint
|
||||||
|
|
||||||
|
MISSPELL = $(TOOLS)/misspell
|
||||||
|
$(TOOLS)/misspell: PACKAGE=github.com/client9/misspell/cmd/misspell
|
||||||
|
|
||||||
|
GOCOVMERGE = $(TOOLS)/gocovmerge
|
||||||
|
$(TOOLS)/gocovmerge: PACKAGE=github.com/wadey/gocovmerge
|
||||||
|
|
||||||
|
STRINGER = $(TOOLS)/stringer
|
||||||
|
$(TOOLS)/stringer: PACKAGE=golang.org/x/tools/cmd/stringer
|
||||||
|
|
||||||
|
PORTO = $(TOOLS)/porto
|
||||||
|
$(TOOLS)/porto: PACKAGE=github.com/jcchavezs/porto/cmd/porto
|
||||||
|
|
||||||
|
GOJQ = $(TOOLS)/gojq
|
||||||
|
$(TOOLS)/gojq: PACKAGE=github.com/itchyny/gojq/cmd/gojq
|
||||||
|
|
||||||
|
.PHONY: tools
|
||||||
|
tools: $(CROSSLINK) $(GOLANGCI_LINT) $(MISSPELL) $(GOCOVMERGE) $(STRINGER) $(PORTO) $(GOJQ) $(SEMCONVGEN) $(MULTIMOD)
|
||||||
|
|
||||||
|
# Build
|
||||||
|
|
||||||
|
.PHONY: examples generate build
|
||||||
|
examples:
|
||||||
|
@set -e; for dir in $(EXAMPLES); do \
|
||||||
|
echo "$(GO) build $${dir}/..."; \
|
||||||
|
(cd "$${dir}" && \
|
||||||
|
$(GO) build .); \
|
||||||
|
done
|
||||||
|
|
||||||
|
generate: $(STRINGER) $(PORTO)
|
||||||
|
set -e; for dir in $(ALL_GO_MOD_DIRS); do \
|
||||||
|
echo "$(GO) generate $${dir}/..."; \
|
||||||
|
(cd "$${dir}" && \
|
||||||
|
PATH="$(TOOLS):$${PATH}" $(GO) generate ./... && \
|
||||||
|
$(PORTO) -w .); \
|
||||||
|
done
|
||||||
|
|
||||||
|
build: generate
|
||||||
|
# Build all package code including testing code.
|
||||||
|
set -e; for dir in $(ALL_GO_MOD_DIRS); do \
|
||||||
|
echo "$(GO) build $${dir}/..."; \
|
||||||
|
(cd "$${dir}" && \
|
||||||
|
$(GO) build ./... && \
|
||||||
|
$(GO) list ./... \
|
||||||
|
| grep -v third_party \
|
||||||
|
| xargs $(GO) test -vet=off -run xxxxxMatchNothingxxxxx >/dev/null); \
|
||||||
|
done
|
||||||
|
|
||||||
|
# Tests
|
||||||
|
|
||||||
|
TEST_TARGETS := test-default test-bench test-short test-verbose test-race
|
||||||
|
.PHONY: $(TEST_TARGETS) test
|
||||||
|
test-default: ARGS=-v -race
|
||||||
|
test-bench: ARGS=-run=xxxxxMatchNothingxxxxx -test.benchtime=1ms -bench=.
|
||||||
|
test-short: ARGS=-short
|
||||||
|
test-verbose: ARGS=-v
|
||||||
|
test-race: ARGS=-race
|
||||||
|
$(TEST_TARGETS): test
|
||||||
|
test:
|
||||||
|
@set -e; for dir in $(ALL_GO_MOD_DIRS); do \
|
||||||
|
echo "$(GO) test -timeout $(TIMEOUT)s $(ARGS) $${dir}/..."; \
|
||||||
|
(cd "$${dir}" && \
|
||||||
|
$(GO) list ./... \
|
||||||
|
| grep -v third_party \
|
||||||
|
| xargs $(GO) test -timeout $(TIMEOUT)s $(ARGS)); \
|
||||||
|
done
|
||||||
|
|
||||||
|
COVERAGE_MODE = atomic
|
||||||
|
COVERAGE_PROFILE = coverage.out
|
||||||
|
.PHONY: test-coverage
|
||||||
|
test-coverage: | $(GOCOVMERGE)
|
||||||
|
@set -e; \
|
||||||
|
printf "" > coverage.txt; \
|
||||||
|
for dir in $(ALL_COVERAGE_MOD_DIRS); do \
|
||||||
|
echo "$(GO) test -coverpkg=go.opentelemetry.io/otel/... -covermode=$(COVERAGE_MODE) -coverprofile="$(COVERAGE_PROFILE)" $${dir}/..."; \
|
||||||
|
(cd "$${dir}" && \
|
||||||
|
$(GO) list ./... \
|
||||||
|
| grep -v third_party \
|
||||||
|
| xargs $(GO) test -coverpkg=./... -covermode=$(COVERAGE_MODE) -coverprofile="$(COVERAGE_PROFILE)" && \
|
||||||
|
$(GO) tool cover -html=coverage.out -o coverage.html); \
|
||||||
|
done; \
|
||||||
|
$(GOCOVMERGE) $$(find . -name coverage.out) > coverage.txt
|
||||||
|
|
||||||
|
.PHONY: lint
|
||||||
|
lint: misspell lint-modules | $(GOLANGCI_LINT)
|
||||||
|
set -e; for dir in $(ALL_GO_MOD_DIRS); do \
|
||||||
|
echo "golangci-lint in $${dir}"; \
|
||||||
|
(cd "$${dir}" && \
|
||||||
|
$(GOLANGCI_LINT) run --fix && \
|
||||||
|
$(GOLANGCI_LINT) run); \
|
||||||
|
done
|
||||||
|
|
||||||
|
.PHONY: vanity-import-check
|
||||||
|
vanity-import-check: | $(PORTO)
|
||||||
|
$(PORTO) --include-internal -l .
|
||||||
|
|
||||||
|
.PHONY: misspell
|
||||||
|
misspell: | $(MISSPELL)
|
||||||
|
$(MISSPELL) -w $(ALL_DOCS)
|
||||||
|
|
||||||
|
.PHONY: lint-modules
|
||||||
|
lint-modules: | $(CROSSLINK)
|
||||||
|
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
|
||||||
|
echo "cross-linking all go modules"
|
||||||
|
$(CROSSLINK)
|
||||||
|
|
||||||
|
.PHONY: license-check
|
||||||
|
license-check:
|
||||||
|
@licRes=$$(for f in $$(find . -type f \( -iname '*.go' -o -iname '*.sh' \) ! -path '**/third_party/*') ; 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 "directory: \+$$f" .github/dependabot.yml \
|
||||||
|
|| echo "$$f"; \
|
||||||
|
done; \
|
||||||
|
); \
|
||||||
|
if [ -n "$$result" ]; then \
|
||||||
|
echo "missing go.mod dependabot check:"; echo "$$result"; \
|
||||||
|
echo "new modules need to be added to the .github/dependabot.yml file"; \
|
||||||
|
exit 1; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
.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: prerelease
|
||||||
|
prerelease: | $(MULTIMOD)
|
||||||
|
@[ "${MODSET}" ] || ( echo ">> env var MODSET is not set"; exit 1 )
|
||||||
|
$(MULTIMOD) verify && $(MULTIMOD) prerelease -m ${MODSET}
|
||||||
|
|
||||||
|
COMMIT ?= "HEAD"
|
||||||
|
.PHONY: add-tags
|
||||||
|
add-tags: | $(MULTIMOD)
|
||||||
|
@[ "${MODSET}" ] || ( echo ">> env var MODSET is not set"; exit 1 )
|
||||||
|
$(MULTIMOD) verify && $(MULTIMOD) tag -m ${MODSET} -c ${COMMIT}
|
||||||
102
vendor/go.opentelemetry.io/otel/README.md
generated
vendored
Normal file
102
vendor/go.opentelemetry.io/otel/README.md
generated
vendored
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
# OpenTelemetry-Go
|
||||||
|
|
||||||
|
[](https://github.com/open-telemetry/opentelemetry-go/actions?query=workflow%3Aci+branch%3Amain)
|
||||||
|
[](https://app.codecov.io/gh/open-telemetry/opentelemetry-go?branch=main)
|
||||||
|
[](https://pkg.go.dev/go.opentelemetry.io/otel)
|
||||||
|
[](https://goreportcard.com/report/go.opentelemetry.io/otel)
|
||||||
|
[](https://cloud-native.slack.com/archives/C01NPAXACKT)
|
||||||
|
|
||||||
|
OpenTelemetry-Go is the [Go](https://golang.org/) implementation of [OpenTelemetry](https://opentelemetry.io/).
|
||||||
|
It provides a set of APIs to directly measure performance and behavior of your software and send this data to observability platforms.
|
||||||
|
|
||||||
|
## Project Status
|
||||||
|
|
||||||
|
| Signal | Status | Project |
|
||||||
|
| ------- | ---------- | ------- |
|
||||||
|
| Traces | Stable | N/A |
|
||||||
|
| Metrics | Alpha | N/A |
|
||||||
|
| Logs | Frozen [1] | N/A |
|
||||||
|
|
||||||
|
- [1]: The Logs signal development is halted for this project while we develop both Traces and Metrics.
|
||||||
|
No Logs Pull Requests are currently being accepted.
|
||||||
|
|
||||||
|
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).
|
||||||
|
|
||||||
|
### Compatibility
|
||||||
|
|
||||||
|
OpenTelemetry-Go attempts to track the current supported versions of the
|
||||||
|
[Go language](https://golang.org/doc/devel/release#policy). The release
|
||||||
|
schedule after a new minor version of go is as follows:
|
||||||
|
|
||||||
|
- The first release or one month, which ever is sooner, will add build steps for the new go version.
|
||||||
|
- The first release after three months will remove support for the oldest go version.
|
||||||
|
|
||||||
|
This project is tested on the following systems.
|
||||||
|
|
||||||
|
| OS | Go Version | Architecture |
|
||||||
|
| ------- | ---------- | ------------ |
|
||||||
|
| Ubuntu | 1.17 | amd64 |
|
||||||
|
| Ubuntu | 1.16 | amd64 |
|
||||||
|
| Ubuntu | 1.17 | 386 |
|
||||||
|
| Ubuntu | 1.16 | 386 |
|
||||||
|
| MacOS | 1.17 | amd64 |
|
||||||
|
| MacOS | 1.16 | amd64 |
|
||||||
|
| Windows | 1.17 | amd64 |
|
||||||
|
| Windows | 1.16 | amd64 |
|
||||||
|
| Windows | 1.17 | 386 |
|
||||||
|
| Windows | 1.16 | 386 |
|
||||||
|
|
||||||
|
While this project should work for other systems, no compatibility guarantees
|
||||||
|
are made for those systems currently.
|
||||||
|
|
||||||
|
## 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/main/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.
|
||||||
|
|
||||||
|
All officially supported exporters for the OpenTelemetry project are contained in the [exporters directory](./exporters).
|
||||||
|
|
||||||
|
| Exporter | Metrics | Traces |
|
||||||
|
| :-----------------------------------: | :-----: | :----: |
|
||||||
|
| [Jaeger](./exporters/jaeger/) | | ✓ |
|
||||||
|
| [OTLP](./exporters/otlp/) | ✓ | ✓ |
|
||||||
|
| [Prometheus](./exporters/prometheus/) | ✓ | |
|
||||||
|
| [stdout](./exporters/stdout/) | ✓ | ✓ |
|
||||||
|
| [Zipkin](./exporters/zipkin/) | | ✓ |
|
||||||
|
|
||||||
|
Additionally, OpenTelemetry community supported exporters can be found in the [contrib repository](https://github.com/open-telemetry/opentelemetry-go-contrib/tree/main/exporters).
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
See the [contributing documentation](CONTRIBUTING.md).
|
||||||
133
vendor/go.opentelemetry.io/otel/RELEASING.md
generated
vendored
Normal file
133
vendor/go.opentelemetry.io/otel/RELEASING.md
generated
vendored
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
# Release Process
|
||||||
|
|
||||||
|
## Semantic Convention Generation
|
||||||
|
|
||||||
|
If a new version of the OpenTelemetry Specification has been released it will be necessary to generate a new
|
||||||
|
semantic convention package from the YAML definitions in the specification repository. There is a `semconvgen` utility
|
||||||
|
installed by `make tools` that can be used to generate the a package with the name matching the specification
|
||||||
|
version number under the `semconv` package. This will ideally be done soon after the specification release is
|
||||||
|
tagged. Make sure that the specification repo contains a checkout of the the latest tagged release so that the
|
||||||
|
generated files match the released semantic conventions.
|
||||||
|
|
||||||
|
There are currently two categories of semantic conventions that must be generated, `resource` and `trace`.
|
||||||
|
|
||||||
|
```
|
||||||
|
.tools/semconvgen -i /path/to/specification/repo/semantic_conventions/resource -t semconv/template.j2
|
||||||
|
.tools/semconvgen -i /path/to/specification/repo/semantic_conventions/trace -t semconv/template.j2
|
||||||
|
```
|
||||||
|
|
||||||
|
Using default values for all options other than `input` will result in using the `template.j2` template to
|
||||||
|
generate `resource.go` and `trace.go` in `/path/to/otelgo/repo/semconv/<version>`.
|
||||||
|
|
||||||
|
There are several ancillary files that are not generated and should be copied into the new package from the
|
||||||
|
prior package, with updates made as appropriate to canonical import path statements and constant values.
|
||||||
|
These files include:
|
||||||
|
|
||||||
|
* doc.go
|
||||||
|
* exception.go
|
||||||
|
* http(_test)?.go
|
||||||
|
* schema.go
|
||||||
|
|
||||||
|
Uses of the previous schema version in this repository should be updated to use the newly generated version.
|
||||||
|
No tooling for this exists at present, so use find/replace in your editor of choice or craft a `grep | sed`
|
||||||
|
pipeline if you like living on the edge.
|
||||||
|
|
||||||
|
## Pre-Release
|
||||||
|
|
||||||
|
First, decide which module sets will be released and update their versions
|
||||||
|
in `versions.yaml`. Commit this change to a new branch.
|
||||||
|
|
||||||
|
Update go.mod for submodules to depend on the new release which will happen in the next step.
|
||||||
|
|
||||||
|
1. Run the `prerelease` make target. It creates a branch
|
||||||
|
`prerelease_<module set>_<new tag>` that will contain all release changes.
|
||||||
|
|
||||||
|
```
|
||||||
|
make prerelease MODSET=<module set>
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Verify the changes.
|
||||||
|
|
||||||
|
```
|
||||||
|
git diff ...prerelease_<module set>_<new tag>
|
||||||
|
```
|
||||||
|
|
||||||
|
This should have changed the version for all modules to be `<new tag>`.
|
||||||
|
If these changes look correct, merge them into your pre-release branch:
|
||||||
|
|
||||||
|
```go
|
||||||
|
git merge prerelease_<module set>_<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. As long as you do not
|
||||||
|
change `versions.yaml` between pre-release and this step, things should be fine.
|
||||||
|
|
||||||
|
***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. For each module set that will be released, run the `add-tags` make target
|
||||||
|
using the `<commit-hash>` of the commit on the main branch for the merged Pull Request.
|
||||||
|
|
||||||
|
```
|
||||||
|
make add-tags MODSET=<module set> COMMIT=<commit hash>
|
||||||
|
```
|
||||||
|
|
||||||
|
It should only be necessary to provide an explicit `COMMIT` value if the
|
||||||
|
current `HEAD` of your working directory is not the correct commit.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
## Post-Release
|
||||||
|
|
||||||
|
### Contrib Repository
|
||||||
|
|
||||||
|
Once verified be sure to [make a release for the `contrib` repository](https://github.com/open-telemetry/opentelemetry-go-contrib/blob/main/RELEASING.md) that uses this release.
|
||||||
|
|
||||||
|
### Website Documentation
|
||||||
|
|
||||||
|
Update [the documentation](./website_docs) for [the OpenTelemetry website](https://opentelemetry.io/docs/go/).
|
||||||
|
Importantly, bump any package versions referenced to be the latest one you just released and ensure all code examples still compile and are accurate.
|
||||||
224
vendor/go.opentelemetry.io/otel/VERSIONING.md
generated
vendored
Normal file
224
vendor/go.opentelemetry.io/otel/VERSIONING.md
generated
vendored
Normal file
@@ -0,0 +1,224 @@
|
|||||||
|
# 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) with the following exceptions.
|
||||||
|
* New methods may be added to exported API interfaces. All exported
|
||||||
|
interfaces that fall within this exception will include the following
|
||||||
|
paragraph in their public documentation.
|
||||||
|
|
||||||
|
> Warning: methods may be added to this interface in minor releases.
|
||||||
|
|
||||||
|
* 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-RC1`
|
||||||
|
* `otel/trace`: `v1.0.0-RC1`
|
||||||
|
* `otel/baggage`: `v1.0.0-RC1`
|
||||||
|
* `otel/sdk/trace`: `v1.0.0-RC1`
|
||||||
|
|
||||||
|
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-RC2`
|
||||||
|
* `otel/trace`: `v1.0.0-RC2`
|
||||||
|
* `otel/baggage`: `v1.0.0-RC2`
|
||||||
|
* `otel/sdk/trace`: `v1.0.0-RC2`
|
||||||
|
|
||||||
|
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-RC1`
|
||||||
|
* `otel/trace`: `v1.1.0-RC1`
|
||||||
|
* `otel/metric`: `v1.1.0-RC1`
|
||||||
|
* `otel/baggage`: `v1.1.0-RC1`
|
||||||
|
* `otel/sdk/trace`: `v1.1.0-RC1`
|
||||||
|
* `otel/sdk/metric`: `v1.1.0-RC1`
|
||||||
|
|
||||||
|
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`
|
||||||
16
vendor/go.opentelemetry.io/otel/attribute/doc.go
generated
vendored
Normal file
16
vendor/go.opentelemetry.io/otel/attribute/doc.go
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
// 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 attribute provides key and value attributes.
|
||||||
|
package attribute // import "go.opentelemetry.io/otel/attribute"
|
||||||
150
vendor/go.opentelemetry.io/otel/attribute/encoder.go
generated
vendored
Normal file
150
vendor/go.opentelemetry.io/otel/attribute/encoder.go
generated
vendored
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
// 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 attribute // import "go.opentelemetry.io/otel/attribute"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// Encoder is a mechanism for serializing a label set into a
|
||||||
|
// specific string representation that supports caching, to
|
||||||
|
// avoid repeated serialization. An example could be an
|
||||||
|
// exporter encoding the label set into a wire representation.
|
||||||
|
Encoder interface {
|
||||||
|
// Encode returns the serialized encoding of the label
|
||||||
|
// set using its Iterator. This result may be cached
|
||||||
|
// by a attribute.Set.
|
||||||
|
Encode(iterator Iterator) string
|
||||||
|
|
||||||
|
// ID returns a value that is unique for each class of
|
||||||
|
// label encoder. Label encoders allocate these using
|
||||||
|
// `NewEncoderID`.
|
||||||
|
ID() EncoderID
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncoderID is used to identify distinct Encoder
|
||||||
|
// implementations, for caching encoded results.
|
||||||
|
EncoderID struct {
|
||||||
|
value uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// defaultLabelEncoder uses a sync.Pool of buffers to reduce
|
||||||
|
// the number of allocations used in encoding labels. This
|
||||||
|
// implementation encodes a comma-separated list of key=value,
|
||||||
|
// with '/'-escaping of '=', ',', and '\'.
|
||||||
|
defaultLabelEncoder struct {
|
||||||
|
// pool is a pool of labelset builders. The buffers in this
|
||||||
|
// pool grow to a size that most label encodings will not
|
||||||
|
// allocate new memory.
|
||||||
|
pool sync.Pool // *bytes.Buffer
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// escapeChar is used to ensure uniqueness of the label encoding where
|
||||||
|
// keys or values contain either '=' or ','. Since there is no parser
|
||||||
|
// needed for this encoding and its only requirement is to be unique,
|
||||||
|
// this choice is arbitrary. Users will see these in some exporters
|
||||||
|
// (e.g., stdout), so the backslash ('\') is used as a conventional choice.
|
||||||
|
const escapeChar = '\\'
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ Encoder = &defaultLabelEncoder{}
|
||||||
|
|
||||||
|
// encoderIDCounter is for generating IDs for other label
|
||||||
|
// encoders.
|
||||||
|
encoderIDCounter uint64
|
||||||
|
|
||||||
|
defaultEncoderOnce sync.Once
|
||||||
|
defaultEncoderID = NewEncoderID()
|
||||||
|
defaultEncoderInstance *defaultLabelEncoder
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewEncoderID returns a unique label encoder ID. It should be
|
||||||
|
// called once per each type of label encoder. Preferably in init() or
|
||||||
|
// in var definition.
|
||||||
|
func NewEncoderID() EncoderID {
|
||||||
|
return EncoderID{value: atomic.AddUint64(&encoderIDCounter, 1)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultEncoder returns a label encoder that encodes labels
|
||||||
|
// in such a way that each escaped label's key is followed by an equal
|
||||||
|
// sign and then by an escaped label's value. All key-value pairs are
|
||||||
|
// separated by a comma.
|
||||||
|
//
|
||||||
|
// Escaping is done by prepending a backslash before either a
|
||||||
|
// backslash, equal sign or a comma.
|
||||||
|
func DefaultEncoder() Encoder {
|
||||||
|
defaultEncoderOnce.Do(func() {
|
||||||
|
defaultEncoderInstance = &defaultLabelEncoder{
|
||||||
|
pool: sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
return &bytes.Buffer{}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return defaultEncoderInstance
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode is a part of an implementation of the LabelEncoder
|
||||||
|
// interface.
|
||||||
|
func (d *defaultLabelEncoder) Encode(iter Iterator) string {
|
||||||
|
buf := d.pool.Get().(*bytes.Buffer)
|
||||||
|
defer d.pool.Put(buf)
|
||||||
|
buf.Reset()
|
||||||
|
|
||||||
|
for iter.Next() {
|
||||||
|
i, keyValue := iter.IndexedLabel()
|
||||||
|
if i > 0 {
|
||||||
|
_, _ = buf.WriteRune(',')
|
||||||
|
}
|
||||||
|
copyAndEscape(buf, string(keyValue.Key))
|
||||||
|
|
||||||
|
_, _ = buf.WriteRune('=')
|
||||||
|
|
||||||
|
if keyValue.Value.Type() == STRING {
|
||||||
|
copyAndEscape(buf, keyValue.Value.AsString())
|
||||||
|
} else {
|
||||||
|
_, _ = buf.WriteString(keyValue.Value.Emit())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID is a part of an implementation of the LabelEncoder interface.
|
||||||
|
func (*defaultLabelEncoder) ID() EncoderID {
|
||||||
|
return defaultEncoderID
|
||||||
|
}
|
||||||
|
|
||||||
|
// copyAndEscape escapes `=`, `,` and its own escape character (`\`),
|
||||||
|
// making the default encoding unique.
|
||||||
|
func copyAndEscape(buf *bytes.Buffer, val string) {
|
||||||
|
for _, ch := range val {
|
||||||
|
switch ch {
|
||||||
|
case '=', ',', escapeChar:
|
||||||
|
buf.WriteRune(escapeChar)
|
||||||
|
}
|
||||||
|
buf.WriteRune(ch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid returns true if this encoder ID was allocated by
|
||||||
|
// `NewEncoderID`. Invalid encoder IDs will not be cached.
|
||||||
|
func (id EncoderID) Valid() bool {
|
||||||
|
return id.value != 0
|
||||||
|
}
|
||||||
143
vendor/go.opentelemetry.io/otel/attribute/iterator.go
generated
vendored
Normal file
143
vendor/go.opentelemetry.io/otel/attribute/iterator.go
generated
vendored
Normal file
@@ -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 attribute // import "go.opentelemetry.io/otel/attribute"
|
||||||
|
|
||||||
|
// Iterator allows iterating over the set of labels in order,
|
||||||
|
// sorted by key.
|
||||||
|
type Iterator struct {
|
||||||
|
storage *Set
|
||||||
|
idx int
|
||||||
|
}
|
||||||
|
|
||||||
|
// MergeIterator supports iterating over two sets of labels while
|
||||||
|
// eliminating duplicate values from the combined set. The first
|
||||||
|
// iterator value takes precedence.
|
||||||
|
type MergeIterator struct {
|
||||||
|
one oneIterator
|
||||||
|
two oneIterator
|
||||||
|
current KeyValue
|
||||||
|
}
|
||||||
|
|
||||||
|
type oneIterator struct {
|
||||||
|
iter Iterator
|
||||||
|
done bool
|
||||||
|
label KeyValue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next moves the iterator to the next position. Returns false if there
|
||||||
|
// are no more labels.
|
||||||
|
func (i *Iterator) Next() bool {
|
||||||
|
i.idx++
|
||||||
|
return i.idx < i.Len()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Label returns current KeyValue. Must be called only after Next returns
|
||||||
|
// true.
|
||||||
|
func (i *Iterator) Label() KeyValue {
|
||||||
|
kv, _ := i.storage.Get(i.idx)
|
||||||
|
return kv
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attribute is a synonym for Label().
|
||||||
|
func (i *Iterator) Attribute() KeyValue {
|
||||||
|
return i.Label()
|
||||||
|
}
|
||||||
|
|
||||||
|
// IndexedLabel returns current index and attribute. Must be called only
|
||||||
|
// after Next returns true.
|
||||||
|
func (i *Iterator) IndexedLabel() (int, KeyValue) {
|
||||||
|
return i.idx, i.Label()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns a number of labels in the iterator's `*Set`.
|
||||||
|
func (i *Iterator) Len() int {
|
||||||
|
return i.storage.Len()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToSlice is a convenience function that creates a slice of labels
|
||||||
|
// from the passed iterator. The iterator is set up to start from the
|
||||||
|
// beginning before creating the slice.
|
||||||
|
func (i *Iterator) ToSlice() []KeyValue {
|
||||||
|
l := i.Len()
|
||||||
|
if l == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
i.idx = -1
|
||||||
|
slice := make([]KeyValue, 0, l)
|
||||||
|
for i.Next() {
|
||||||
|
slice = append(slice, i.Label())
|
||||||
|
}
|
||||||
|
return slice
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMergeIterator returns a MergeIterator for merging two label sets
|
||||||
|
// Duplicates are resolved by taking the value from the first set.
|
||||||
|
func NewMergeIterator(s1, s2 *Set) MergeIterator {
|
||||||
|
mi := MergeIterator{
|
||||||
|
one: makeOne(s1.Iter()),
|
||||||
|
two: makeOne(s2.Iter()),
|
||||||
|
}
|
||||||
|
return mi
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeOne(iter Iterator) oneIterator {
|
||||||
|
oi := oneIterator{
|
||||||
|
iter: iter,
|
||||||
|
}
|
||||||
|
oi.advance()
|
||||||
|
return oi
|
||||||
|
}
|
||||||
|
|
||||||
|
func (oi *oneIterator) advance() {
|
||||||
|
if oi.done = !oi.iter.Next(); !oi.done {
|
||||||
|
oi.label = oi.iter.Label()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next returns true if there is another label available.
|
||||||
|
func (m *MergeIterator) Next() bool {
|
||||||
|
if m.one.done && m.two.done {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if m.one.done {
|
||||||
|
m.current = m.two.label
|
||||||
|
m.two.advance()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if m.two.done {
|
||||||
|
m.current = m.one.label
|
||||||
|
m.one.advance()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if m.one.label.Key == m.two.label.Key {
|
||||||
|
m.current = m.one.label // first iterator label value wins
|
||||||
|
m.one.advance()
|
||||||
|
m.two.advance()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if m.one.label.Key < m.two.label.Key {
|
||||||
|
m.current = m.one.label
|
||||||
|
m.one.advance()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
m.current = m.two.label
|
||||||
|
m.two.advance()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Label returns the current value after Next() returns true.
|
||||||
|
func (m *MergeIterator) Label() KeyValue {
|
||||||
|
return m.current
|
||||||
|
}
|
||||||
134
vendor/go.opentelemetry.io/otel/attribute/key.go
generated
vendored
Normal file
134
vendor/go.opentelemetry.io/otel/attribute/key.go
generated
vendored
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
// 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 attribute // import "go.opentelemetry.io/otel/attribute"
|
||||||
|
|
||||||
|
// Key represents the key part in key-value pairs. It's a string. The
|
||||||
|
// allowed character set in the key depends on the use of the key.
|
||||||
|
type Key string
|
||||||
|
|
||||||
|
// Bool creates a KeyValue instance with a BOOL Value.
|
||||||
|
//
|
||||||
|
// If creating both a key and value at the same time, use the provided
|
||||||
|
// convenience function instead -- Bool(name, value).
|
||||||
|
func (k Key) Bool(v bool) KeyValue {
|
||||||
|
return KeyValue{
|
||||||
|
Key: k,
|
||||||
|
Value: BoolValue(v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BoolSlice creates a KeyValue instance with a BOOLSLICE Value.
|
||||||
|
//
|
||||||
|
// If creating both a key and value at the same time, use the provided
|
||||||
|
// convenience function instead -- BoolSlice(name, value).
|
||||||
|
func (k Key) BoolSlice(v []bool) KeyValue {
|
||||||
|
return KeyValue{
|
||||||
|
Key: k,
|
||||||
|
Value: BoolSliceValue(v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int creates a KeyValue instance with an INT64 Value.
|
||||||
|
//
|
||||||
|
// If creating both a key and value at the same time, use the provided
|
||||||
|
// convenience function instead -- Int(name, value).
|
||||||
|
func (k Key) Int(v int) KeyValue {
|
||||||
|
return KeyValue{
|
||||||
|
Key: k,
|
||||||
|
Value: IntValue(v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntSlice creates a KeyValue instance with an INT64SLICE Value.
|
||||||
|
//
|
||||||
|
// If creating both a key and value at the same time, use the provided
|
||||||
|
// convenience function instead -- IntSlice(name, value).
|
||||||
|
func (k Key) IntSlice(v []int) KeyValue {
|
||||||
|
return KeyValue{
|
||||||
|
Key: k,
|
||||||
|
Value: IntSliceValue(v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64 creates a KeyValue instance with an INT64 Value.
|
||||||
|
//
|
||||||
|
// If creating both a key and value at the same time, use the provided
|
||||||
|
// convenience function instead -- Int64(name, value).
|
||||||
|
func (k Key) Int64(v int64) KeyValue {
|
||||||
|
return KeyValue{
|
||||||
|
Key: k,
|
||||||
|
Value: Int64Value(v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64Slice creates a KeyValue instance with an INT64SLICE Value.
|
||||||
|
//
|
||||||
|
// If creating both a key and value at the same time, use the provided
|
||||||
|
// convenience function instead -- Int64Slice(name, value).
|
||||||
|
func (k Key) Int64Slice(v []int64) KeyValue {
|
||||||
|
return KeyValue{
|
||||||
|
Key: k,
|
||||||
|
Value: Int64SliceValue(v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64 creates a KeyValue instance with a FLOAT64 Value.
|
||||||
|
//
|
||||||
|
// If creating both a key and value at the same time, use the provided
|
||||||
|
// convenience function instead -- Float64(name, value).
|
||||||
|
func (k Key) Float64(v float64) KeyValue {
|
||||||
|
return KeyValue{
|
||||||
|
Key: k,
|
||||||
|
Value: Float64Value(v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64Slice creates a KeyValue instance with a FLOAT64SLICE Value.
|
||||||
|
//
|
||||||
|
// If creating both a key and value at the same time, use the provided
|
||||||
|
// convenience function instead -- Float64(name, value).
|
||||||
|
func (k Key) Float64Slice(v []float64) KeyValue {
|
||||||
|
return KeyValue{
|
||||||
|
Key: k,
|
||||||
|
Value: Float64SliceValue(v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// String creates a KeyValue instance with a STRING Value.
|
||||||
|
//
|
||||||
|
// If creating both a key and value at the same time, use the provided
|
||||||
|
// convenience function instead -- String(name, value).
|
||||||
|
func (k Key) String(v string) KeyValue {
|
||||||
|
return KeyValue{
|
||||||
|
Key: k,
|
||||||
|
Value: StringValue(v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringSlice creates a KeyValue instance with a STRINGSLICE Value.
|
||||||
|
//
|
||||||
|
// If creating both a key and value at the same time, use the provided
|
||||||
|
// convenience function instead -- StringSlice(name, value).
|
||||||
|
func (k Key) StringSlice(v []string) KeyValue {
|
||||||
|
return KeyValue{
|
||||||
|
Key: k,
|
||||||
|
Value: StringSliceValue(v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defined returns true for non-empty keys.
|
||||||
|
func (k Key) Defined() bool {
|
||||||
|
return len(k) != 0
|
||||||
|
}
|
||||||
86
vendor/go.opentelemetry.io/otel/attribute/kv.go
generated
vendored
Normal file
86
vendor/go.opentelemetry.io/otel/attribute/kv.go
generated
vendored
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
// 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 attribute // import "go.opentelemetry.io/otel/attribute"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// KeyValue holds a key and value pair.
|
||||||
|
type KeyValue struct {
|
||||||
|
Key Key
|
||||||
|
Value Value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid returns if kv is a valid OpenTelemetry attribute.
|
||||||
|
func (kv KeyValue) Valid() bool {
|
||||||
|
return kv.Key != "" && kv.Value.Type() != INVALID
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bool creates a KeyValue with a BOOL Value type.
|
||||||
|
func Bool(k string, v bool) KeyValue {
|
||||||
|
return Key(k).Bool(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BoolSlice creates a KeyValue with a BOOLSLICE Value type.
|
||||||
|
func BoolSlice(k string, v []bool) KeyValue {
|
||||||
|
return Key(k).BoolSlice(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int creates a KeyValue with an INT64 Value type.
|
||||||
|
func Int(k string, v int) KeyValue {
|
||||||
|
return Key(k).Int(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntSlice creates a KeyValue with an INT64SLICE Value type.
|
||||||
|
func IntSlice(k string, v []int) KeyValue {
|
||||||
|
return Key(k).IntSlice(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64 creates a KeyValue with an INT64 Value type.
|
||||||
|
func Int64(k string, v int64) KeyValue {
|
||||||
|
return Key(k).Int64(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64Slice creates a KeyValue with an INT64SLICE Value type.
|
||||||
|
func Int64Slice(k string, v []int64) KeyValue {
|
||||||
|
return Key(k).Int64Slice(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64 creates a KeyValue with a FLOAT64 Value type.
|
||||||
|
func Float64(k string, v float64) KeyValue {
|
||||||
|
return Key(k).Float64(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64Slice creates a KeyValue with a FLOAT64SLICE Value type.
|
||||||
|
func Float64Slice(k string, v []float64) KeyValue {
|
||||||
|
return Key(k).Float64Slice(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// String creates a KeyValue with a STRING Value type.
|
||||||
|
func String(k, v string) KeyValue {
|
||||||
|
return Key(k).String(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringSlice creates a KeyValue with a STRINGSLICE Value type.
|
||||||
|
func StringSlice(k string, v []string) KeyValue {
|
||||||
|
return Key(k).StringSlice(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stringer creates a new key-value pair with a passed name and a string
|
||||||
|
// value generated by the passed Stringer interface.
|
||||||
|
func Stringer(k string, v fmt.Stringer) KeyValue {
|
||||||
|
return Key(k).String(v.String())
|
||||||
|
}
|
||||||
426
vendor/go.opentelemetry.io/otel/attribute/set.go
generated
vendored
Normal file
426
vendor/go.opentelemetry.io/otel/attribute/set.go
generated
vendored
Normal file
@@ -0,0 +1,426 @@
|
|||||||
|
// 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 attribute // import "go.opentelemetry.io/otel/attribute"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// Set is the representation for a distinct label set. It
|
||||||
|
// manages an immutable set of labels, with an internal cache
|
||||||
|
// for storing label encodings.
|
||||||
|
//
|
||||||
|
// This type supports the `Equivalent` method of comparison
|
||||||
|
// using values of type `Distinct`.
|
||||||
|
//
|
||||||
|
// This type is used to implement:
|
||||||
|
// 1. Metric labels
|
||||||
|
// 2. Resource sets
|
||||||
|
// 3. Correlation map (TODO)
|
||||||
|
Set struct {
|
||||||
|
equivalent Distinct
|
||||||
|
}
|
||||||
|
|
||||||
|
// Distinct wraps a variable-size array of `KeyValue`,
|
||||||
|
// constructed with keys in sorted order. This can be used as
|
||||||
|
// a map key or for equality checking between Sets.
|
||||||
|
Distinct struct {
|
||||||
|
iface interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter supports removing certain labels from label sets.
|
||||||
|
// When the filter returns true, the label will be kept in
|
||||||
|
// the filtered label set. When the filter returns false, the
|
||||||
|
// label is excluded from the filtered label set, and the
|
||||||
|
// label instead appears in the `removed` list of excluded labels.
|
||||||
|
Filter func(KeyValue) bool
|
||||||
|
|
||||||
|
// Sortable implements `sort.Interface`, used for sorting
|
||||||
|
// `KeyValue`. This is an exported type to support a
|
||||||
|
// memory optimization. A pointer to one of these is needed
|
||||||
|
// for the call to `sort.Stable()`, which the caller may
|
||||||
|
// provide in order to avoid an allocation. See
|
||||||
|
// `NewSetWithSortable()`.
|
||||||
|
Sortable []KeyValue
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// keyValueType is used in `computeDistinctReflect`.
|
||||||
|
keyValueType = reflect.TypeOf(KeyValue{})
|
||||||
|
|
||||||
|
// emptySet is returned for empty label sets.
|
||||||
|
emptySet = &Set{
|
||||||
|
equivalent: Distinct{
|
||||||
|
iface: [0]KeyValue{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// EmptySet returns a reference to a Set with no elements.
|
||||||
|
//
|
||||||
|
// This is a convenience provided for optimized calling utility.
|
||||||
|
func EmptySet() *Set {
|
||||||
|
return emptySet
|
||||||
|
}
|
||||||
|
|
||||||
|
// reflect abbreviates `reflect.ValueOf`.
|
||||||
|
func (d Distinct) reflect() reflect.Value {
|
||||||
|
return reflect.ValueOf(d.iface)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid returns true if this value refers to a valid `*Set`.
|
||||||
|
func (d Distinct) Valid() bool {
|
||||||
|
return d.iface != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the number of labels in this set.
|
||||||
|
func (l *Set) Len() int {
|
||||||
|
if l == nil || !l.equivalent.Valid() {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return l.equivalent.reflect().Len()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns the KeyValue at ordered position `idx` in this set.
|
||||||
|
func (l *Set) Get(idx int) (KeyValue, bool) {
|
||||||
|
if l == nil {
|
||||||
|
return KeyValue{}, false
|
||||||
|
}
|
||||||
|
value := l.equivalent.reflect()
|
||||||
|
|
||||||
|
if idx >= 0 && idx < value.Len() {
|
||||||
|
// Note: The Go compiler successfully avoids an allocation for
|
||||||
|
// the interface{} conversion here:
|
||||||
|
return value.Index(idx).Interface().(KeyValue), true
|
||||||
|
}
|
||||||
|
|
||||||
|
return KeyValue{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value returns the value of a specified key in this set.
|
||||||
|
func (l *Set) Value(k Key) (Value, bool) {
|
||||||
|
if l == nil {
|
||||||
|
return Value{}, false
|
||||||
|
}
|
||||||
|
rValue := l.equivalent.reflect()
|
||||||
|
vlen := rValue.Len()
|
||||||
|
|
||||||
|
idx := sort.Search(vlen, func(idx int) bool {
|
||||||
|
return rValue.Index(idx).Interface().(KeyValue).Key >= k
|
||||||
|
})
|
||||||
|
if idx >= vlen {
|
||||||
|
return Value{}, false
|
||||||
|
}
|
||||||
|
keyValue := rValue.Index(idx).Interface().(KeyValue)
|
||||||
|
if k == keyValue.Key {
|
||||||
|
return keyValue.Value, true
|
||||||
|
}
|
||||||
|
return Value{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasValue tests whether a key is defined in this set.
|
||||||
|
func (l *Set) HasValue(k Key) bool {
|
||||||
|
if l == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
_, ok := l.Value(k)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iter returns an iterator for visiting the labels in this set.
|
||||||
|
func (l *Set) Iter() Iterator {
|
||||||
|
return Iterator{
|
||||||
|
storage: l,
|
||||||
|
idx: -1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToSlice returns the set of labels belonging to this set, sorted,
|
||||||
|
// where keys appear no more than once.
|
||||||
|
func (l *Set) ToSlice() []KeyValue {
|
||||||
|
iter := l.Iter()
|
||||||
|
return iter.ToSlice()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equivalent returns a value that may be used as a map key. The
|
||||||
|
// Distinct type guarantees that the result will equal the equivalent
|
||||||
|
// Distinct value of any label set with the same elements as this,
|
||||||
|
// where sets are made unique by choosing the last value in the input
|
||||||
|
// for any given key.
|
||||||
|
func (l *Set) Equivalent() Distinct {
|
||||||
|
if l == nil || !l.equivalent.Valid() {
|
||||||
|
return emptySet.equivalent
|
||||||
|
}
|
||||||
|
return l.equivalent
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equals returns true if the argument set is equivalent to this set.
|
||||||
|
func (l *Set) Equals(o *Set) bool {
|
||||||
|
return l.Equivalent() == o.Equivalent()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encoded returns the encoded form of this set, according to
|
||||||
|
// `encoder`.
|
||||||
|
func (l *Set) Encoded(encoder Encoder) string {
|
||||||
|
if l == nil || encoder == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return encoder.Encode(l.Iter())
|
||||||
|
}
|
||||||
|
|
||||||
|
func empty() Set {
|
||||||
|
return Set{
|
||||||
|
equivalent: emptySet.equivalent,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSet returns a new `Set`. See the documentation for
|
||||||
|
// `NewSetWithSortableFiltered` for more details.
|
||||||
|
//
|
||||||
|
// Except for empty sets, this method adds an additional allocation
|
||||||
|
// compared with calls that include a `*Sortable`.
|
||||||
|
func NewSet(kvs ...KeyValue) Set {
|
||||||
|
// Check for empty set.
|
||||||
|
if len(kvs) == 0 {
|
||||||
|
return empty()
|
||||||
|
}
|
||||||
|
s, _ := NewSetWithSortableFiltered(kvs, new(Sortable), nil)
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSetWithSortable returns a new `Set`. See the documentation for
|
||||||
|
// `NewSetWithSortableFiltered` for more details.
|
||||||
|
//
|
||||||
|
// This call includes a `*Sortable` option as a memory optimization.
|
||||||
|
func NewSetWithSortable(kvs []KeyValue, tmp *Sortable) Set {
|
||||||
|
// Check for empty set.
|
||||||
|
if len(kvs) == 0 {
|
||||||
|
return empty()
|
||||||
|
}
|
||||||
|
s, _ := NewSetWithSortableFiltered(kvs, tmp, nil)
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSetWithFiltered returns a new `Set`. See the documentation for
|
||||||
|
// `NewSetWithSortableFiltered` for more details.
|
||||||
|
//
|
||||||
|
// This call includes a `Filter` to include/exclude label keys from
|
||||||
|
// the return value. Excluded keys are returned as a slice of label
|
||||||
|
// values.
|
||||||
|
func NewSetWithFiltered(kvs []KeyValue, filter Filter) (Set, []KeyValue) {
|
||||||
|
// Check for empty set.
|
||||||
|
if len(kvs) == 0 {
|
||||||
|
return empty(), nil
|
||||||
|
}
|
||||||
|
return NewSetWithSortableFiltered(kvs, new(Sortable), filter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSetWithSortableFiltered returns a new `Set`.
|
||||||
|
//
|
||||||
|
// Duplicate keys are eliminated by taking the last value. This
|
||||||
|
// re-orders the input slice so that unique last-values are contiguous
|
||||||
|
// at the end of the slice.
|
||||||
|
//
|
||||||
|
// This ensures the following:
|
||||||
|
//
|
||||||
|
// - Last-value-wins semantics
|
||||||
|
// - Caller sees the reordering, but doesn't lose values
|
||||||
|
// - Repeated call preserve last-value wins.
|
||||||
|
//
|
||||||
|
// Note that methods are defined on `*Set`, although this returns `Set`.
|
||||||
|
// Callers can avoid memory allocations by:
|
||||||
|
//
|
||||||
|
// - allocating a `Sortable` for use as a temporary in this method
|
||||||
|
// - allocating a `Set` for storing the return value of this
|
||||||
|
// constructor.
|
||||||
|
//
|
||||||
|
// The result maintains a cache of encoded labels, by attribute.EncoderID.
|
||||||
|
// This value should not be copied after its first use.
|
||||||
|
//
|
||||||
|
// The second `[]KeyValue` return value is a list of labels that were
|
||||||
|
// excluded by the Filter (if non-nil).
|
||||||
|
func NewSetWithSortableFiltered(kvs []KeyValue, tmp *Sortable, filter Filter) (Set, []KeyValue) {
|
||||||
|
// Check for empty set.
|
||||||
|
if len(kvs) == 0 {
|
||||||
|
return empty(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
*tmp = kvs
|
||||||
|
|
||||||
|
// Stable sort so the following de-duplication can implement
|
||||||
|
// last-value-wins semantics.
|
||||||
|
sort.Stable(tmp)
|
||||||
|
|
||||||
|
*tmp = nil
|
||||||
|
|
||||||
|
position := len(kvs) - 1
|
||||||
|
offset := position - 1
|
||||||
|
|
||||||
|
// The requirements stated above require that the stable
|
||||||
|
// result be placed in the end of the input slice, while
|
||||||
|
// overwritten values are swapped to the beginning.
|
||||||
|
//
|
||||||
|
// De-duplicate with last-value-wins semantics. Preserve
|
||||||
|
// duplicate values at the beginning of the input slice.
|
||||||
|
for ; offset >= 0; offset-- {
|
||||||
|
if kvs[offset].Key == kvs[position].Key {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
position--
|
||||||
|
kvs[offset], kvs[position] = kvs[position], kvs[offset]
|
||||||
|
}
|
||||||
|
if filter != nil {
|
||||||
|
return filterSet(kvs[position:], filter)
|
||||||
|
}
|
||||||
|
return Set{
|
||||||
|
equivalent: computeDistinct(kvs[position:]),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// filterSet reorders `kvs` so that included keys are contiguous at
|
||||||
|
// the end of the slice, while excluded keys precede the included keys.
|
||||||
|
func filterSet(kvs []KeyValue, filter Filter) (Set, []KeyValue) {
|
||||||
|
var excluded []KeyValue
|
||||||
|
|
||||||
|
// Move labels that do not match the filter so
|
||||||
|
// they're adjacent before calling computeDistinct().
|
||||||
|
distinctPosition := len(kvs)
|
||||||
|
|
||||||
|
// Swap indistinct keys forward and distinct keys toward the
|
||||||
|
// end of the slice.
|
||||||
|
offset := len(kvs) - 1
|
||||||
|
for ; offset >= 0; offset-- {
|
||||||
|
if filter(kvs[offset]) {
|
||||||
|
distinctPosition--
|
||||||
|
kvs[offset], kvs[distinctPosition] = kvs[distinctPosition], kvs[offset]
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
excluded = kvs[:distinctPosition]
|
||||||
|
|
||||||
|
return Set{
|
||||||
|
equivalent: computeDistinct(kvs[distinctPosition:]),
|
||||||
|
}, excluded
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter returns a filtered copy of this `Set`. See the
|
||||||
|
// documentation for `NewSetWithSortableFiltered` for more details.
|
||||||
|
func (l *Set) Filter(re Filter) (Set, []KeyValue) {
|
||||||
|
if re == nil {
|
||||||
|
return Set{
|
||||||
|
equivalent: l.equivalent,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: This could be refactored to avoid the temporary slice
|
||||||
|
// allocation, if it proves to be expensive.
|
||||||
|
return filterSet(l.ToSlice(), re)
|
||||||
|
}
|
||||||
|
|
||||||
|
// computeDistinct returns a `Distinct` using either the fixed- or
|
||||||
|
// reflect-oriented code path, depending on the size of the input.
|
||||||
|
// The input slice is assumed to already be sorted and de-duplicated.
|
||||||
|
func computeDistinct(kvs []KeyValue) Distinct {
|
||||||
|
iface := computeDistinctFixed(kvs)
|
||||||
|
if iface == nil {
|
||||||
|
iface = computeDistinctReflect(kvs)
|
||||||
|
}
|
||||||
|
return Distinct{
|
||||||
|
iface: iface,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// computeDistinctFixed computes a `Distinct` for small slices. It
|
||||||
|
// returns nil if the input is too large for this code path.
|
||||||
|
func computeDistinctFixed(kvs []KeyValue) interface{} {
|
||||||
|
switch len(kvs) {
|
||||||
|
case 1:
|
||||||
|
ptr := new([1]KeyValue)
|
||||||
|
copy((*ptr)[:], kvs)
|
||||||
|
return *ptr
|
||||||
|
case 2:
|
||||||
|
ptr := new([2]KeyValue)
|
||||||
|
copy((*ptr)[:], kvs)
|
||||||
|
return *ptr
|
||||||
|
case 3:
|
||||||
|
ptr := new([3]KeyValue)
|
||||||
|
copy((*ptr)[:], kvs)
|
||||||
|
return *ptr
|
||||||
|
case 4:
|
||||||
|
ptr := new([4]KeyValue)
|
||||||
|
copy((*ptr)[:], kvs)
|
||||||
|
return *ptr
|
||||||
|
case 5:
|
||||||
|
ptr := new([5]KeyValue)
|
||||||
|
copy((*ptr)[:], kvs)
|
||||||
|
return *ptr
|
||||||
|
case 6:
|
||||||
|
ptr := new([6]KeyValue)
|
||||||
|
copy((*ptr)[:], kvs)
|
||||||
|
return *ptr
|
||||||
|
case 7:
|
||||||
|
ptr := new([7]KeyValue)
|
||||||
|
copy((*ptr)[:], kvs)
|
||||||
|
return *ptr
|
||||||
|
case 8:
|
||||||
|
ptr := new([8]KeyValue)
|
||||||
|
copy((*ptr)[:], kvs)
|
||||||
|
return *ptr
|
||||||
|
case 9:
|
||||||
|
ptr := new([9]KeyValue)
|
||||||
|
copy((*ptr)[:], kvs)
|
||||||
|
return *ptr
|
||||||
|
case 10:
|
||||||
|
ptr := new([10]KeyValue)
|
||||||
|
copy((*ptr)[:], kvs)
|
||||||
|
return *ptr
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// computeDistinctReflect computes a `Distinct` using reflection,
|
||||||
|
// works for any size input.
|
||||||
|
func computeDistinctReflect(kvs []KeyValue) interface{} {
|
||||||
|
at := reflect.New(reflect.ArrayOf(len(kvs), keyValueType)).Elem()
|
||||||
|
for i, keyValue := range kvs {
|
||||||
|
*(at.Index(i).Addr().Interface().(*KeyValue)) = keyValue
|
||||||
|
}
|
||||||
|
return at.Interface()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON returns the JSON encoding of the `*Set`.
|
||||||
|
func (l *Set) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(l.equivalent.iface)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len implements `sort.Interface`.
|
||||||
|
func (l *Sortable) Len() int {
|
||||||
|
return len(*l)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap implements `sort.Interface`.
|
||||||
|
func (l *Sortable) Swap(i, j int) {
|
||||||
|
(*l)[i], (*l)[j] = (*l)[j], (*l)[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Less implements `sort.Interface`.
|
||||||
|
func (l *Sortable) Less(i, j int) bool {
|
||||||
|
return (*l)[i].Key < (*l)[j].Key
|
||||||
|
}
|
||||||
31
vendor/go.opentelemetry.io/otel/attribute/type_string.go
generated
vendored
Normal file
31
vendor/go.opentelemetry.io/otel/attribute/type_string.go
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
// Code generated by "stringer -type=Type"; DO NOT EDIT.
|
||||||
|
|
||||||
|
package attribute
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||||
|
// Re-run the stringer command to generate them again.
|
||||||
|
var x [1]struct{}
|
||||||
|
_ = x[INVALID-0]
|
||||||
|
_ = x[BOOL-1]
|
||||||
|
_ = x[INT64-2]
|
||||||
|
_ = x[FLOAT64-3]
|
||||||
|
_ = x[STRING-4]
|
||||||
|
_ = x[BOOLSLICE-5]
|
||||||
|
_ = x[INT64SLICE-6]
|
||||||
|
_ = x[FLOAT64SLICE-7]
|
||||||
|
_ = x[STRINGSLICE-8]
|
||||||
|
}
|
||||||
|
|
||||||
|
const _Type_name = "INVALIDBOOLINT64FLOAT64STRINGBOOLSLICEINT64SLICEFLOAT64SLICESTRINGSLICE"
|
||||||
|
|
||||||
|
var _Type_index = [...]uint8{0, 7, 11, 16, 23, 29, 38, 48, 60, 71}
|
||||||
|
|
||||||
|
func (i Type) String() string {
|
||||||
|
if i < 0 || i >= Type(len(_Type_index)-1) {
|
||||||
|
return "Type(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
|
}
|
||||||
|
return _Type_name[_Type_index[i]:_Type_index[i+1]]
|
||||||
|
}
|
||||||
271
vendor/go.opentelemetry.io/otel/attribute/value.go
generated
vendored
Normal file
271
vendor/go.opentelemetry.io/otel/attribute/value.go
generated
vendored
Normal file
@@ -0,0 +1,271 @@
|
|||||||
|
// 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 attribute // import "go.opentelemetry.io/otel/attribute"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/internal"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:generate stringer -type=Type
|
||||||
|
|
||||||
|
// Type describes the type of the data Value holds.
|
||||||
|
type Type int
|
||||||
|
|
||||||
|
// Value represents the value part in key-value pairs.
|
||||||
|
type Value struct {
|
||||||
|
vtype Type
|
||||||
|
numeric uint64
|
||||||
|
stringly string
|
||||||
|
slice interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// INVALID is used for a Value with no value set.
|
||||||
|
INVALID Type = iota
|
||||||
|
// BOOL is a boolean Type Value.
|
||||||
|
BOOL
|
||||||
|
// INT64 is a 64-bit signed integral Type Value.
|
||||||
|
INT64
|
||||||
|
// FLOAT64 is a 64-bit floating point Type Value.
|
||||||
|
FLOAT64
|
||||||
|
// STRING is a string Type Value.
|
||||||
|
STRING
|
||||||
|
// BOOLSLICE is a slice of booleans Type Value.
|
||||||
|
BOOLSLICE
|
||||||
|
// INT64SLICE is a slice of 64-bit signed integral numbers Type Value.
|
||||||
|
INT64SLICE
|
||||||
|
// FLOAT64SLICE is a slice of 64-bit floating point numbers Type Value.
|
||||||
|
FLOAT64SLICE
|
||||||
|
// STRINGSLICE is a slice of strings Type Value.
|
||||||
|
STRINGSLICE
|
||||||
|
)
|
||||||
|
|
||||||
|
// BoolValue creates a BOOL Value.
|
||||||
|
func BoolValue(v bool) Value {
|
||||||
|
return Value{
|
||||||
|
vtype: BOOL,
|
||||||
|
numeric: internal.BoolToRaw(v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BoolSliceValue creates a BOOLSLICE Value.
|
||||||
|
func BoolSliceValue(v []bool) Value {
|
||||||
|
cp := make([]bool, len(v))
|
||||||
|
copy(cp, v)
|
||||||
|
return Value{
|
||||||
|
vtype: BOOLSLICE,
|
||||||
|
slice: &cp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntValue creates an INT64 Value.
|
||||||
|
func IntValue(v int) Value {
|
||||||
|
return Int64Value(int64(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntSliceValue creates an INTSLICE Value.
|
||||||
|
func IntSliceValue(v []int) Value {
|
||||||
|
cp := make([]int64, 0, len(v))
|
||||||
|
for _, i := range v {
|
||||||
|
cp = append(cp, int64(i))
|
||||||
|
}
|
||||||
|
return Value{
|
||||||
|
vtype: INT64SLICE,
|
||||||
|
slice: &cp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64Value creates an INT64 Value.
|
||||||
|
func Int64Value(v int64) Value {
|
||||||
|
return Value{
|
||||||
|
vtype: INT64,
|
||||||
|
numeric: internal.Int64ToRaw(v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64SliceValue creates an INT64SLICE Value.
|
||||||
|
func Int64SliceValue(v []int64) Value {
|
||||||
|
cp := make([]int64, len(v))
|
||||||
|
copy(cp, v)
|
||||||
|
return Value{
|
||||||
|
vtype: INT64SLICE,
|
||||||
|
slice: &cp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64Value creates a FLOAT64 Value.
|
||||||
|
func Float64Value(v float64) Value {
|
||||||
|
return Value{
|
||||||
|
vtype: FLOAT64,
|
||||||
|
numeric: internal.Float64ToRaw(v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64SliceValue creates a FLOAT64SLICE Value.
|
||||||
|
func Float64SliceValue(v []float64) Value {
|
||||||
|
cp := make([]float64, len(v))
|
||||||
|
copy(cp, v)
|
||||||
|
return Value{
|
||||||
|
vtype: FLOAT64SLICE,
|
||||||
|
slice: &cp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringValue creates a STRING Value.
|
||||||
|
func StringValue(v string) Value {
|
||||||
|
return Value{
|
||||||
|
vtype: STRING,
|
||||||
|
stringly: v,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringSliceValue creates a STRINGSLICE Value.
|
||||||
|
func StringSliceValue(v []string) Value {
|
||||||
|
cp := make([]string, len(v))
|
||||||
|
copy(cp, v)
|
||||||
|
return Value{
|
||||||
|
vtype: STRINGSLICE,
|
||||||
|
slice: &cp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type returns a type of the Value.
|
||||||
|
func (v Value) Type() Type {
|
||||||
|
return v.vtype
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsBool returns the bool value. Make sure that the Value's type is
|
||||||
|
// BOOL.
|
||||||
|
func (v Value) AsBool() bool {
|
||||||
|
return internal.RawToBool(v.numeric)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsBoolSlice returns the []bool value. Make sure that the Value's type is
|
||||||
|
// BOOLSLICE.
|
||||||
|
func (v Value) AsBoolSlice() []bool {
|
||||||
|
if s, ok := v.slice.(*[]bool); ok {
|
||||||
|
return *s
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsInt64 returns the int64 value. Make sure that the Value's type is
|
||||||
|
// INT64.
|
||||||
|
func (v Value) AsInt64() int64 {
|
||||||
|
return internal.RawToInt64(v.numeric)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsInt64Slice returns the []int64 value. Make sure that the Value's type is
|
||||||
|
// INT64SLICE.
|
||||||
|
func (v Value) AsInt64Slice() []int64 {
|
||||||
|
if s, ok := v.slice.(*[]int64); ok {
|
||||||
|
return *s
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsFloat64 returns the float64 value. Make sure that the Value's
|
||||||
|
// type is FLOAT64.
|
||||||
|
func (v Value) AsFloat64() float64 {
|
||||||
|
return internal.RawToFloat64(v.numeric)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsFloat64Slice returns the []float64 value. Make sure that the Value's type is
|
||||||
|
// INT64SLICE.
|
||||||
|
func (v Value) AsFloat64Slice() []float64 {
|
||||||
|
if s, ok := v.slice.(*[]float64); ok {
|
||||||
|
return *s
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsString returns the string value. Make sure that the Value's type
|
||||||
|
// is STRING.
|
||||||
|
func (v Value) AsString() string {
|
||||||
|
return v.stringly
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsStringSlice returns the []string value. Make sure that the Value's type is
|
||||||
|
// INT64SLICE.
|
||||||
|
func (v Value) AsStringSlice() []string {
|
||||||
|
if s, ok := v.slice.(*[]string); ok {
|
||||||
|
return *s
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type unknownValueType struct{}
|
||||||
|
|
||||||
|
// AsInterface returns Value's data as interface{}.
|
||||||
|
func (v Value) AsInterface() interface{} {
|
||||||
|
switch v.Type() {
|
||||||
|
case BOOL:
|
||||||
|
return v.AsBool()
|
||||||
|
case BOOLSLICE:
|
||||||
|
return v.AsBoolSlice()
|
||||||
|
case INT64:
|
||||||
|
return v.AsInt64()
|
||||||
|
case INT64SLICE:
|
||||||
|
return v.AsInt64Slice()
|
||||||
|
case FLOAT64:
|
||||||
|
return v.AsFloat64()
|
||||||
|
case FLOAT64SLICE:
|
||||||
|
return v.AsFloat64Slice()
|
||||||
|
case STRING:
|
||||||
|
return v.stringly
|
||||||
|
case STRINGSLICE:
|
||||||
|
return v.AsStringSlice()
|
||||||
|
}
|
||||||
|
return unknownValueType{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit returns a string representation of Value's data.
|
||||||
|
func (v Value) Emit() string {
|
||||||
|
switch v.Type() {
|
||||||
|
case BOOLSLICE:
|
||||||
|
return fmt.Sprint(*(v.slice.(*[]bool)))
|
||||||
|
case BOOL:
|
||||||
|
return strconv.FormatBool(v.AsBool())
|
||||||
|
case INT64SLICE:
|
||||||
|
return fmt.Sprint(*(v.slice.(*[]int64)))
|
||||||
|
case INT64:
|
||||||
|
return strconv.FormatInt(v.AsInt64(), 10)
|
||||||
|
case FLOAT64SLICE:
|
||||||
|
return fmt.Sprint(*(v.slice.(*[]float64)))
|
||||||
|
case FLOAT64:
|
||||||
|
return fmt.Sprint(v.AsFloat64())
|
||||||
|
case STRINGSLICE:
|
||||||
|
return fmt.Sprint(*(v.slice.(*[]string)))
|
||||||
|
case STRING:
|
||||||
|
return v.stringly
|
||||||
|
default:
|
||||||
|
return "unknown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON returns the JSON encoding of the Value.
|
||||||
|
func (v Value) MarshalJSON() ([]byte, error) {
|
||||||
|
var jsonVal struct {
|
||||||
|
Type string
|
||||||
|
Value interface{}
|
||||||
|
}
|
||||||
|
jsonVal.Type = v.Type().String()
|
||||||
|
jsonVal.Value = v.AsInterface()
|
||||||
|
return json.Marshal(jsonVal)
|
||||||
|
}
|
||||||
509
vendor/go.opentelemetry.io/otel/baggage/baggage.go
generated
vendored
Normal file
509
vendor/go.opentelemetry.io/otel/baggage/baggage.go
generated
vendored
Normal file
@@ -0,0 +1,509 @@
|
|||||||
|
// 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 // import "go.opentelemetry.io/otel/baggage"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/internal/baggage"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
maxMembers = 180
|
||||||
|
maxBytesPerMembers = 4096
|
||||||
|
maxBytesPerBaggageString = 8192
|
||||||
|
|
||||||
|
listDelimiter = ","
|
||||||
|
keyValueDelimiter = "="
|
||||||
|
propertyDelimiter = ";"
|
||||||
|
|
||||||
|
keyDef = `([\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5a\x5e-\x7a\x7c\x7e]+)`
|
||||||
|
valueDef = `([\x21\x23-\x2b\x2d-\x3a\x3c-\x5B\x5D-\x7e]*)`
|
||||||
|
keyValueDef = `\s*` + keyDef + `\s*` + keyValueDelimiter + `\s*` + valueDef + `\s*`
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
keyRe = regexp.MustCompile(`^` + keyDef + `$`)
|
||||||
|
valueRe = regexp.MustCompile(`^` + valueDef + `$`)
|
||||||
|
propertyRe = regexp.MustCompile(`^(?:\s*` + keyDef + `\s*|` + keyValueDef + `)$`)
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errInvalidKey = errors.New("invalid key")
|
||||||
|
errInvalidValue = errors.New("invalid value")
|
||||||
|
errInvalidProperty = errors.New("invalid baggage list-member property")
|
||||||
|
errInvalidMember = errors.New("invalid baggage list-member")
|
||||||
|
errMemberNumber = errors.New("too many list-members in baggage-string")
|
||||||
|
errMemberBytes = errors.New("list-member too large")
|
||||||
|
errBaggageBytes = errors.New("baggage-string too large")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Property is an additional metadata entry for a baggage list-member.
|
||||||
|
type Property struct {
|
||||||
|
key, value string
|
||||||
|
|
||||||
|
// hasValue indicates if a zero-value value means the property does not
|
||||||
|
// have a value or if it was the zero-value.
|
||||||
|
hasValue bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewKeyProperty(key string) (Property, error) {
|
||||||
|
p := Property{}
|
||||||
|
if !keyRe.MatchString(key) {
|
||||||
|
return p, fmt.Errorf("%w: %q", errInvalidKey, key)
|
||||||
|
}
|
||||||
|
p.key = key
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewKeyValueProperty(key, value string) (Property, error) {
|
||||||
|
p := Property{}
|
||||||
|
if !keyRe.MatchString(key) {
|
||||||
|
return p, fmt.Errorf("%w: %q", errInvalidKey, key)
|
||||||
|
}
|
||||||
|
if !valueRe.MatchString(value) {
|
||||||
|
return p, fmt.Errorf("%w: %q", errInvalidValue, value)
|
||||||
|
}
|
||||||
|
p.key = key
|
||||||
|
p.value = value
|
||||||
|
p.hasValue = true
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseProperty attempts to decode a Property from the passed string. It
|
||||||
|
// returns an error if the input is invalid according to the W3C Baggage
|
||||||
|
// specification.
|
||||||
|
func parseProperty(property string) (Property, error) {
|
||||||
|
p := Property{}
|
||||||
|
if property == "" {
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
match := propertyRe.FindStringSubmatch(property)
|
||||||
|
if len(match) != 4 {
|
||||||
|
return p, fmt.Errorf("%w: %q", errInvalidProperty, property)
|
||||||
|
}
|
||||||
|
|
||||||
|
if match[1] != "" {
|
||||||
|
p.key = match[1]
|
||||||
|
} else {
|
||||||
|
p.key = match[2]
|
||||||
|
p.value = match[3]
|
||||||
|
p.hasValue = true
|
||||||
|
}
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate ensures p conforms to the W3C Baggage specification, returning an
|
||||||
|
// error otherwise.
|
||||||
|
func (p Property) validate() error {
|
||||||
|
errFunc := func(err error) error {
|
||||||
|
return fmt.Errorf("invalid property: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !keyRe.MatchString(p.key) {
|
||||||
|
return errFunc(fmt.Errorf("%w: %q", errInvalidKey, p.key))
|
||||||
|
}
|
||||||
|
if p.hasValue && !valueRe.MatchString(p.value) {
|
||||||
|
return errFunc(fmt.Errorf("%w: %q", errInvalidValue, p.value))
|
||||||
|
}
|
||||||
|
if !p.hasValue && p.value != "" {
|
||||||
|
return errFunc(errors.New("inconsistent value"))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key returns the Property key.
|
||||||
|
func (p Property) Key() string {
|
||||||
|
return p.key
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value returns the Property value. Additionally a boolean value is returned
|
||||||
|
// indicating if the returned value is the empty if the Property has a value
|
||||||
|
// that is empty or if the value is not set.
|
||||||
|
func (p Property) Value() (string, bool) {
|
||||||
|
return p.value, p.hasValue
|
||||||
|
}
|
||||||
|
|
||||||
|
// String encodes Property into a string compliant with the W3C Baggage
|
||||||
|
// specification.
|
||||||
|
func (p Property) String() string {
|
||||||
|
if p.hasValue {
|
||||||
|
return fmt.Sprintf("%s%s%v", p.key, keyValueDelimiter, p.value)
|
||||||
|
}
|
||||||
|
return p.key
|
||||||
|
}
|
||||||
|
|
||||||
|
type properties []Property
|
||||||
|
|
||||||
|
func fromInternalProperties(iProps []baggage.Property) properties {
|
||||||
|
if len(iProps) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
props := make(properties, len(iProps))
|
||||||
|
for i, p := range iProps {
|
||||||
|
props[i] = Property{
|
||||||
|
key: p.Key,
|
||||||
|
value: p.Value,
|
||||||
|
hasValue: p.HasValue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return props
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p properties) asInternal() []baggage.Property {
|
||||||
|
if len(p) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
iProps := make([]baggage.Property, len(p))
|
||||||
|
for i, prop := range p {
|
||||||
|
iProps[i] = baggage.Property{
|
||||||
|
Key: prop.key,
|
||||||
|
Value: prop.value,
|
||||||
|
HasValue: prop.hasValue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return iProps
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p properties) Copy() properties {
|
||||||
|
if len(p) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
props := make(properties, len(p))
|
||||||
|
copy(props, p)
|
||||||
|
return props
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate ensures each Property in p conforms to the W3C Baggage
|
||||||
|
// specification, returning an error otherwise.
|
||||||
|
func (p properties) validate() error {
|
||||||
|
for _, prop := range p {
|
||||||
|
if err := prop.validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// String encodes properties into a string compliant with the W3C Baggage
|
||||||
|
// specification.
|
||||||
|
func (p properties) String() string {
|
||||||
|
props := make([]string, len(p))
|
||||||
|
for i, prop := range p {
|
||||||
|
props[i] = prop.String()
|
||||||
|
}
|
||||||
|
return strings.Join(props, propertyDelimiter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Member is a list-member of a baggage-string as defined by the W3C Baggage
|
||||||
|
// specification.
|
||||||
|
type Member struct {
|
||||||
|
key, value string
|
||||||
|
properties properties
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMember returns a new Member from the passed arguments. An error is
|
||||||
|
// returned if the created Member would be invalid according to the W3C
|
||||||
|
// Baggage specification.
|
||||||
|
func NewMember(key, value string, props ...Property) (Member, error) {
|
||||||
|
m := Member{key: key, value: value, properties: properties(props).Copy()}
|
||||||
|
if err := m.validate(); err != nil {
|
||||||
|
return Member{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseMember attempts to decode a Member from the passed string. It returns
|
||||||
|
// an error if the input is invalid according to the W3C Baggage
|
||||||
|
// specification.
|
||||||
|
func parseMember(member string) (Member, error) {
|
||||||
|
if n := len(member); n > maxBytesPerMembers {
|
||||||
|
return Member{}, fmt.Errorf("%w: %d", errMemberBytes, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
key, value string
|
||||||
|
props properties
|
||||||
|
)
|
||||||
|
|
||||||
|
parts := strings.SplitN(member, propertyDelimiter, 2)
|
||||||
|
switch len(parts) {
|
||||||
|
case 2:
|
||||||
|
// Parse the member properties.
|
||||||
|
for _, pStr := range strings.Split(parts[1], propertyDelimiter) {
|
||||||
|
p, err := parseProperty(pStr)
|
||||||
|
if err != nil {
|
||||||
|
return Member{}, err
|
||||||
|
}
|
||||||
|
props = append(props, p)
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
case 1:
|
||||||
|
// Parse the member key/value pair.
|
||||||
|
|
||||||
|
// Take into account a value can contain equal signs (=).
|
||||||
|
kv := strings.SplitN(parts[0], keyValueDelimiter, 2)
|
||||||
|
if len(kv) != 2 {
|
||||||
|
return Member{}, fmt.Errorf("%w: %q", errInvalidMember, member)
|
||||||
|
}
|
||||||
|
// "Leading and trailing whitespaces are allowed but MUST be trimmed
|
||||||
|
// when converting the header into a data structure."
|
||||||
|
key, value = strings.TrimSpace(kv[0]), strings.TrimSpace(kv[1])
|
||||||
|
if !keyRe.MatchString(key) {
|
||||||
|
return Member{}, fmt.Errorf("%w: %q", errInvalidKey, key)
|
||||||
|
}
|
||||||
|
if !valueRe.MatchString(value) {
|
||||||
|
return Member{}, fmt.Errorf("%w: %q", errInvalidValue, value)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// This should never happen unless a developer has changed the string
|
||||||
|
// splitting somehow. Panic instead of failing silently and allowing
|
||||||
|
// the bug to slip past the CI checks.
|
||||||
|
panic("failed to parse baggage member")
|
||||||
|
}
|
||||||
|
|
||||||
|
return Member{key: key, value: value, properties: props}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate ensures m conforms to the W3C Baggage specification, returning an
|
||||||
|
// error otherwise.
|
||||||
|
func (m Member) validate() error {
|
||||||
|
if !keyRe.MatchString(m.key) {
|
||||||
|
return fmt.Errorf("%w: %q", errInvalidKey, m.key)
|
||||||
|
}
|
||||||
|
if !valueRe.MatchString(m.value) {
|
||||||
|
return fmt.Errorf("%w: %q", errInvalidValue, m.value)
|
||||||
|
}
|
||||||
|
return m.properties.validate()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key returns the Member key.
|
||||||
|
func (m Member) Key() string { return m.key }
|
||||||
|
|
||||||
|
// Value returns the Member value.
|
||||||
|
func (m Member) Value() string { return m.value }
|
||||||
|
|
||||||
|
// Properties returns a copy of the Member properties.
|
||||||
|
func (m Member) Properties() []Property { return m.properties.Copy() }
|
||||||
|
|
||||||
|
// String encodes Member into a string compliant with the W3C Baggage
|
||||||
|
// specification.
|
||||||
|
func (m Member) String() string {
|
||||||
|
// A key is just an ASCII string, but a value is URL encoded UTF-8.
|
||||||
|
s := fmt.Sprintf("%s%s%s", m.key, keyValueDelimiter, url.QueryEscape(m.value))
|
||||||
|
if len(m.properties) > 0 {
|
||||||
|
s = fmt.Sprintf("%s%s%s", s, propertyDelimiter, m.properties.String())
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Baggage is a list of baggage members representing the baggage-string as
|
||||||
|
// defined by the W3C Baggage specification.
|
||||||
|
type Baggage struct { //nolint:golint
|
||||||
|
list baggage.List
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a new valid Baggage. It returns an error if the passed members
|
||||||
|
// are invalid according to the W3C Baggage specification or if it results in
|
||||||
|
// a Baggage exceeding limits set in that specification.
|
||||||
|
func New(members ...Member) (Baggage, error) {
|
||||||
|
if len(members) == 0 {
|
||||||
|
return Baggage{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
b := make(baggage.List)
|
||||||
|
for _, m := range members {
|
||||||
|
if err := m.validate(); err != nil {
|
||||||
|
return Baggage{}, err
|
||||||
|
}
|
||||||
|
// OpenTelemetry resolves duplicates by last-one-wins.
|
||||||
|
b[m.key] = baggage.Item{
|
||||||
|
Value: m.value,
|
||||||
|
Properties: m.properties.asInternal(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check member numbers after deduplicating.
|
||||||
|
if len(b) > maxMembers {
|
||||||
|
return Baggage{}, errMemberNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
bag := Baggage{b}
|
||||||
|
if n := len(bag.String()); n > maxBytesPerBaggageString {
|
||||||
|
return Baggage{}, fmt.Errorf("%w: %d", errBaggageBytes, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
return bag, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse attempts to decode a baggage-string from the passed string. It
|
||||||
|
// returns an error if the input is invalid according to the W3C Baggage
|
||||||
|
// specification.
|
||||||
|
//
|
||||||
|
// If there are duplicate list-members contained in baggage, the last one
|
||||||
|
// defined (reading left-to-right) will be the only one kept. This diverges
|
||||||
|
// from the W3C Baggage specification which allows duplicate list-members, but
|
||||||
|
// conforms to the OpenTelemetry Baggage specification.
|
||||||
|
func Parse(bStr string) (Baggage, error) {
|
||||||
|
if bStr == "" {
|
||||||
|
return Baggage{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if n := len(bStr); n > maxBytesPerBaggageString {
|
||||||
|
return Baggage{}, fmt.Errorf("%w: %d", errBaggageBytes, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
b := make(baggage.List)
|
||||||
|
for _, memberStr := range strings.Split(bStr, listDelimiter) {
|
||||||
|
m, err := parseMember(memberStr)
|
||||||
|
if err != nil {
|
||||||
|
return Baggage{}, err
|
||||||
|
}
|
||||||
|
// OpenTelemetry resolves duplicates by last-one-wins.
|
||||||
|
b[m.key] = baggage.Item{
|
||||||
|
Value: m.value,
|
||||||
|
Properties: m.properties.asInternal(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenTelemetry does not allow for duplicate list-members, but the W3C
|
||||||
|
// specification does. Now that we have deduplicated, ensure the baggage
|
||||||
|
// does not exceed list-member limits.
|
||||||
|
if len(b) > maxMembers {
|
||||||
|
return Baggage{}, errMemberNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
return Baggage{b}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Member returns the baggage list-member identified by key.
|
||||||
|
//
|
||||||
|
// If there is no list-member matching the passed key the returned Member will
|
||||||
|
// be a zero-value Member.
|
||||||
|
func (b Baggage) Member(key string) Member {
|
||||||
|
v, ok := b.list[key]
|
||||||
|
if !ok {
|
||||||
|
// We do not need to worry about distiguising between the situation
|
||||||
|
// where a zero-valued Member is included in the Baggage because a
|
||||||
|
// zero-valued Member is invalid according to the W3C Baggage
|
||||||
|
// specification (it has an empty key).
|
||||||
|
return Member{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Member{
|
||||||
|
key: key,
|
||||||
|
value: v.Value,
|
||||||
|
properties: fromInternalProperties(v.Properties),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Members returns all the baggage list-members.
|
||||||
|
// The order of the returned list-members does not have significance.
|
||||||
|
func (b Baggage) Members() []Member {
|
||||||
|
if len(b.list) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
members := make([]Member, 0, len(b.list))
|
||||||
|
for k, v := range b.list {
|
||||||
|
members = append(members, Member{
|
||||||
|
key: k,
|
||||||
|
value: v.Value,
|
||||||
|
properties: fromInternalProperties(v.Properties),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return members
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMember returns a copy the Baggage with the member included. If the
|
||||||
|
// baggage contains a Member with the same key the existing Member is
|
||||||
|
// replaced.
|
||||||
|
//
|
||||||
|
// If member is invalid according to the W3C Baggage specification, an error
|
||||||
|
// is returned with the original Baggage.
|
||||||
|
func (b Baggage) SetMember(member Member) (Baggage, error) {
|
||||||
|
if err := member.validate(); err != nil {
|
||||||
|
return b, fmt.Errorf("%w: %s", errInvalidMember, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
n := len(b.list)
|
||||||
|
if _, ok := b.list[member.key]; !ok {
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
list := make(baggage.List, n)
|
||||||
|
|
||||||
|
for k, v := range b.list {
|
||||||
|
// Do not copy if we are just going to overwrite.
|
||||||
|
if k == member.key {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
list[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
list[member.key] = baggage.Item{
|
||||||
|
Value: member.value,
|
||||||
|
Properties: member.properties.asInternal(),
|
||||||
|
}
|
||||||
|
|
||||||
|
return Baggage{list: list}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteMember returns a copy of the Baggage with the list-member identified
|
||||||
|
// by key removed.
|
||||||
|
func (b Baggage) DeleteMember(key string) Baggage {
|
||||||
|
n := len(b.list)
|
||||||
|
if _, ok := b.list[key]; ok {
|
||||||
|
n--
|
||||||
|
}
|
||||||
|
list := make(baggage.List, n)
|
||||||
|
|
||||||
|
for k, v := range b.list {
|
||||||
|
if k == key {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
list[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
return Baggage{list: list}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the number of list-members in the Baggage.
|
||||||
|
func (b Baggage) Len() int {
|
||||||
|
return len(b.list)
|
||||||
|
}
|
||||||
|
|
||||||
|
// String encodes Baggage into a string compliant with the W3C Baggage
|
||||||
|
// specification. The returned string will be invalid if the Baggage contains
|
||||||
|
// any invalid list-members.
|
||||||
|
func (b Baggage) String() string {
|
||||||
|
members := make([]string, 0, len(b.list))
|
||||||
|
for k, v := range b.list {
|
||||||
|
members = append(members, Member{
|
||||||
|
key: k,
|
||||||
|
value: v.Value,
|
||||||
|
properties: fromInternalProperties(v.Properties),
|
||||||
|
}.String())
|
||||||
|
}
|
||||||
|
return strings.Join(members, listDelimiter)
|
||||||
|
}
|
||||||
39
vendor/go.opentelemetry.io/otel/baggage/context.go
generated
vendored
Normal file
39
vendor/go.opentelemetry.io/otel/baggage/context.go
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
// 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 // import "go.opentelemetry.io/otel/baggage"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/internal/baggage"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ContextWithBaggage returns a copy of parent with baggage.
|
||||||
|
func ContextWithBaggage(parent context.Context, b Baggage) context.Context {
|
||||||
|
// Delegate so any hooks for the OpenTracing bridge are handled.
|
||||||
|
return baggage.ContextWithList(parent, b.list)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContextWithoutBaggage returns a copy of parent with no baggage.
|
||||||
|
func ContextWithoutBaggage(parent context.Context) context.Context {
|
||||||
|
// Delegate so any hooks for the OpenTracing bridge are handled.
|
||||||
|
return baggage.ContextWithList(parent, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromContext returns the baggage contained in ctx.
|
||||||
|
func FromContext(ctx context.Context) Baggage {
|
||||||
|
// Delegate so any hooks for the OpenTracing bridge are handled.
|
||||||
|
return Baggage{list: baggage.ListFromContext(ctx)}
|
||||||
|
}
|
||||||
20
vendor/go.opentelemetry.io/otel/baggage/doc.go
generated
vendored
Normal file
20
vendor/go.opentelemetry.io/otel/baggage/doc.go
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
// 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 functionality for storing and retrieving
|
||||||
|
baggage items in Go context. For propagating the baggage, see the
|
||||||
|
go.opentelemetry.io/otel/propagation package.
|
||||||
|
*/
|
||||||
|
package baggage // import "go.opentelemetry.io/otel/baggage"
|
||||||
106
vendor/go.opentelemetry.io/otel/codes/codes.go
generated
vendored
Normal file
106
vendor/go.opentelemetry.io/otel/codes/codes.go
generated
vendored
Normal file
@@ -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
|
||||||
|
}
|
||||||
21
vendor/go.opentelemetry.io/otel/codes/doc.go
generated
vendored
Normal file
21
vendor/go.opentelemetry.io/otel/codes/doc.go
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
It conforms to [the OpenTelemetry
|
||||||
|
specification](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md#statuscanonicalcode).
|
||||||
|
*/
|
||||||
|
package codes // import "go.opentelemetry.io/otel/codes"
|
||||||
34
vendor/go.opentelemetry.io/otel/doc.go
generated
vendored
Normal file
34
vendor/go.opentelemetry.io/otel/doc.go
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
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"
|
||||||
38
vendor/go.opentelemetry.io/otel/error_handler.go
generated
vendored
Normal file
38
vendor/go.opentelemetry.io/otel/error_handler.go
generated
vendored
Normal file
@@ -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 // import "go.opentelemetry.io/otel"
|
||||||
|
|
||||||
|
// ErrorHandler handles irremediable events.
|
||||||
|
type ErrorHandler interface {
|
||||||
|
// DO NOT CHANGE: any modification will not be backwards compatible and
|
||||||
|
// must never be done outside of a new major release.
|
||||||
|
|
||||||
|
// Handle handles any error deemed irremediable by an OpenTelemetry
|
||||||
|
// component.
|
||||||
|
Handle(error)
|
||||||
|
// DO NOT CHANGE: any modification will not be backwards compatible and
|
||||||
|
// must never be done outside of a new major release.
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrorHandlerFunc is a convenience adapter to allow the use of a function
|
||||||
|
// as an ErrorHandler.
|
||||||
|
type ErrorHandlerFunc func(error)
|
||||||
|
|
||||||
|
var _ ErrorHandler = ErrorHandlerFunc(nil)
|
||||||
|
|
||||||
|
// Handle handles the irremediable error by calling the ErrorHandlerFunc itself.
|
||||||
|
func (f ErrorHandlerFunc) Handle(err error) {
|
||||||
|
f(err)
|
||||||
|
}
|
||||||
201
vendor/go.opentelemetry.io/otel/exporters/stdout/stdoutmetric/LICENSE
generated
vendored
Normal file
201
vendor/go.opentelemetry.io/otel/exporters/stdout/stdoutmetric/LICENSE
generated
vendored
Normal file
@@ -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.
|
||||||
114
vendor/go.opentelemetry.io/otel/exporters/stdout/stdoutmetric/config.go
generated
vendored
Normal file
114
vendor/go.opentelemetry.io/otel/exporters/stdout/stdoutmetric/config.go
generated
vendored
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
// 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 stdoutmetric // import "go.opentelemetry.io/otel/exporters/stdout/stdoutmetric"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
defaultWriter = os.Stdout
|
||||||
|
defaultPrettyPrint = false
|
||||||
|
defaultTimestamps = true
|
||||||
|
defaultLabelEncoder = attribute.DefaultEncoder()
|
||||||
|
)
|
||||||
|
|
||||||
|
// config contains options for the STDOUT exporter.
|
||||||
|
type config struct {
|
||||||
|
// Writer is the destination. If not set, os.Stdout is used.
|
||||||
|
Writer io.Writer
|
||||||
|
|
||||||
|
// PrettyPrint will encode the output into readable JSON. Default is
|
||||||
|
// false.
|
||||||
|
PrettyPrint bool
|
||||||
|
|
||||||
|
// Timestamps specifies if timestamps should be printed. Default is
|
||||||
|
// true.
|
||||||
|
Timestamps bool
|
||||||
|
|
||||||
|
// LabelEncoder encodes the labels.
|
||||||
|
LabelEncoder attribute.Encoder
|
||||||
|
}
|
||||||
|
|
||||||
|
// newConfig creates a validated Config configured with options.
|
||||||
|
func newConfig(options ...Option) (config, error) {
|
||||||
|
cfg := config{
|
||||||
|
Writer: defaultWriter,
|
||||||
|
PrettyPrint: defaultPrettyPrint,
|
||||||
|
Timestamps: defaultTimestamps,
|
||||||
|
LabelEncoder: defaultLabelEncoder,
|
||||||
|
}
|
||||||
|
for _, opt := range options {
|
||||||
|
opt.apply(&cfg)
|
||||||
|
|
||||||
|
}
|
||||||
|
return cfg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Option sets the value of an option for a Config.
|
||||||
|
type Option interface {
|
||||||
|
apply(*config)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithWriter sets the export stream destination.
|
||||||
|
func WithWriter(w io.Writer) Option {
|
||||||
|
return writerOption{w}
|
||||||
|
}
|
||||||
|
|
||||||
|
type writerOption struct {
|
||||||
|
W io.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o writerOption) apply(cfg *config) {
|
||||||
|
cfg.Writer = o.W
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithPrettyPrint sets the export stream format to use JSON.
|
||||||
|
func WithPrettyPrint() Option {
|
||||||
|
return prettyPrintOption(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
type prettyPrintOption bool
|
||||||
|
|
||||||
|
func (o prettyPrintOption) apply(cfg *config) {
|
||||||
|
cfg.PrettyPrint = bool(o)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithoutTimestamps sets the export stream to not include timestamps.
|
||||||
|
func WithoutTimestamps() Option {
|
||||||
|
return timestampsOption(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
type timestampsOption bool
|
||||||
|
|
||||||
|
func (o timestampsOption) apply(cfg *config) {
|
||||||
|
cfg.Timestamps = bool(o)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithLabelEncoder sets the label encoder used in export.
|
||||||
|
func WithLabelEncoder(enc attribute.Encoder) Option {
|
||||||
|
return labelEncoderOption{enc}
|
||||||
|
}
|
||||||
|
|
||||||
|
type labelEncoderOption struct {
|
||||||
|
LabelEncoder attribute.Encoder
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o labelEncoderOption) apply(cfg *config) {
|
||||||
|
cfg.LabelEncoder = o.LabelEncoder
|
||||||
|
}
|
||||||
21
vendor/go.opentelemetry.io/otel/exporters/stdout/stdoutmetric/doc.go
generated
vendored
Normal file
21
vendor/go.opentelemetry.io/otel/exporters/stdout/stdoutmetric/doc.go
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
// 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 stdout contains an OpenTelemetry exporter for metric telemetry
|
||||||
|
// to be written to an output destination as JSON.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
package stdoutmetric // import "go.opentelemetry.io/otel/exporters/stdout/stdoutmetric"
|
||||||
38
vendor/go.opentelemetry.io/otel/exporters/stdout/stdoutmetric/exporter.go
generated
vendored
Normal file
38
vendor/go.opentelemetry.io/otel/exporters/stdout/stdoutmetric/exporter.go
generated
vendored
Normal file
@@ -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 stdoutmetric // import "go.opentelemetry.io/otel/exporters/stdout/stdoutmetric"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go.opentelemetry.io/otel/sdk/export/metric"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Exporter struct {
|
||||||
|
metricExporter
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ metric.Exporter = &Exporter{}
|
||||||
|
)
|
||||||
|
|
||||||
|
// New creates an Exporter with the passed options.
|
||||||
|
func New(options ...Option) (*Exporter, error) {
|
||||||
|
cfg, err := newConfig(options...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Exporter{
|
||||||
|
metricExporter: metricExporter{cfg},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
145
vendor/go.opentelemetry.io/otel/exporters/stdout/stdoutmetric/metric.go
generated
vendored
Normal file
145
vendor/go.opentelemetry.io/otel/exporters/stdout/stdoutmetric/metric.go
generated
vendored
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
// 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 stdoutmetric // import "go.opentelemetry.io/otel/exporters/stdout/stdoutmetric"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/metric/sdkapi"
|
||||||
|
exportmetric "go.opentelemetry.io/otel/sdk/export/metric"
|
||||||
|
"go.opentelemetry.io/otel/sdk/export/metric/aggregation"
|
||||||
|
"go.opentelemetry.io/otel/sdk/instrumentation"
|
||||||
|
"go.opentelemetry.io/otel/sdk/resource"
|
||||||
|
)
|
||||||
|
|
||||||
|
type metricExporter struct {
|
||||||
|
config config
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ exportmetric.Exporter = &metricExporter{}
|
||||||
|
|
||||||
|
type line struct {
|
||||||
|
Name string `json:"Name"`
|
||||||
|
Sum interface{} `json:"Sum,omitempty"`
|
||||||
|
Count interface{} `json:"Count,omitempty"`
|
||||||
|
LastValue interface{} `json:"Last,omitempty"`
|
||||||
|
|
||||||
|
// Note: this is a pointer because omitempty doesn't work when time.IsZero()
|
||||||
|
Timestamp *time.Time `json:"Timestamp,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *metricExporter) TemporalityFor(desc *sdkapi.Descriptor, kind aggregation.Kind) aggregation.Temporality {
|
||||||
|
return aggregation.StatelessTemporalitySelector().TemporalityFor(desc, kind)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *metricExporter) Export(_ context.Context, res *resource.Resource, reader exportmetric.InstrumentationLibraryReader) error {
|
||||||
|
var aggError error
|
||||||
|
var batch []line
|
||||||
|
aggError = reader.ForEach(func(lib instrumentation.Library, mr exportmetric.Reader) error {
|
||||||
|
|
||||||
|
var instLabels []attribute.KeyValue
|
||||||
|
if name := lib.Name; name != "" {
|
||||||
|
instLabels = append(instLabels, attribute.String("instrumentation.name", name))
|
||||||
|
if version := lib.Version; version != "" {
|
||||||
|
instLabels = append(instLabels, attribute.String("instrumentation.version", version))
|
||||||
|
}
|
||||||
|
if schema := lib.SchemaURL; schema != "" {
|
||||||
|
instLabels = append(instLabels, attribute.String("instrumentation.schema_url", schema))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
instSet := attribute.NewSet(instLabels...)
|
||||||
|
encodedInstLabels := instSet.Encoded(e.config.LabelEncoder)
|
||||||
|
|
||||||
|
return mr.ForEach(e, func(record exportmetric.Record) error {
|
||||||
|
desc := record.Descriptor()
|
||||||
|
agg := record.Aggregation()
|
||||||
|
kind := desc.NumberKind()
|
||||||
|
encodedResource := res.Encoded(e.config.LabelEncoder)
|
||||||
|
|
||||||
|
var expose line
|
||||||
|
|
||||||
|
if sum, ok := agg.(aggregation.Sum); ok {
|
||||||
|
value, err := sum.Sum()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
expose.Sum = value.AsInterface(kind)
|
||||||
|
} else if lv, ok := agg.(aggregation.LastValue); ok {
|
||||||
|
value, timestamp, err := lv.LastValue()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
expose.LastValue = value.AsInterface(kind)
|
||||||
|
|
||||||
|
if e.config.Timestamps {
|
||||||
|
expose.Timestamp = ×tamp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var encodedLabels string
|
||||||
|
iter := record.Labels().Iter()
|
||||||
|
if iter.Len() > 0 {
|
||||||
|
encodedLabels = record.Labels().Encoded(e.config.LabelEncoder)
|
||||||
|
}
|
||||||
|
|
||||||
|
var sb strings.Builder
|
||||||
|
|
||||||
|
sb.WriteString(desc.Name())
|
||||||
|
|
||||||
|
if len(encodedLabels) > 0 || len(encodedResource) > 0 || len(encodedInstLabels) > 0 {
|
||||||
|
sb.WriteRune('{')
|
||||||
|
sb.WriteString(encodedResource)
|
||||||
|
if len(encodedInstLabels) > 0 && len(encodedResource) > 0 {
|
||||||
|
sb.WriteRune(',')
|
||||||
|
}
|
||||||
|
sb.WriteString(encodedInstLabels)
|
||||||
|
if len(encodedLabels) > 0 && (len(encodedInstLabels) > 0 || len(encodedResource) > 0) {
|
||||||
|
sb.WriteRune(',')
|
||||||
|
}
|
||||||
|
sb.WriteString(encodedLabels)
|
||||||
|
sb.WriteRune('}')
|
||||||
|
}
|
||||||
|
|
||||||
|
expose.Name = sb.String()
|
||||||
|
|
||||||
|
batch = append(batch, expose)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
})
|
||||||
|
if len(batch) == 0 {
|
||||||
|
return aggError
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := e.marshal(batch)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Fprintln(e.config.Writer, string(data))
|
||||||
|
|
||||||
|
return aggError
|
||||||
|
}
|
||||||
|
|
||||||
|
// marshal v with appropriate indentation.
|
||||||
|
func (e *metricExporter) marshal(v interface{}) ([]byte, error) {
|
||||||
|
if e.config.PrettyPrint {
|
||||||
|
return json.MarshalIndent(v, "", "\t")
|
||||||
|
}
|
||||||
|
return json.Marshal(v)
|
||||||
|
}
|
||||||
41
vendor/go.opentelemetry.io/otel/get_main_pkgs.sh
generated
vendored
Normal file
41
vendor/go.opentelemetry.io/otel/get_main_pkgs.sh
generated
vendored
Normal file
@@ -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
|
||||||
98
vendor/go.opentelemetry.io/otel/handler.go
generated
vendored
Normal file
98
vendor/go.opentelemetry.io/otel/handler.go
generated
vendored
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
// 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
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 = defaultErrorHandler()
|
||||||
|
|
||||||
|
// Compile-time check that delegator implements ErrorHandler.
|
||||||
|
_ ErrorHandler = (*delegator)(nil)
|
||||||
|
// Compile-time check that errLogger implements ErrorHandler.
|
||||||
|
_ ErrorHandler = (*errLogger)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
|
type delegator struct {
|
||||||
|
lock *sync.RWMutex
|
||||||
|
eh ErrorHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *delegator) Handle(err error) {
|
||||||
|
d.lock.RLock()
|
||||||
|
defer d.lock.RUnlock()
|
||||||
|
d.eh.Handle(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// setDelegate sets the ErrorHandler delegate.
|
||||||
|
func (d *delegator) setDelegate(eh ErrorHandler) {
|
||||||
|
d.lock.Lock()
|
||||||
|
defer d.lock.Unlock()
|
||||||
|
d.eh = eh
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultErrorHandler() *delegator {
|
||||||
|
return &delegator{
|
||||||
|
lock: &sync.RWMutex{},
|
||||||
|
eh: &errLogger{l: log.New(os.Stderr, "", log.LstdFlags)},
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// errLogger logs errors if no delegate is set, otherwise they are delegated.
|
||||||
|
type errLogger struct {
|
||||||
|
l *log.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle logs err if no delegate is set, otherwise it is delegated.
|
||||||
|
func (h *errLogger) Handle(err error) {
|
||||||
|
h.l.Print(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetErrorHandler returns the global ErrorHandler instance.
|
||||||
|
//
|
||||||
|
// The default ErrorHandler instance returned will log all errors to STDERR
|
||||||
|
// until an override ErrorHandler is set with SetErrorHandler. All
|
||||||
|
// ErrorHandler returned prior to this will automatically forward errors to
|
||||||
|
// the set instance instead of logging.
|
||||||
|
//
|
||||||
|
// Subsequent calls to SetErrorHandler after the first will not forward errors
|
||||||
|
// to the new ErrorHandler for prior returned instances.
|
||||||
|
func GetErrorHandler() ErrorHandler {
|
||||||
|
return globalErrorHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetErrorHandler sets the global ErrorHandler to h.
|
||||||
|
//
|
||||||
|
// The first time this is called all ErrorHandler previously returned from
|
||||||
|
// GetErrorHandler will send errors to h instead of the default logging
|
||||||
|
// ErrorHandler. Subsequent calls will set the global ErrorHandler, but not
|
||||||
|
// delegate errors to h.
|
||||||
|
func SetErrorHandler(h ErrorHandler) {
|
||||||
|
globalErrorHandler.setDelegate(h)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle is a convenience function for ErrorHandler().Handle(err)
|
||||||
|
func Handle(err error) {
|
||||||
|
GetErrorHandler().Handle(err)
|
||||||
|
}
|
||||||
43
vendor/go.opentelemetry.io/otel/internal/baggage/baggage.go
generated
vendored
Normal file
43
vendor/go.opentelemetry.io/otel/internal/baggage/baggage.go
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
// 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 base types and functionality to store and retrieve
|
||||||
|
baggage in Go context. This package exists because the OpenTracing bridge to
|
||||||
|
OpenTelemetry needs to synchronize state whenever baggage for a context is
|
||||||
|
modified and that context contains an OpenTracing span. If it were not for
|
||||||
|
this need this package would not need to exist and the
|
||||||
|
`go.opentelemetry.io/otel/baggage` package would be the singular place where
|
||||||
|
W3C baggage is handled.
|
||||||
|
*/
|
||||||
|
package baggage // import "go.opentelemetry.io/otel/internal/baggage"
|
||||||
|
|
||||||
|
// List is the collection of baggage members. The W3C allows for duplicates,
|
||||||
|
// but OpenTelemetry does not, therefore, this is represented as a map.
|
||||||
|
type List map[string]Item
|
||||||
|
|
||||||
|
// Item is the value and metadata properties part of a list-member.
|
||||||
|
type Item struct {
|
||||||
|
Value string
|
||||||
|
Properties []Property
|
||||||
|
}
|
||||||
|
|
||||||
|
// Property is a metadata entry for a list-member.
|
||||||
|
type Property struct {
|
||||||
|
Key, Value string
|
||||||
|
|
||||||
|
// HasValue indicates if a zero-value value means the property does not
|
||||||
|
// have a value or if it was the zero-value.
|
||||||
|
HasValue bool
|
||||||
|
}
|
||||||
95
vendor/go.opentelemetry.io/otel/internal/baggage/context.go
generated
vendored
Normal file
95
vendor/go.opentelemetry.io/otel/internal/baggage/context.go
generated
vendored
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
// 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 // import "go.opentelemetry.io/otel/internal/baggage"
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
type baggageContextKeyType int
|
||||||
|
|
||||||
|
const baggageKey baggageContextKeyType = iota
|
||||||
|
|
||||||
|
// SetHookFunc is a callback called when storing baggage in the context.
|
||||||
|
type SetHookFunc func(context.Context, List) context.Context
|
||||||
|
|
||||||
|
// GetHookFunc is a callback called when getting baggage from the context.
|
||||||
|
type GetHookFunc func(context.Context, List) List
|
||||||
|
|
||||||
|
type baggageState struct {
|
||||||
|
list List
|
||||||
|
|
||||||
|
setHook SetHookFunc
|
||||||
|
getHook GetHookFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContextWithSetHook returns a copy of parent with hook configured to be
|
||||||
|
// invoked every time ContextWithBaggage is called.
|
||||||
|
//
|
||||||
|
// Passing nil SetHookFunc creates a context with no set hook to call.
|
||||||
|
func ContextWithSetHook(parent context.Context, hook SetHookFunc) context.Context {
|
||||||
|
var s baggageState
|
||||||
|
switch v := parent.Value(baggageKey).(type) {
|
||||||
|
case baggageState:
|
||||||
|
s = v
|
||||||
|
}
|
||||||
|
|
||||||
|
s.setHook = hook
|
||||||
|
return context.WithValue(parent, baggageKey, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContextWithGetHook returns a copy of parent with hook configured to be
|
||||||
|
// invoked every time FromContext is called.
|
||||||
|
//
|
||||||
|
// Passing nil GetHookFunc creates a context with no get hook to call.
|
||||||
|
func ContextWithGetHook(parent context.Context, hook GetHookFunc) context.Context {
|
||||||
|
var s baggageState
|
||||||
|
switch v := parent.Value(baggageKey).(type) {
|
||||||
|
case baggageState:
|
||||||
|
s = v
|
||||||
|
}
|
||||||
|
|
||||||
|
s.getHook = hook
|
||||||
|
return context.WithValue(parent, baggageKey, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContextWithList returns a copy of parent with baggage. Passing nil list
|
||||||
|
// returns a context without any baggage.
|
||||||
|
func ContextWithList(parent context.Context, list List) context.Context {
|
||||||
|
var s baggageState
|
||||||
|
switch v := parent.Value(baggageKey).(type) {
|
||||||
|
case baggageState:
|
||||||
|
s = v
|
||||||
|
}
|
||||||
|
|
||||||
|
s.list = list
|
||||||
|
ctx := context.WithValue(parent, baggageKey, s)
|
||||||
|
if s.setHook != nil {
|
||||||
|
ctx = s.setHook(ctx, list)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListFromContext returns the baggage contained in ctx.
|
||||||
|
func ListFromContext(ctx context.Context) List {
|
||||||
|
switch v := ctx.Value(baggageKey).(type) {
|
||||||
|
case baggageState:
|
||||||
|
if v.getHook != nil {
|
||||||
|
return v.getHook(ctx, v.list)
|
||||||
|
}
|
||||||
|
return v.list
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
63
vendor/go.opentelemetry.io/otel/internal/global/internal_logging.go
generated
vendored
Normal file
63
vendor/go.opentelemetry.io/otel/internal/global/internal_logging.go
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
// 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 "go.opentelemetry.io/otel/internal/global"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/go-logr/logr"
|
||||||
|
"github.com/go-logr/stdr"
|
||||||
|
)
|
||||||
|
|
||||||
|
// globalLogger is the logging interface used within the otel api and sdk provide deatails of the internals.
|
||||||
|
//
|
||||||
|
// The default logger uses stdr which is backed by the standard `log.Logger`
|
||||||
|
// interface. This logger will only show messages at the Error Level.
|
||||||
|
var globalLogger logr.Logger = stdr.New(log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile))
|
||||||
|
var globalLoggerLock = &sync.RWMutex{}
|
||||||
|
|
||||||
|
// SetLogger overrides the globalLogger with l.
|
||||||
|
//
|
||||||
|
// To see Info messages use a logger with `l.V(1).Enabled() == true`
|
||||||
|
// To see Debug messages use a logger with `l.V(5).Enabled() == true`
|
||||||
|
func SetLogger(l logr.Logger) {
|
||||||
|
globalLoggerLock.Lock()
|
||||||
|
defer globalLoggerLock.Unlock()
|
||||||
|
globalLogger = l
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info prints messages about the general state of the API or SDK.
|
||||||
|
// This should usually be less then 5 messages a minute
|
||||||
|
func Info(msg string, keysAndValues ...interface{}) {
|
||||||
|
globalLoggerLock.RLock()
|
||||||
|
defer globalLoggerLock.RUnlock()
|
||||||
|
globalLogger.V(1).Info(msg, keysAndValues...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error prints messages about exceptional states of the API or SDK.
|
||||||
|
func Error(err error, msg string, keysAndValues ...interface{}) {
|
||||||
|
globalLoggerLock.RLock()
|
||||||
|
defer globalLoggerLock.RUnlock()
|
||||||
|
globalLogger.Error(err, msg, keysAndValues...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug prints messages about all internal changes in the API or SDK.
|
||||||
|
func Debug(msg string, keysAndValues ...interface{}) {
|
||||||
|
globalLoggerLock.RLock()
|
||||||
|
defer globalLoggerLock.RUnlock()
|
||||||
|
globalLogger.V(5).Info(msg, keysAndValues...)
|
||||||
|
}
|
||||||
82
vendor/go.opentelemetry.io/otel/internal/global/propagator.go
generated
vendored
Normal file
82
vendor/go.opentelemetry.io/otel/internal/global/propagator.go
generated
vendored
Normal file
@@ -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 "go.opentelemetry.io/otel/internal/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()
|
||||||
|
}
|
||||||
106
vendor/go.opentelemetry.io/otel/internal/global/state.go
generated
vendored
Normal file
106
vendor/go.opentelemetry.io/otel/internal/global/state.go
generated
vendored
Normal file
@@ -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 global // import "go.opentelemetry.io/otel/internal/global"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/propagation"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
tracerProviderHolder struct {
|
||||||
|
tp trace.TracerProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
propagatorsHolder struct {
|
||||||
|
tm propagation.TextMapPropagator
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
globalTracer = defaultTracerValue()
|
||||||
|
globalPropagators = defaultPropagatorsValue()
|
||||||
|
|
||||||
|
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})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 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()
|
||||||
|
globalPropagators = defaultPropagatorsValue()
|
||||||
|
delegateTraceOnce = sync.Once{}
|
||||||
|
delegateTextMapPropagatorOnce = sync.Once{}
|
||||||
|
}
|
||||||
192
vendor/go.opentelemetry.io/otel/internal/global/trace.go
generated
vendored
Normal file
192
vendor/go.opentelemetry.io/otel/internal/global/trace.go
generated
vendored
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
// 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 "go.opentelemetry.io/otel/internal/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"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/codes"
|
||||||
|
"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 map[il]*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.
|
||||||
|
//
|
||||||
|
// It is guaranteed by the caller that this happens only once.
|
||||||
|
func (p *tracerProvider) setDelegate(provider trace.TracerProvider) {
|
||||||
|
p.mtx.Lock()
|
||||||
|
defer p.mtx.Unlock()
|
||||||
|
|
||||||
|
p.delegate = provider
|
||||||
|
|
||||||
|
if len(p.tracers) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
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...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this moment it is guaranteed that no sdk is installed, save the tracer in the tracers map.
|
||||||
|
|
||||||
|
c := trace.NewTracerConfig(opts...)
|
||||||
|
key := il{
|
||||||
|
name: name,
|
||||||
|
version: c.InstrumentationVersion(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.tracers == nil {
|
||||||
|
p.tracers = make(map[il]*tracer)
|
||||||
|
}
|
||||||
|
|
||||||
|
if val, ok := p.tracers[key]; ok {
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
t := &tracer{name: name, opts: opts, provider: p}
|
||||||
|
p.tracers[key] = t
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
type il struct {
|
||||||
|
name string
|
||||||
|
version string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
name string
|
||||||
|
opts []trace.TracerOption
|
||||||
|
provider *tracerProvider
|
||||||
|
|
||||||
|
delegate atomic.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
// It is guaranteed by the caller that this happens only once.
|
||||||
|
func (t *tracer) setDelegate(provider trace.TracerProvider) {
|
||||||
|
t.delegate.Store(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.SpanStartOption) (context.Context, trace.Span) {
|
||||||
|
delegate := t.delegate.Load()
|
||||||
|
if delegate != nil {
|
||||||
|
return delegate.(trace.Tracer).Start(ctx, name, opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
s := nonRecordingSpan{sc: trace.SpanContextFromContext(ctx), tracer: t}
|
||||||
|
ctx = trace.ContextWithSpan(ctx, s)
|
||||||
|
return ctx, s
|
||||||
|
}
|
||||||
|
|
||||||
|
// nonRecordingSpan is a minimal implementation of a Span that wraps a
|
||||||
|
// SpanContext. It performs no operations other than to return the wrapped
|
||||||
|
// SpanContext.
|
||||||
|
type nonRecordingSpan struct {
|
||||||
|
sc trace.SpanContext
|
||||||
|
tracer *tracer
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ trace.Span = nonRecordingSpan{}
|
||||||
|
|
||||||
|
// SpanContext returns the wrapped SpanContext.
|
||||||
|
func (s nonRecordingSpan) SpanContext() trace.SpanContext { return s.sc }
|
||||||
|
|
||||||
|
// IsRecording always returns false.
|
||||||
|
func (nonRecordingSpan) IsRecording() bool { return false }
|
||||||
|
|
||||||
|
// SetStatus does nothing.
|
||||||
|
func (nonRecordingSpan) SetStatus(codes.Code, string) {}
|
||||||
|
|
||||||
|
// SetError does nothing.
|
||||||
|
func (nonRecordingSpan) SetError(bool) {}
|
||||||
|
|
||||||
|
// SetAttributes does nothing.
|
||||||
|
func (nonRecordingSpan) SetAttributes(...attribute.KeyValue) {}
|
||||||
|
|
||||||
|
// End does nothing.
|
||||||
|
func (nonRecordingSpan) End(...trace.SpanEndOption) {}
|
||||||
|
|
||||||
|
// RecordError does nothing.
|
||||||
|
func (nonRecordingSpan) RecordError(error, ...trace.EventOption) {}
|
||||||
|
|
||||||
|
// AddEvent does nothing.
|
||||||
|
func (nonRecordingSpan) AddEvent(string, ...trace.EventOption) {}
|
||||||
|
|
||||||
|
// SetName does nothing.
|
||||||
|
func (nonRecordingSpan) SetName(string) {}
|
||||||
|
|
||||||
|
func (s nonRecordingSpan) TracerProvider() trace.TracerProvider { return s.tracer.provider }
|
||||||
201
vendor/go.opentelemetry.io/otel/internal/metric/LICENSE
generated
vendored
Normal file
201
vendor/go.opentelemetry.io/otel/internal/metric/LICENSE
generated
vendored
Normal file
@@ -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.
|
||||||
149
vendor/go.opentelemetry.io/otel/internal/metric/async.go
generated
vendored
Normal file
149
vendor/go.opentelemetry.io/otel/internal/metric/async.go
generated
vendored
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
// 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 metric // import "go.opentelemetry.io/otel/internal/metric"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/metric/sdkapi"
|
||||||
|
)
|
||||||
|
|
||||||
|
//nolint:revive // ignoring missing comments for exported error in an internal package
|
||||||
|
var ErrInvalidAsyncRunner = errors.New("unknown async runner type")
|
||||||
|
|
||||||
|
// AsyncCollector is an interface used between the MeterImpl and the
|
||||||
|
// AsyncInstrumentState helper below. This interface is implemented by
|
||||||
|
// the SDK to provide support for running observer callbacks.
|
||||||
|
type AsyncCollector interface {
|
||||||
|
// CollectAsync passes a batch of observations to the MeterImpl.
|
||||||
|
CollectAsync(labels []attribute.KeyValue, observation ...sdkapi.Observation)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsyncInstrumentState manages an ordered set of asynchronous
|
||||||
|
// instruments and the distinct runners, taking into account batch
|
||||||
|
// observer callbacks.
|
||||||
|
type AsyncInstrumentState struct {
|
||||||
|
lock sync.Mutex
|
||||||
|
|
||||||
|
// errorOnce will use the otel.Handler to report an error
|
||||||
|
// once in case of an invalid runner attempting to run.
|
||||||
|
errorOnce sync.Once
|
||||||
|
|
||||||
|
// runnerMap keeps the set of runners that will run each
|
||||||
|
// collection interval. Singletons are entered with a real
|
||||||
|
// instrument each, batch observers are entered with a nil
|
||||||
|
// instrument, ensuring that when a singleton callback is used
|
||||||
|
// repeatedly, it is executed repeatedly in the interval, while
|
||||||
|
// when a batch callback is used repeatedly, it only executes
|
||||||
|
// once per interval.
|
||||||
|
runnerMap map[asyncRunnerPair]struct{}
|
||||||
|
|
||||||
|
// runners maintains the set of runners in the order they were
|
||||||
|
// registered.
|
||||||
|
runners []asyncRunnerPair
|
||||||
|
|
||||||
|
// instruments maintains the set of instruments in the order
|
||||||
|
// they were registered.
|
||||||
|
instruments []sdkapi.AsyncImpl
|
||||||
|
}
|
||||||
|
|
||||||
|
// asyncRunnerPair is a map entry for Observer callback runners.
|
||||||
|
type asyncRunnerPair struct {
|
||||||
|
// runner is used as a map key here. The API ensures
|
||||||
|
// that all callbacks are pointers for this reason.
|
||||||
|
runner sdkapi.AsyncRunner
|
||||||
|
|
||||||
|
// inst refers to a non-nil instrument when `runner` is a
|
||||||
|
// AsyncSingleRunner.
|
||||||
|
inst sdkapi.AsyncImpl
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAsyncInstrumentState returns a new *AsyncInstrumentState, for
|
||||||
|
// use by MeterImpl to manage running the set of observer callbacks in
|
||||||
|
// the correct order.
|
||||||
|
func NewAsyncInstrumentState() *AsyncInstrumentState {
|
||||||
|
return &AsyncInstrumentState{
|
||||||
|
runnerMap: map[asyncRunnerPair]struct{}{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Instruments returns the asynchronous instruments managed by this
|
||||||
|
// object, the set that should be checkpointed after observers are
|
||||||
|
// run.
|
||||||
|
func (a *AsyncInstrumentState) Instruments() []sdkapi.AsyncImpl {
|
||||||
|
a.lock.Lock()
|
||||||
|
defer a.lock.Unlock()
|
||||||
|
return a.instruments
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register adds a new asynchronous instrument to by managed by this
|
||||||
|
// object. This should be called during NewAsyncInstrument() and
|
||||||
|
// assumes that errors (e.g., duplicate registration) have already
|
||||||
|
// been checked.
|
||||||
|
func (a *AsyncInstrumentState) Register(inst sdkapi.AsyncImpl, runner sdkapi.AsyncRunner) {
|
||||||
|
a.lock.Lock()
|
||||||
|
defer a.lock.Unlock()
|
||||||
|
|
||||||
|
a.instruments = append(a.instruments, inst)
|
||||||
|
|
||||||
|
// asyncRunnerPair reflects this callback in the asyncRunners
|
||||||
|
// list. If this is a batch runner, the instrument is nil.
|
||||||
|
// If this is a single-Observer runner, the instrument is
|
||||||
|
// included. This ensures that batch callbacks are called
|
||||||
|
// once and single callbacks are called once per instrument.
|
||||||
|
rp := asyncRunnerPair{
|
||||||
|
runner: runner,
|
||||||
|
}
|
||||||
|
if _, ok := runner.(sdkapi.AsyncSingleRunner); ok {
|
||||||
|
rp.inst = inst
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := a.runnerMap[rp]; !ok {
|
||||||
|
a.runnerMap[rp] = struct{}{}
|
||||||
|
a.runners = append(a.runners, rp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run executes the complete set of observer callbacks.
|
||||||
|
func (a *AsyncInstrumentState) Run(ctx context.Context, collector AsyncCollector) {
|
||||||
|
a.lock.Lock()
|
||||||
|
runners := a.runners
|
||||||
|
a.lock.Unlock()
|
||||||
|
|
||||||
|
for _, rp := range runners {
|
||||||
|
// The runner must be a single or batch runner, no
|
||||||
|
// other implementations are possible because the
|
||||||
|
// interface has un-exported methods.
|
||||||
|
|
||||||
|
if singleRunner, ok := rp.runner.(sdkapi.AsyncSingleRunner); ok {
|
||||||
|
singleRunner.Run(ctx, rp.inst, collector.CollectAsync)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if multiRunner, ok := rp.runner.(sdkapi.AsyncBatchRunner); ok {
|
||||||
|
multiRunner.Run(ctx, collector.CollectAsync)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
a.errorOnce.Do(func() {
|
||||||
|
otel.Handle(fmt.Errorf("%w: type %T (reported once)", ErrInvalidAsyncRunner, rp))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
299
vendor/go.opentelemetry.io/otel/internal/metric/global/meter.go
generated
vendored
Normal file
299
vendor/go.opentelemetry.io/otel/internal/metric/global/meter.go
generated
vendored
Normal file
@@ -0,0 +1,299 @@
|
|||||||
|
// 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 "go.opentelemetry.io/otel/internal/metric/global"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/internal/metric/registry"
|
||||||
|
"go.opentelemetry.io/otel/metric"
|
||||||
|
"go.opentelemetry.io/otel/metric/number"
|
||||||
|
"go.opentelemetry.io/otel/metric/sdkapi"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
// Metric uniqueness checking is implemented by calling the exported
|
||||||
|
// methods of the api/metric/registry package.
|
||||||
|
|
||||||
|
type meterKey struct {
|
||||||
|
InstrumentationName string
|
||||||
|
InstrumentationVersion string
|
||||||
|
SchemaURL 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 sdkapi.MeterImpl
|
||||||
|
impl meterImpl
|
||||||
|
}
|
||||||
|
|
||||||
|
type instrument struct {
|
||||||
|
descriptor sdkapi.Descriptor
|
||||||
|
}
|
||||||
|
|
||||||
|
type syncImpl struct {
|
||||||
|
delegate unsafe.Pointer // (*sdkapi.SyncImpl)
|
||||||
|
|
||||||
|
instrument
|
||||||
|
}
|
||||||
|
|
||||||
|
type asyncImpl struct {
|
||||||
|
delegate unsafe.Pointer // (*sdkapi.AsyncImpl)
|
||||||
|
|
||||||
|
instrument
|
||||||
|
|
||||||
|
runner sdkapi.AsyncRunner
|
||||||
|
}
|
||||||
|
|
||||||
|
// SyncImpler is implemented by all of the sync metric
|
||||||
|
// instruments.
|
||||||
|
type SyncImpler interface {
|
||||||
|
SyncImpl() sdkapi.SyncImpl
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsyncImpler is implemented by all of the async
|
||||||
|
// metric instruments.
|
||||||
|
type AsyncImpler interface {
|
||||||
|
AsyncImpl() sdkapi.AsyncImpl
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ metric.MeterProvider = &meterProvider{}
|
||||||
|
var _ sdkapi.MeterImpl = &meterImpl{}
|
||||||
|
var _ sdkapi.InstrumentImpl = &syncImpl{}
|
||||||
|
var _ sdkapi.AsyncImpl = &asyncImpl{}
|
||||||
|
|
||||||
|
func (inst *instrument) Descriptor() sdkapi.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, 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...)
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := metric.NewMeterConfig(opts...)
|
||||||
|
key := meterKey{
|
||||||
|
InstrumentationName: instrumentationName,
|
||||||
|
InstrumentationVersion: cfg.InstrumentationVersion(),
|
||||||
|
SchemaURL: cfg.SchemaURL(),
|
||||||
|
}
|
||||||
|
entry, ok := p.meters[key]
|
||||||
|
if !ok {
|
||||||
|
entry = &meterEntry{}
|
||||||
|
// Note: This code implements its own MeterProvider
|
||||||
|
// name-uniqueness logic because there is
|
||||||
|
// synchronization required at the moment of
|
||||||
|
// delegation. We use the same instrument-uniqueness
|
||||||
|
// checking the real SDK uses here:
|
||||||
|
entry.unique = registry.NewUniqueInstrumentMeterImpl(&entry.impl)
|
||||||
|
p.meters[key] = entry
|
||||||
|
}
|
||||||
|
return metric.WrapMeterImpl(entry.unique)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Meter interface and delegation
|
||||||
|
|
||||||
|
func (m *meterImpl) setDelegate(key meterKey, provider metric.MeterProvider) {
|
||||||
|
m.lock.Lock()
|
||||||
|
defer m.lock.Unlock()
|
||||||
|
|
||||||
|
d := new(sdkapi.MeterImpl)
|
||||||
|
*d = provider.Meter(
|
||||||
|
key.InstrumentationName,
|
||||||
|
metric.WithInstrumentationVersion(key.InstrumentationVersion),
|
||||||
|
metric.WithSchemaURL(key.SchemaURL),
|
||||||
|
).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 sdkapi.Descriptor) (sdkapi.SyncImpl, error) {
|
||||||
|
m.lock.Lock()
|
||||||
|
defer m.lock.Unlock()
|
||||||
|
|
||||||
|
if meterPtr := (*sdkapi.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 sdkapi.MeterImpl) {
|
||||||
|
implPtr := new(sdkapi.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 := (*sdkapi.SyncImpl)(atomic.LoadPointer(&inst.delegate)); implPtr != nil {
|
||||||
|
return (*implPtr).Implementation()
|
||||||
|
}
|
||||||
|
return inst
|
||||||
|
}
|
||||||
|
|
||||||
|
// Async delegation
|
||||||
|
|
||||||
|
func (m *meterImpl) NewAsyncInstrument(
|
||||||
|
desc sdkapi.Descriptor,
|
||||||
|
runner sdkapi.AsyncRunner,
|
||||||
|
) (sdkapi.AsyncImpl, error) {
|
||||||
|
|
||||||
|
m.lock.Lock()
|
||||||
|
defer m.lock.Unlock()
|
||||||
|
|
||||||
|
if meterPtr := (*sdkapi.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 := (*sdkapi.AsyncImpl)(atomic.LoadPointer(&obs.delegate)); implPtr != nil {
|
||||||
|
return (*implPtr).Implementation()
|
||||||
|
}
|
||||||
|
return obs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (obs *asyncImpl) setDelegate(d sdkapi.MeterImpl) {
|
||||||
|
implPtr := new(sdkapi.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 []attribute.KeyValue, measurements ...sdkapi.Measurement) {
|
||||||
|
if delegatePtr := (*sdkapi.MeterImpl)(atomic.LoadPointer(&m.delegate)); delegatePtr != nil {
|
||||||
|
(*delegatePtr).RecordBatch(ctx, labels, measurements...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (inst *syncImpl) RecordOne(ctx context.Context, number number.Number, labels []attribute.KeyValue) {
|
||||||
|
if instPtr := (*sdkapi.SyncImpl)(atomic.LoadPointer(&inst.delegate)); instPtr != nil {
|
||||||
|
(*instPtr).RecordOne(ctx, number, labels)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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),
|
||||||
|
}
|
||||||
|
}
|
||||||
66
vendor/go.opentelemetry.io/otel/internal/metric/global/metric.go
generated
vendored
Normal file
66
vendor/go.opentelemetry.io/otel/internal/metric/global/metric.go
generated
vendored
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
// 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 "go.opentelemetry.io/otel/internal/metric/global"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/metric"
|
||||||
|
)
|
||||||
|
|
||||||
|
type meterProviderHolder struct {
|
||||||
|
mp metric.MeterProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
globalMeter = defaultMeterValue()
|
||||||
|
|
||||||
|
delegateMeterOnce sync.Once
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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})
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultMeterValue() *atomic.Value {
|
||||||
|
v := &atomic.Value{}
|
||||||
|
v.Store(meterProviderHolder{mp: newMeterProvider()})
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResetForTest restores the initial global state, for testing purposes.
|
||||||
|
func ResetForTest() {
|
||||||
|
globalMeter = defaultMeterValue()
|
||||||
|
delegateMeterOnce = sync.Once{}
|
||||||
|
}
|
||||||
24
vendor/go.opentelemetry.io/otel/internal/metric/registry/doc.go
generated
vendored
Normal file
24
vendor/go.opentelemetry.io/otel/internal/metric/registry/doc.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
// 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 registry provides a non-standalone implementation of
|
||||||
|
MeterProvider that adds uniqueness checking for instrument descriptors
|
||||||
|
on top of other MeterProvider it wraps.
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
package registry // import "go.opentelemetry.io/otel/internal/metric/registry"
|
||||||
139
vendor/go.opentelemetry.io/otel/internal/metric/registry/registry.go
generated
vendored
Normal file
139
vendor/go.opentelemetry.io/otel/internal/metric/registry/registry.go
generated
vendored
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
// 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 registry // import "go.opentelemetry.io/otel/internal/metric/registry"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/metric/sdkapi"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UniqueInstrumentMeterImpl implements the metric.MeterImpl interface, adding
|
||||||
|
// uniqueness checking for instrument descriptors.
|
||||||
|
type UniqueInstrumentMeterImpl struct {
|
||||||
|
lock sync.Mutex
|
||||||
|
impl sdkapi.MeterImpl
|
||||||
|
state map[string]sdkapi.InstrumentImpl
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ sdkapi.MeterImpl = (*UniqueInstrumentMeterImpl)(nil)
|
||||||
|
|
||||||
|
// ErrMetricKindMismatch is the standard error for mismatched metric
|
||||||
|
// instrument definitions.
|
||||||
|
var ErrMetricKindMismatch = fmt.Errorf(
|
||||||
|
"a metric was already registered by this name with another kind or number type")
|
||||||
|
|
||||||
|
// NewUniqueInstrumentMeterImpl returns a wrapped metric.MeterImpl
|
||||||
|
// with the addition of instrument name uniqueness checking.
|
||||||
|
func NewUniqueInstrumentMeterImpl(impl sdkapi.MeterImpl) *UniqueInstrumentMeterImpl {
|
||||||
|
return &UniqueInstrumentMeterImpl{
|
||||||
|
impl: impl,
|
||||||
|
state: map[string]sdkapi.InstrumentImpl{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MeterImpl gives the caller access to the underlying MeterImpl
|
||||||
|
// used by this UniqueInstrumentMeterImpl.
|
||||||
|
func (u *UniqueInstrumentMeterImpl) MeterImpl() sdkapi.MeterImpl {
|
||||||
|
return u.impl
|
||||||
|
}
|
||||||
|
|
||||||
|
// RecordBatch implements sdkapi.MeterImpl.
|
||||||
|
func (u *UniqueInstrumentMeterImpl) RecordBatch(ctx context.Context, labels []attribute.KeyValue, ms ...sdkapi.Measurement) {
|
||||||
|
u.impl.RecordBatch(ctx, labels, ms...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMetricKindMismatchError formats an error that describes a
|
||||||
|
// mismatched metric instrument definition.
|
||||||
|
func NewMetricKindMismatchError(desc sdkapi.Descriptor) error {
|
||||||
|
return fmt.Errorf("metric %s registered as %s %s: %w",
|
||||||
|
desc.Name(),
|
||||||
|
desc.NumberKind(),
|
||||||
|
desc.InstrumentKind(),
|
||||||
|
ErrMetricKindMismatch)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compatible determines whether two sdkapi.Descriptors are considered
|
||||||
|
// the same for the purpose of uniqueness checking.
|
||||||
|
func Compatible(candidate, existing sdkapi.Descriptor) bool {
|
||||||
|
return candidate.InstrumentKind() == existing.InstrumentKind() &&
|
||||||
|
candidate.NumberKind() == existing.NumberKind()
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkUniqueness returns an ErrMetricKindMismatch error if there is
|
||||||
|
// a conflict between a descriptor that was already registered and the
|
||||||
|
// `descriptor` argument. If there is an existing compatible
|
||||||
|
// registration, this returns the already-registered instrument. If
|
||||||
|
// there is no conflict and no prior registration, returns (nil, nil).
|
||||||
|
func (u *UniqueInstrumentMeterImpl) checkUniqueness(descriptor sdkapi.Descriptor) (sdkapi.InstrumentImpl, error) {
|
||||||
|
impl, ok := u.state[descriptor.Name()]
|
||||||
|
if !ok {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !Compatible(descriptor, impl.Descriptor()) {
|
||||||
|
return nil, NewMetricKindMismatchError(impl.Descriptor())
|
||||||
|
}
|
||||||
|
|
||||||
|
return impl, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSyncInstrument implements sdkapi.MeterImpl.
|
||||||
|
func (u *UniqueInstrumentMeterImpl) NewSyncInstrument(descriptor sdkapi.Descriptor) (sdkapi.SyncImpl, error) {
|
||||||
|
u.lock.Lock()
|
||||||
|
defer u.lock.Unlock()
|
||||||
|
|
||||||
|
impl, err := u.checkUniqueness(descriptor)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if impl != nil {
|
||||||
|
return impl.(sdkapi.SyncImpl), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
syncInst, err := u.impl.NewSyncInstrument(descriptor)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
u.state[descriptor.Name()] = syncInst
|
||||||
|
return syncInst, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAsyncInstrument implements sdkapi.MeterImpl.
|
||||||
|
func (u *UniqueInstrumentMeterImpl) NewAsyncInstrument(
|
||||||
|
descriptor sdkapi.Descriptor,
|
||||||
|
runner sdkapi.AsyncRunner,
|
||||||
|
) (sdkapi.AsyncImpl, error) {
|
||||||
|
u.lock.Lock()
|
||||||
|
defer u.lock.Unlock()
|
||||||
|
|
||||||
|
impl, err := u.checkUniqueness(descriptor)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if impl != nil {
|
||||||
|
return impl.(sdkapi.AsyncImpl), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
asyncInst, err := u.impl.NewAsyncInstrument(descriptor, runner)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
u.state[descriptor.Name()] = asyncInst
|
||||||
|
return asyncInst, nil
|
||||||
|
}
|
||||||
55
vendor/go.opentelemetry.io/otel/internal/rawhelpers.go
generated
vendored
Normal file
55
vendor/go.opentelemetry.io/otel/internal/rawhelpers.go
generated
vendored
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
// 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 "go.opentelemetry.io/otel/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 Float64ToRaw(f float64) uint64 {
|
||||||
|
return math.Float64bits(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RawToFloat64(r uint64) float64 {
|
||||||
|
return math.Float64frombits(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RawPtrToFloat64Ptr(r *uint64) *float64 {
|
||||||
|
return (*float64)(unsafe.Pointer(r))
|
||||||
|
}
|
||||||
|
|
||||||
|
func RawPtrToInt64Ptr(r *uint64) *int64 {
|
||||||
|
return (*int64)(unsafe.Pointer(r))
|
||||||
|
}
|
||||||
26
vendor/go.opentelemetry.io/otel/internal_logging.go
generated
vendored
Normal file
26
vendor/go.opentelemetry.io/otel/internal_logging.go
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
// 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 (
|
||||||
|
"github.com/go-logr/logr"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/internal/global"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SetLogger configures the logger used internally to opentelemetry.
|
||||||
|
func SetLogger(logger logr.Logger) {
|
||||||
|
global.SetLogger(logger)
|
||||||
|
}
|
||||||
201
vendor/go.opentelemetry.io/otel/metric/LICENSE
generated
vendored
Normal file
201
vendor/go.opentelemetry.io/otel/metric/LICENSE
generated
vendored
Normal file
@@ -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.
|
||||||
124
vendor/go.opentelemetry.io/otel/metric/config.go
generated
vendored
Normal file
124
vendor/go.opentelemetry.io/otel/metric/config.go
generated
vendored
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
// 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 metric // import "go.opentelemetry.io/otel/metric"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go.opentelemetry.io/otel/metric/unit"
|
||||||
|
)
|
||||||
|
|
||||||
|
// InstrumentConfig contains options for metric instrument descriptors.
|
||||||
|
type InstrumentConfig struct {
|
||||||
|
description string
|
||||||
|
unit unit.Unit
|
||||||
|
}
|
||||||
|
|
||||||
|
// Description describes the instrument in human-readable terms.
|
||||||
|
func (cfg InstrumentConfig) Description() string {
|
||||||
|
return cfg.description
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unit describes the measurement unit for a instrument.
|
||||||
|
func (cfg InstrumentConfig) Unit() unit.Unit {
|
||||||
|
return cfg.unit
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstrumentOption is an interface for applying metric instrument options.
|
||||||
|
type InstrumentOption interface {
|
||||||
|
// ApplyMeter is used to set a InstrumentOption value of a
|
||||||
|
// InstrumentConfig.
|
||||||
|
applyInstrument(*InstrumentConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInstrumentConfig creates a new InstrumentConfig
|
||||||
|
// and applies all the given options.
|
||||||
|
func NewInstrumentConfig(opts ...InstrumentOption) InstrumentConfig {
|
||||||
|
var config InstrumentConfig
|
||||||
|
for _, o := range opts {
|
||||||
|
o.applyInstrument(&config)
|
||||||
|
}
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
type instrumentOptionFunc func(*InstrumentConfig)
|
||||||
|
|
||||||
|
func (fn instrumentOptionFunc) applyInstrument(cfg *InstrumentConfig) {
|
||||||
|
fn(cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithDescription applies provided description.
|
||||||
|
func WithDescription(desc string) InstrumentOption {
|
||||||
|
return instrumentOptionFunc(func(cfg *InstrumentConfig) {
|
||||||
|
cfg.description = desc
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithUnit applies provided unit.
|
||||||
|
func WithUnit(unit unit.Unit) InstrumentOption {
|
||||||
|
return instrumentOptionFunc(func(cfg *InstrumentConfig) {
|
||||||
|
cfg.unit = unit
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// MeterConfig contains options for Meters.
|
||||||
|
type MeterConfig struct {
|
||||||
|
instrumentationVersion string
|
||||||
|
schemaURL string
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstrumentationVersion is the version of the library providing instrumentation.
|
||||||
|
func (cfg MeterConfig) InstrumentationVersion() string {
|
||||||
|
return cfg.instrumentationVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
// SchemaURL is the schema_url of the library providing instrumentation.
|
||||||
|
func (cfg MeterConfig) SchemaURL() string {
|
||||||
|
return cfg.schemaURL
|
||||||
|
}
|
||||||
|
|
||||||
|
// MeterOption is an interface for applying Meter options.
|
||||||
|
type MeterOption interface {
|
||||||
|
// ApplyMeter is used to set a MeterOption value of a MeterConfig.
|
||||||
|
applyMeter(*MeterConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMeterConfig creates a new MeterConfig and applies
|
||||||
|
// all the given options.
|
||||||
|
func NewMeterConfig(opts ...MeterOption) MeterConfig {
|
||||||
|
var config MeterConfig
|
||||||
|
for _, o := range opts {
|
||||||
|
o.applyMeter(&config)
|
||||||
|
}
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
type meterOptionFunc func(*MeterConfig)
|
||||||
|
|
||||||
|
func (fn meterOptionFunc) applyMeter(cfg *MeterConfig) {
|
||||||
|
fn(cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithInstrumentationVersion sets the instrumentation version.
|
||||||
|
func WithInstrumentationVersion(version string) MeterOption {
|
||||||
|
return meterOptionFunc(func(config *MeterConfig) {
|
||||||
|
config.instrumentationVersion = version
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithSchemaURL sets the schema URL.
|
||||||
|
func WithSchemaURL(schemaURL string) MeterOption {
|
||||||
|
return meterOptionFunc(func(config *MeterConfig) {
|
||||||
|
config.schemaURL = schemaURL
|
||||||
|
})
|
||||||
|
}
|
||||||
67
vendor/go.opentelemetry.io/otel/metric/doc.go
generated
vendored
Normal file
67
vendor/go.opentelemetry.io/otel/metric/doc.go
generated
vendored
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
// 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 metric provides an implementation of the metrics part 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.
|
||||||
|
|
||||||
|
Measurements can be made about an operation being performed or the state of a
|
||||||
|
system in general. These measurements can be crucial to the reliable operation
|
||||||
|
of code and provide valuable insights about the inner workings of a system.
|
||||||
|
|
||||||
|
Measurements are made using instruments provided by this package. The type of
|
||||||
|
instrument used will depend on the type of measurement being made and of what
|
||||||
|
part of a system is being measured.
|
||||||
|
|
||||||
|
Instruments are categorized as Synchronous or Asynchronous and independently
|
||||||
|
as Adding or Grouping. Synchronous instruments are called by the user with a
|
||||||
|
Context. Asynchronous instruments are called by the SDK during collection.
|
||||||
|
Adding instruments are semantically intended for capturing a sum. Grouping
|
||||||
|
instruments are intended for capturing a distribution.
|
||||||
|
|
||||||
|
Adding instruments may be monotonic, in which case they are non-decreasing
|
||||||
|
and naturally define a rate.
|
||||||
|
|
||||||
|
The synchronous instrument names are:
|
||||||
|
|
||||||
|
Counter: adding, monotonic
|
||||||
|
UpDownCounter: adding
|
||||||
|
Histogram: grouping
|
||||||
|
|
||||||
|
and the asynchronous instruments are:
|
||||||
|
|
||||||
|
CounterObserver: adding, monotonic
|
||||||
|
UpDownCounterObserver: adding
|
||||||
|
GaugeObserver: grouping
|
||||||
|
|
||||||
|
All instruments are provided with support for either float64 or int64 input
|
||||||
|
values.
|
||||||
|
|
||||||
|
An instrument is created using a Meter. Additionally, a Meter is used to
|
||||||
|
record batches of synchronous measurements or asynchronous observations. A
|
||||||
|
Meter is obtained using a MeterProvider. A Meter, like a Tracer, is unique to
|
||||||
|
the instrumentation it instruments and must be named and versioned when
|
||||||
|
created with a MeterProvider with the name and version of the instrumentation
|
||||||
|
library.
|
||||||
|
|
||||||
|
Instrumentation should be designed to accept a MeterProvider from which it can
|
||||||
|
create its own unique Meter. Alternatively, the registered global
|
||||||
|
MeterProvider from the go.opentelemetry.io/otel package can be used as a
|
||||||
|
default.
|
||||||
|
*/
|
||||||
|
package metric // import "go.opentelemetry.io/otel/metric"
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user