Compare commits
15 Commits
0.0.1
...
f93f144288
| Author | SHA1 | Date | |
|---|---|---|---|
| f93f144288 | |||
| 56b9570287 | |||
| ba9e54d1fb | |||
| ce58df8a6d | |||
| c5c87da714 | |||
| 05a0af6b74 | |||
| 4c3954ec76 | |||
| 67d1a1d8e5 | |||
| bb99d3b7c9 | |||
| 85fe69dcaa | |||
| 6fd794f7d1 | |||
| 0001c3e36e | |||
| 61d8bd22c4 | |||
| 77a2aa351b | |||
| b7be75793b |
@@ -1,8 +1,10 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"github.com/cyrilix/robocar-base/cli"
|
||||
"github.com/cyrilix/robocar-steering-tflite-edgetpu/pkg/metrics"
|
||||
"github.com/cyrilix/robocar-steering-tflite-edgetpu/pkg/steering"
|
||||
"go.uber.org/zap"
|
||||
"log"
|
||||
@@ -18,12 +20,7 @@ func main() {
|
||||
var cameraTopic, steeringTopic string
|
||||
var modelPath string
|
||||
var edgeVerbosity int
|
||||
|
||||
lgr, err := zap.NewProductionConfig().Build()
|
||||
if err != nil {
|
||||
log.Fatalf("unable to init logger: %v", err)
|
||||
}
|
||||
zap.ReplaceGlobals(lgr)
|
||||
var imgWidth, imgHeight, horizon int
|
||||
|
||||
mqttQos := cli.InitIntFlag("MQTT_QOS", 0)
|
||||
_, mqttRetain := os.LookupEnv("MQTT_RETAIN")
|
||||
@@ -34,18 +31,43 @@ 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(&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(&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()
|
||||
|
||||
if len(os.Args) <= 1 {
|
||||
flag.PrintDefaults()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
config := zap.NewDevelopmentConfig()
|
||||
config.Level = zap.NewAtomicLevelAt(*logLevel)
|
||||
lgr, err := config.Build()
|
||||
if err != nil {
|
||||
log.Fatalf("unable to init logger: %v", err)
|
||||
}
|
||||
defer func() {
|
||||
if err := lgr.Sync(); err != nil {
|
||||
log.Printf("unable to Sync logger: %v\n", err)
|
||||
}
|
||||
}()
|
||||
zap.ReplaceGlobals(lgr)
|
||||
|
||||
cleanup := metrics.Init(context.Background())
|
||||
defer cleanup()
|
||||
|
||||
if modelPath == "" {
|
||||
zap.L().Error("model path is mandatory")
|
||||
flag.PrintDefaults()
|
||||
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)
|
||||
if err != nil {
|
||||
@@ -53,7 +75,7 @@ func main() {
|
||||
}
|
||||
defer client.Disconnect(50)
|
||||
|
||||
p := steering.NewPart(client, steeringTopic, cameraTopic, edgeVerbosity)
|
||||
p := steering.NewPart(client, modelPath, steeringTopic, cameraTopic, edgeVerbosity, imgWidth, imgHeight, horizon)
|
||||
defer p.Stop()
|
||||
|
||||
cli.HandleExit(p)
|
||||
|
||||
21
go.mod
21
go.mod
@@ -3,19 +3,32 @@ module github.com/cyrilix/robocar-steering-tflite-edgetpu
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/cyrilix/robocar-base v0.1.4
|
||||
github.com/cyrilix/robocar-protobuf/go v1.0.3
|
||||
github.com/cyrilix/robocar-base v0.1.6
|
||||
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/golang/protobuf v1.5.2
|
||||
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
|
||||
google.golang.org/protobuf v1.27.1
|
||||
)
|
||||
|
||||
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/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/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
|
||||
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/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/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.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/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
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/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.4/go.mod h1:Tt04UmbGBiQtU0Cn3wFD0q7XoyokTwIlWYQxThKI+04=
|
||||
github.com/cyrilix/robocar-protobuf/go v1.0.3 h1:iPHw2+7FVXG2C4+Th1m11hQ+2RpAQzlxKhc5M7XOa6Q=
|
||||
github.com/cyrilix/robocar-protobuf/go v1.0.3/go.mod h1:xb95cK07lYXnKcHZKnGafmAgYRrqZWZgV9LMiJAp+gE=
|
||||
github.com/cyrilix/robocar-base v0.1.6 h1:VVcSZD8DPsha3XDLxRBMvtcd6uC8CcIjqbxG482dxvo=
|
||||
github.com/cyrilix/robocar-base v0.1.6/go.mod h1:m5ov/7hpRHi0yMp2prKafL6UEsM2O71Uea85WR0/jjI=
|
||||
github.com/cyrilix/robocar-protobuf/go v1.0.4 h1:XTolFYbiKw4gQ2l+z/LMZkLrmAUMzlHcQBzp/czlANo=
|
||||
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.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
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/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=
|
||||
@@ -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-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
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/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||
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/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
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.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/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
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/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=
|
||||
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/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
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-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-20200430140353-33d19683fad8 h1:6WW6V3x1P/jokJBpRQYUJnMHRP6isStQwCozxnU7XQw=
|
||||
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-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-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-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/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
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/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 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
||||
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 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
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,24 +2,33 @@ package steering
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/cyrilix/robocar-base/service"
|
||||
"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"
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/mattn/go-tflite"
|
||||
"github.com/mattn/go-tflite/delegates/edgetpu"
|
||||
"go.uber.org/zap"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"image"
|
||||
"sort"
|
||||
_ "image/jpeg"
|
||||
"time"
|
||||
)
|
||||
|
||||
func NewPart(client mqtt.Client, steeringTopic, cameraTopic string, edgeVerbosity int) *Part {
|
||||
func NewPart(client mqtt.Client, modelPath, steeringTopic, cameraTopic string, edgeVerbosity int, imgWidth, imgHeight, horizon int) *Part {
|
||||
return &Part{
|
||||
client: client,
|
||||
modelPath: modelPath,
|
||||
steeringTopic: steeringTopic,
|
||||
cameraTopic: cameraTopic,
|
||||
edgeVebosity: edgeVerbosity,
|
||||
imgWidth: imgWidth,
|
||||
imgHeight: imgHeight,
|
||||
horizon: horizon,
|
||||
}
|
||||
|
||||
}
|
||||
@@ -36,9 +45,14 @@ type Part struct {
|
||||
modelPath string
|
||||
model *tflite.Model
|
||||
edgeVebosity int
|
||||
|
||||
imgWidth int
|
||||
imgHeight int
|
||||
horizon int
|
||||
}
|
||||
|
||||
func (p *Part) Start() error {
|
||||
p.cancel = make(chan interface{})
|
||||
p.model = tflite.NewModelFromFile(p.modelPath)
|
||||
if p.model == nil {
|
||||
return fmt.Errorf("cannot load model %v", p.modelPath)
|
||||
@@ -58,19 +72,27 @@ func (p *Part) Start() error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot get EdgeTPU version: %w", err)
|
||||
}
|
||||
zap.S().Infof("EdgeTPU Version: %s\n", edgetpuVersion)
|
||||
zap.S().Infof("EdgeTPU Version: %s", edgetpuVersion)
|
||||
edgetpu.Verbosity(p.edgeVebosity)
|
||||
|
||||
p.options = tflite.NewInterpreterOptions()
|
||||
//options.SetNumThread(4)
|
||||
p.options.SetNumThread(4)
|
||||
p.options.SetErrorReporter(func(msg string, userData interface{}) {
|
||||
zap.L().Error(msg,
|
||||
zap.Any("userData", userData),
|
||||
zap.S().Errorw(msg,
|
||||
"userData", userData,
|
||||
)
|
||||
}, nil)
|
||||
|
||||
zap.S().Infof("find %d edgetpu devices", len(devices))
|
||||
zap.S().Infow("configure edgetpu",
|
||||
"path", devices[0].Path,
|
||||
"type", uint32(devices[0].Type),
|
||||
)
|
||||
// Add the first EdgeTPU device
|
||||
d := edgetpu.New(devices[0])
|
||||
if d == nil {
|
||||
return fmt.Errorf("unable to create new EdgeTpu delegate")
|
||||
}
|
||||
p.options.AddDelegate(d)
|
||||
|
||||
p.interpreter = tflite.NewInterpreter(p.model, p.options)
|
||||
@@ -79,7 +101,7 @@ func (p *Part) Start() error {
|
||||
}
|
||||
|
||||
if err := registerCallbacks(p); err != nil {
|
||||
zap.S().Errorf("unable to register callbacks: %v", err)
|
||||
zap.S().Errorw("unable to register callbacks", "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -111,12 +133,31 @@ func (p *Part) onFrame(_ mqtt.Client, message mqtt.Message) {
|
||||
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()))
|
||||
if err != nil {
|
||||
zap.L().Error("unable to decode frame", zap.Error(err))
|
||||
zap.L().Error("unable to decode frame, skip frame", zap.Error(err))
|
||||
return
|
||||
}
|
||||
|
||||
steering, confidence, err := p.Value(img)
|
||||
inferenceDuration := time.Now().UnixMilli() - now
|
||||
go metrics.InferenceDuration.Record(context.Background(), inferenceDuration)
|
||||
|
||||
if err != nil {
|
||||
zap.S().Errorw("unable to compute sterring",
|
||||
"frame", msg.GetId().GetId(),
|
||||
"error", err,
|
||||
)
|
||||
return
|
||||
}
|
||||
zap.L().Debug("new steering value",
|
||||
zap.Float32("steering", steering),
|
||||
zap.Float32("confidence", confidence),
|
||||
)
|
||||
msgSteering := &events.SteeringMessage{
|
||||
Steering: steering,
|
||||
Confidence: confidence,
|
||||
@@ -141,13 +182,23 @@ func (p *Part) Value(img image.Image) (float32, float32, error) {
|
||||
dx := img.Bounds().Dx()
|
||||
dy := img.Bounds().Dy()
|
||||
|
||||
bb := make([]byte, dx*dy*3)
|
||||
for y := 0; y < 128; y++ {
|
||||
for x := 0; x < 160; x++ {
|
||||
if dx != p.imgWidth || dy != p.imgHeight {
|
||||
img = imaging.Resize(img, p.imgWidth, p.imgHeight, imaging.NearestNeighbor)
|
||||
}
|
||||
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()
|
||||
bb[(y*dx+x)*3+0] = byte(float64(r) / 255.0)
|
||||
bb[(y*dx+x)*3+1] = byte(float64(g) / 255.0)
|
||||
bb[(y*dx+x)*3+2] = byte(float64(b) / 255.0)
|
||||
bb[(y*dx+x)*3+0] = uint8(float64(r) / 257.0)
|
||||
bb[(y*dx+x)*3+1] = uint8(float64(g) / 257.0)
|
||||
bb[(y*dx+x)*3+2] = uint8(float64(b) / 257.0)
|
||||
}
|
||||
}
|
||||
status = input.CopyFromBuffer(bb)
|
||||
@@ -160,48 +211,21 @@ func (p *Part) Value(img image.Image) (float32, float32, error) {
|
||||
return 0., 0., fmt.Errorf("invoke failed: %v", status)
|
||||
}
|
||||
|
||||
output := p.interpreter.GetOutputTensor(0)
|
||||
outputSize := output.Dim(output.NumDims() - 1)
|
||||
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)
|
||||
}
|
||||
output := p.interpreter.GetOutputTensor(0).UInt8s()
|
||||
zap.L().Debug("raw steering", zap.Uint8s("result", output))
|
||||
|
||||
var results []result
|
||||
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
|
||||
steering, score := tools.LinearBin(output, 15, -1, 2.0)
|
||||
zap.L().Debug("found 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 {
|
||||
err := service.RegisterCallback(p.client, p.cameraTopic, p.onFrame)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("unable to register callback: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
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"
|
||||
"github.com/cyrilix/robocar-base/service"
|
||||
MQTT "github.com/eclipse/paho.mqtt.golang"
|
||||
"log"
|
||||
"go.uber.org/zap"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strconv"
|
||||
@@ -25,7 +25,7 @@ func SetIntDefaultValueFromEnv(value *int, key string, defaultValue int) error {
|
||||
sVal = os.Getenv(key)
|
||||
val, err := strconv.Atoi(sVal)
|
||||
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
|
||||
}
|
||||
*value = val
|
||||
@@ -40,7 +40,7 @@ func SetFloat64DefaultValueFromEnv(value *float64, key string, defaultValue floa
|
||||
sVal = os.Getenv(key)
|
||||
val, err := strconv.ParseFloat(sVal, 64)
|
||||
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
|
||||
}
|
||||
*value = val
|
||||
@@ -81,7 +81,7 @@ func InitIntFlag(key string, defValue int) int {
|
||||
var value int
|
||||
err := SetIntDefaultValueFromEnv(&value, key, defValue)
|
||||
if err != nil {
|
||||
log.Panicf("invalid int value: %v", err)
|
||||
zap.S().Panicf("invalid int value: %v", err)
|
||||
}
|
||||
return value
|
||||
}
|
||||
@@ -90,7 +90,7 @@ func InitFloat64Flag(key string, defValue float64) float64 {
|
||||
var value float64
|
||||
err := SetFloat64DefaultValueFromEnv(&value, key, defValue)
|
||||
if err != nil {
|
||||
log.Panicf("invalid value: %v", err)
|
||||
zap.S().Panicf("invalid value: %v", err)
|
||||
}
|
||||
return value
|
||||
}
|
||||
@@ -106,8 +106,8 @@ func Connect(uri, username, password, clientId string) (MQTT.Client, error) {
|
||||
opts.SetDefaultPublishHandler(
|
||||
//define a function for the default message handler
|
||||
func(client MQTT.Client, msg MQTT.Message) {
|
||||
fmt.Printf("TOPIC: %s\n", msg.Topic())
|
||||
fmt.Printf("MSG: %s\n", msg.Payload())
|
||||
zap.S().Infof("TOPIC: %s", msg.Topic())
|
||||
zap.S().Infof("MSG: %s", msg.Payload())
|
||||
})
|
||||
|
||||
//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 (
|
||||
"fmt"
|
||||
mqtt "github.com/eclipse/paho.mqtt.golang"
|
||||
"log"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
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.Wait()
|
||||
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)
|
||||
}
|
||||
|
||||
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.Wait()
|
||||
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.
|
||||
// versions:
|
||||
// protoc-gen-go v1.26.0
|
||||
// protoc-gen-go v1.27.1
|
||||
// protoc v3.12.4
|
||||
// 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