Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
8f528f27f3 | |||
0247f58668 | |||
d9d6bf6cd6 | |||
3cd3d89b60 | |||
2bdc945ec5 | |||
8234a0cefa | |||
f1566b8e4a |
3
.gitignore
vendored
3
.gitignore
vendored
@ -284,3 +284,6 @@ local.properties
|
|||||||
|
|
||||||
./rc-*
|
./rc-*
|
||||||
rc-simulator
|
rc-simulator
|
||||||
|
rc-simulator.*
|
||||||
|
|
||||||
|
build/
|
||||||
|
50
build-docker.sh
Executable file
50
build-docker.sh
Executable file
@ -0,0 +1,50 @@
|
|||||||
|
#! /bin/bash
|
||||||
|
|
||||||
|
IMAGE_NAME=robocar-simulator
|
||||||
|
TAG=$(git describe)
|
||||||
|
FULL_IMAGE_NAME=docker.io/cyrilix/${IMAGE_NAME}:${TAG}
|
||||||
|
BINARY=rc-simulator
|
||||||
|
|
||||||
|
GOTAGS="-tags netgo"
|
||||||
|
|
||||||
|
image_build(){
|
||||||
|
local platform=$1
|
||||||
|
|
||||||
|
|
||||||
|
GOOS=$(echo $platform | cut -f1 -d/) && \
|
||||||
|
GOARCH=$(echo $platform | cut -f2 -d/) && \
|
||||||
|
GOARM=$(echo $platform | cut -f3 -d/ | sed "s/v//" )
|
||||||
|
VARIANT="--variant $(echo $platform | cut -f3 -d/ )"
|
||||||
|
if [[ -z "$GOARM" ]] ;
|
||||||
|
then
|
||||||
|
VARIANT=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
local binary_suffix="$GOARCH$(echo $platform | cut -f3 -d/ )"
|
||||||
|
|
||||||
|
local containerName="$IMAGE_NAME-$GOARCH$GOARM"
|
||||||
|
|
||||||
|
|
||||||
|
printf "\n\nBuild go binary %s\n\n" "${BINARY}.${binary_suffix}"
|
||||||
|
mkdir -p build
|
||||||
|
CGO_ENABLED=0 GOOS=${GOOS} GOARCH=${GOARCH} GOARM=${GOARM} go build -mod vendor -a ${GOTAGS} -o "build/${BINARY}.${binary_suffix}" ./cmd/${BINARY}/
|
||||||
|
|
||||||
|
buildah --os "$GOOS" --arch "$GOARCH" $VARIANT --name "$containerName" from gcr.io/distroless/static
|
||||||
|
buildah config --user 1234 "$containerName"
|
||||||
|
buildah copy "$containerName" "build/${BINARY}.${binary_suffix}" /go/bin/$BINARY
|
||||||
|
buildah config --entrypoint '["/go/bin/'$BINARY'"]' "${containerName}"
|
||||||
|
|
||||||
|
buildah commit --rm --manifest $IMAGE_NAME "${containerName}" "${containerName}"
|
||||||
|
}
|
||||||
|
|
||||||
|
buildah rmi localhost/$IMAGE_NAME
|
||||||
|
buildah manifest rm localhost/${IMAGE_NAME}
|
||||||
|
|
||||||
|
image_build linux/amd64
|
||||||
|
image_build linux/arm64
|
||||||
|
image_build linux/arm/v7
|
||||||
|
|
||||||
|
|
||||||
|
# push image
|
||||||
|
printf "\n\nPush manifest to %s\n\n" ${FULL_IMAGE_NAME}
|
||||||
|
buildah manifest push --rm -f v2s2 "localhost/$IMAGE_NAME" "docker://$FULL_IMAGE_NAME" --all
|
@ -10,7 +10,8 @@ import (
|
|||||||
"github.com/cyrilix/robocar-simulator/pkg/simulator"
|
"github.com/cyrilix/robocar-simulator/pkg/simulator"
|
||||||
mqtt "github.com/eclipse/paho.mqtt.golang"
|
mqtt "github.com/eclipse/paho.mqtt.golang"
|
||||||
"github.com/golang/protobuf/proto"
|
"github.com/golang/protobuf/proto"
|
||||||
log "github.com/sirupsen/logrus"
|
"go.uber.org/zap"
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -22,7 +23,6 @@ func main() {
|
|||||||
var mqttBroker, username, password, clientId, topicFrame, topicSteering, topicThrottle string
|
var mqttBroker, username, password, clientId, topicFrame, topicSteering, topicThrottle string
|
||||||
var topicCtrlSteering, topicCtrlThrottle string
|
var topicCtrlSteering, topicCtrlThrottle string
|
||||||
var address string
|
var address string
|
||||||
var debug bool
|
|
||||||
|
|
||||||
mqttQos := cli.InitIntFlag("MQTT_QOS", 0)
|
mqttQos := cli.InitIntFlag("MQTT_QOS", 0)
|
||||||
_, mqttRetain := os.LookupEnv("MQTT_RETAIN")
|
_, mqttRetain := os.LookupEnv("MQTT_RETAIN")
|
||||||
@ -35,7 +35,6 @@ func main() {
|
|||||||
flag.StringVar(&topicCtrlSteering, "topic-steering-ctrl", os.Getenv("MQTT_TOPIC_STEERING_CTRL"), "Mqtt topic to send steering instructions, use MQTT_TOPIC_STEERING_CTRL if args not set")
|
flag.StringVar(&topicCtrlSteering, "topic-steering-ctrl", os.Getenv("MQTT_TOPIC_STEERING_CTRL"), "Mqtt topic to send steering instructions, use MQTT_TOPIC_STEERING_CTRL if args not set")
|
||||||
flag.StringVar(&topicCtrlThrottle, "topic-throttle-ctrl", os.Getenv("MQTT_TOPIC_THROTTLE_CTRL"), "Mqtt topic to send throttle instructions, use MQTT_TOPIC_THROTTLE_CTRL if args not set")
|
flag.StringVar(&topicCtrlThrottle, "topic-throttle-ctrl", os.Getenv("MQTT_TOPIC_THROTTLE_CTRL"), "Mqtt topic to send throttle instructions, use MQTT_TOPIC_THROTTLE_CTRL if args not set")
|
||||||
flag.StringVar(&address, "simulator-address", "127.0.0.1:9091", "Simulator address")
|
flag.StringVar(&address, "simulator-address", "127.0.0.1:9091", "Simulator address")
|
||||||
flag.BoolVar(&debug, "debug", false, "Debug logs")
|
|
||||||
|
|
||||||
var carName, carStyle, carColor string
|
var carName, carStyle, carColor string
|
||||||
var carFontSize int
|
var carFontSize int
|
||||||
@ -57,7 +56,7 @@ func main() {
|
|||||||
|
|
||||||
var cameraFov, cameraImgW, cameraImgH, cameraImgD int
|
var cameraFov, cameraImgW, cameraImgH, cameraImgD int
|
||||||
var cameraFishEyeX, cameraFishEyeY float64
|
var cameraFishEyeX, cameraFishEyeY float64
|
||||||
var cameraOffsetX, cameraOffsetY, cameraOffsetZ, cameraRotX float64
|
var cameraOffsetX, cameraOffsetY, cameraOffsetZ, cameraRotX, cameraRotY, cameraRotZ float64
|
||||||
var cameraImgEnc string
|
var cameraImgEnc string
|
||||||
flag.IntVar(&cameraFov, "camera-fov", 90, "")
|
flag.IntVar(&cameraFov, "camera-fov", 90, "")
|
||||||
flag.Float64Var(&cameraFishEyeX, "camera-fish-eye-x", 0.4, "")
|
flag.Float64Var(&cameraFishEyeX, "camera-fish-eye-x", 0.4, "")
|
||||||
@ -69,20 +68,35 @@ func main() {
|
|||||||
flag.Float64Var(&cameraOffsetX, "camera-offset-x", 0, "moves camera left/right")
|
flag.Float64Var(&cameraOffsetX, "camera-offset-x", 0, "moves camera left/right")
|
||||||
flag.Float64Var(&cameraOffsetY, "camera-offset-y", 1.120395, "moves camera up/down")
|
flag.Float64Var(&cameraOffsetY, "camera-offset-y", 1.120395, "moves camera up/down")
|
||||||
flag.Float64Var(&cameraOffsetZ, "camera-offset-z", 0.5528488, "moves camera forward/back")
|
flag.Float64Var(&cameraOffsetZ, "camera-offset-z", 0.5528488, "moves camera forward/back")
|
||||||
flag.Float64Var(&cameraRotX, "camera-rot-x", 15.0, "rotate the camera")
|
flag.Float64Var(&cameraRotX, "camera-rot-x", 15.0, "rotate the camera around x-axis")
|
||||||
|
flag.Float64Var(&cameraRotY, "camera-rot-y", 0.0, "rotate the camera around y-axis")
|
||||||
|
flag.Float64Var(&cameraRotZ, "camera-rot-z", 0.0, "rotate the camera around z-axis")
|
||||||
|
|
||||||
|
logLevel := zap.LevelFlag("log", zap.InfoLevel, "log level")
|
||||||
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
if len(os.Args) <= 1 {
|
if len(os.Args) <= 1 {
|
||||||
flag.PrintDefaults()
|
flag.PrintDefaults()
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
if debug {
|
|
||||||
log.SetLevel(log.DebugLevel)
|
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)
|
||||||
|
|
||||||
client, err := cli.Connect(mqttBroker, username, password, clientId)
|
client, err := cli.Connect(mqttBroker, username, password, clientId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("unable to connect to events broker: %v", err)
|
zap.S().Fatalf("unable to connect to events broker: %v", err)
|
||||||
}
|
}
|
||||||
defer client.Disconnect(10)
|
defer client.Disconnect(10)
|
||||||
|
|
||||||
@ -108,17 +122,22 @@ func main() {
|
|||||||
MsgType: simulator.MsgTypeCameraConfig,
|
MsgType: simulator.MsgTypeCameraConfig,
|
||||||
Fov: strconv.Itoa(cameraFov),
|
Fov: strconv.Itoa(cameraFov),
|
||||||
FishEyeX: fmt.Sprintf("%.2f", cameraFishEyeX),
|
FishEyeX: fmt.Sprintf("%.2f", cameraFishEyeX),
|
||||||
FishEyeY: fmt.Sprintf("%.2f",cameraFishEyeY),
|
FishEyeY: fmt.Sprintf("%.2f", cameraFishEyeY),
|
||||||
ImgW: strconv.Itoa(cameraImgW),
|
ImgW: strconv.Itoa(cameraImgW),
|
||||||
ImgH: strconv.Itoa(cameraImgH),
|
ImgH: strconv.Itoa(cameraImgH),
|
||||||
ImgD: strconv.Itoa(cameraImgD),
|
ImgD: strconv.Itoa(cameraImgD),
|
||||||
ImgEnc: simulator.CameraImageEnc(cameraImgEnc),
|
ImgEnc: simulator.CameraImageEnc(cameraImgEnc),
|
||||||
OffsetX: fmt.Sprintf("%.2f",cameraOffsetX),
|
OffsetX: fmt.Sprintf("%.2f", cameraOffsetX),
|
||||||
OffsetY: fmt.Sprintf("%.2f", cameraOffsetY),
|
OffsetY: fmt.Sprintf("%.2f", cameraOffsetY),
|
||||||
OffsetZ: fmt.Sprintf("%.2f", cameraOffsetZ),
|
OffsetZ: fmt.Sprintf("%.2f", cameraOffsetZ),
|
||||||
RotX: fmt.Sprintf("%.2f", cameraRotX),
|
RotX: fmt.Sprintf("%.2f", cameraRotX),
|
||||||
|
RotY: fmt.Sprintf("%.2f", cameraRotY),
|
||||||
|
RotZ: fmt.Sprintf("%.2f", cameraRotZ),
|
||||||
|
}
|
||||||
|
gtw, err := gateway.New(address, &carConfig, &racer, &camera)
|
||||||
|
if err != nil {
|
||||||
|
zap.S().Fatalf("unable to init gateway: %v", err)
|
||||||
}
|
}
|
||||||
gtw := gateway.New(address, &carConfig, &racer, &camera)
|
|
||||||
defer gtw.Stop()
|
defer gtw.Stop()
|
||||||
|
|
||||||
msgPub := events.NewMsgPublisher(
|
msgPub := events.NewMsgPublisher(
|
||||||
@ -134,13 +153,13 @@ func main() {
|
|||||||
cli.HandleExit(gtw)
|
cli.HandleExit(gtw)
|
||||||
|
|
||||||
if topicCtrlSteering != "" {
|
if topicCtrlSteering != "" {
|
||||||
log.Infof("configure mqtt route on steering command")
|
zap.S().Info("configure mqtt route on steering command")
|
||||||
client.Subscribe(topicCtrlSteering, byte(mqttQos), func(client mqtt.Client, message mqtt.Message) {
|
client.Subscribe(topicCtrlSteering, byte(mqttQos), func(client mqtt.Client, message mqtt.Message) {
|
||||||
onSteeringCommand(gtw, message)
|
onSteeringCommand(gtw, message)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if topicCtrlThrottle != "" {
|
if topicCtrlThrottle != "" {
|
||||||
log.Infof("configure mqtt route on throttle command")
|
zap.S().Info("configure mqtt route on throttle command")
|
||||||
client.Subscribe(topicCtrlThrottle, byte(mqttQos), func(client mqtt.Client, message mqtt.Message) {
|
client.Subscribe(topicCtrlThrottle, byte(mqttQos), func(client mqtt.Client, message mqtt.Message) {
|
||||||
onThrottleCommand(gtw, message)
|
onThrottleCommand(gtw, message)
|
||||||
})
|
})
|
||||||
@ -148,7 +167,7 @@ func main() {
|
|||||||
|
|
||||||
err = gtw.Start()
|
err = gtw.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("unable to start service: %v", err)
|
zap.S().Fatalf("unable to start service: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -157,7 +176,7 @@ func onSteeringCommand(c *gateway.Gateway, message mqtt.Message) {
|
|||||||
var steeringMsg events2.SteeringMessage
|
var steeringMsg events2.SteeringMessage
|
||||||
err := proto.Unmarshal(message.Payload(), &steeringMsg)
|
err := proto.Unmarshal(message.Payload(), &steeringMsg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("unable to unmarshal steering msg: %v", err)
|
zap.S().Errorf("unable to unmarshal steering msg: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.WriteSteering(&steeringMsg)
|
c.WriteSteering(&steeringMsg)
|
||||||
@ -167,7 +186,7 @@ func onThrottleCommand(c *gateway.Gateway, message mqtt.Message) {
|
|||||||
var throttleMsg events2.ThrottleMessage
|
var throttleMsg events2.ThrottleMessage
|
||||||
err := proto.Unmarshal(message.Payload(), &throttleMsg)
|
err := proto.Unmarshal(message.Payload(), &throttleMsg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("unable to unmarshal throttle msg: %v", err)
|
zap.S().Errorf("unable to unmarshal throttle msg: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.WriteThrottle(&throttleMsg)
|
c.WriteThrottle(&throttleMsg)
|
||||||
|
15
go.mod
15
go.mod
@ -4,16 +4,19 @@ go 1.17
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/avast/retry-go v3.0.0+incompatible
|
github.com/avast/retry-go v3.0.0+incompatible
|
||||||
github.com/cyrilix/robocar-base v0.1.4
|
github.com/cyrilix/robocar-base v0.1.6
|
||||||
github.com/cyrilix/robocar-protobuf/go v1.0.3
|
github.com/cyrilix/robocar-protobuf/go v1.0.4
|
||||||
|
github.com/disintegration/imaging v1.6.2
|
||||||
github.com/eclipse/paho.mqtt.golang v1.3.5
|
github.com/eclipse/paho.mqtt.golang v1.3.5
|
||||||
github.com/golang/protobuf v1.5.2
|
github.com/golang/protobuf v1.5.2
|
||||||
github.com/sirupsen/logrus v1.8.1
|
go.uber.org/zap v1.19.1
|
||||||
|
google.golang.org/protobuf v1.27.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/gorilla/websocket v1.4.2 // indirect
|
github.com/gorilla/websocket v1.4.2 // indirect
|
||||||
golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0 // indirect
|
go.uber.org/atomic v1.7.0 // indirect
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd // indirect
|
go.uber.org/multierr v1.6.0 // indirect
|
||||||
google.golang.org/protobuf v1.26.0 // indirect
|
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 // indirect
|
||||||
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 // indirect
|
||||||
)
|
)
|
||||||
|
95
go.sum
95
go.sum
@ -2,31 +2,27 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
|
|||||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
||||||
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
|
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
|
||||||
github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
|
github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
|
||||||
github.com/avast/retry-go v2.6.0+incompatible h1:FelcMrm7Bxacr1/RM8+/eqkDkmVN7tjlsy51dOzB3LI=
|
|
||||||
github.com/avast/retry-go v2.6.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY=
|
|
||||||
github.com/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHSxpiH9JdtuBj0=
|
github.com/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHSxpiH9JdtuBj0=
|
||||||
github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY=
|
github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY=
|
||||||
|
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
||||||
|
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||||
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
||||||
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
|
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
|
||||||
github.com/cyrilix/robocar-base v0.1.2 h1:bhT5ohhviodCOd5usy013ctwu7ko+IfyDXnYHg7lJ8I=
|
github.com/cyrilix/robocar-base v0.1.6 h1:VVcSZD8DPsha3XDLxRBMvtcd6uC8CcIjqbxG482dxvo=
|
||||||
github.com/cyrilix/robocar-base v0.1.2/go.mod h1:G2SiYNUDAIv+65XWpiHnCXj7Jz2V6pOBLaIrcasGkww=
|
github.com/cyrilix/robocar-base v0.1.6/go.mod h1:m5ov/7hpRHi0yMp2prKafL6UEsM2O71Uea85WR0/jjI=
|
||||||
github.com/cyrilix/robocar-base v0.1.4 h1:nfnjRwAcCfS7xGu6tW9rZhmc/HZIsuDJX5NFhgX5dWE=
|
github.com/cyrilix/robocar-protobuf/go v1.0.4 h1:XTolFYbiKw4gQ2l+z/LMZkLrmAUMzlHcQBzp/czlANo=
|
||||||
github.com/cyrilix/robocar-base v0.1.4/go.mod h1:Tt04UmbGBiQtU0Cn3wFD0q7XoyokTwIlWYQxThKI+04=
|
github.com/cyrilix/robocar-protobuf/go v1.0.4/go.mod h1:1fyGMVm4ZodfYRrbWCEQgtvKyvrhyTBe5zA7/Qeh/H0=
|
||||||
github.com/cyrilix/robocar-protobuf/go v1.0.2 h1:eWwu7T07uvABh74bWOJa77alUs6VaWNEPd7Zezua2Cs=
|
|
||||||
github.com/cyrilix/robocar-protobuf/go v1.0.2/go.mod h1:xj7H/a7qpvXgmW1983Fjd143Mz9Yt0C6RCxvB8M6pEM=
|
|
||||||
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/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
|
||||||
|
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
||||||
github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||||
github.com/docker/docker v17.12.0-ce-rc1.0.20200916142827-bd33bbf0497b+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
github.com/docker/docker v17.12.0-ce-rc1.0.20200916142827-bd33bbf0497b+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||||
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||||
github.com/eclipse/paho.mqtt.golang v1.3.1 h1:6F5FYb1hxVSZS+p0ji5xBQamc5ltOolTYRy5R15uVmI=
|
|
||||||
github.com/eclipse/paho.mqtt.golang v1.3.1/go.mod h1:eTzb4gxwwyWpqBUHGQZ4ABAV7+Jgm1PklsYT/eo8Hcc=
|
|
||||||
github.com/eclipse/paho.mqtt.golang v1.3.5 h1:sWtmgNxYM9P2sP+xEItMozsR3w0cqZFlqnNN1bdl41Y=
|
github.com/eclipse/paho.mqtt.golang v1.3.5 h1:sWtmgNxYM9P2sP+xEItMozsR3w0cqZFlqnNN1bdl41Y=
|
||||||
github.com/eclipse/paho.mqtt.golang v1.3.5/go.mod h1:eTzb4gxwwyWpqBUHGQZ4ABAV7+Jgm1PklsYT/eo8Hcc=
|
github.com/eclipse/paho.mqtt.golang v1.3.5/go.mod h1:eTzb4gxwwyWpqBUHGQZ4ABAV7+Jgm1PklsYT/eo8Hcc=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
@ -43,21 +39,11 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU
|
|||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
|
||||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
|
||||||
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
|
|
||||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
|
||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
|
||||||
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
|
||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
@ -69,10 +55,8 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO
|
|||||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
@ -86,38 +70,54 @@ github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQ
|
|||||||
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||||
github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
|
github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
|
||||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
|
||||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
|
||||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/testcontainers/testcontainers-go v0.9.0/go.mod h1:b22BFXhRbg4PJmeMVWh6ftqjyZHgiIl3w274e9r3C2E=
|
github.com/testcontainers/testcontainers-go v0.9.0/go.mod h1:b22BFXhRbg4PJmeMVWh6ftqjyZHgiIl3w274e9r3C2E=
|
||||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||||
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
|
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=
|
||||||
|
go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||||
|
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
|
||||||
|
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||||
|
go.uber.org/zap v1.19.1 h1:ue41HOKd1vGURxrmeKIgELGb3jPW9DMUDGtsinblHwI=
|
||||||
|
go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=
|
||||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 h1:hVwzHzIUGRjiF7EcUjqNxk3NCfkPxbDKRdnNE1Rpg0U=
|
||||||
|
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0 h1:Jcxah/M+oLZ/R4/z5RzfPzGbPXnVDPkEDtf2JnuxN+U=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA=
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
|
||||||
golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
@ -125,39 +125,44 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
|
|||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
|
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
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=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/tools v0.0.0-20180810170437-e96c4e24768d/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180810170437-e96c4e24768d/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
|
||||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
|
||||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
|
||||||
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
|
|
||||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
|
||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
|
||||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
|
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
|
||||||
|
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gotest.tools v0.0.0-20181223230014-1083505acf35/go.mod h1:R//lfYlUuTOTfblYI3lGoAAAebUdzjvbmQsuB7Ykd90=
|
gotest.tools v0.0.0-20181223230014-1083505acf35/go.mod h1:R//lfYlUuTOTfblYI3lGoAAAebUdzjvbmQsuB7Ykd90=
|
||||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
@ -4,8 +4,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"github.com/cyrilix/robocar-simulator/pkg/gateway"
|
"github.com/cyrilix/robocar-simulator/pkg/gateway"
|
||||||
mqtt "github.com/eclipse/paho.mqtt.golang"
|
mqtt "github.com/eclipse/paho.mqtt.golang"
|
||||||
"github.com/golang/protobuf/proto"
|
"go.uber.org/zap"
|
||||||
log "github.com/sirupsen/logrus"
|
"google.golang.org/protobuf/proto"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@ -59,7 +59,7 @@ func (m *MsgPublisher) Stop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *MsgPublisher) listenThrottle() {
|
func (m *MsgPublisher) listenThrottle() {
|
||||||
logr := log.WithField("msg_type", "throttleChan")
|
logr := zap.S().With("msg_type", "throttleChan")
|
||||||
msgChan := m.srcEvents.SubscribeThrottle()
|
msgChan := m.srcEvents.SubscribeThrottle()
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
@ -83,7 +83,7 @@ func (m *MsgPublisher) listenThrottle() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *MsgPublisher) listenSteering() {
|
func (m *MsgPublisher) listenSteering() {
|
||||||
logr := log.WithField("msg_type", "steeringChan")
|
logr := zap.S().With("msg_type", "steeringChan")
|
||||||
msgChan := m.srcEvents.SubscribeSteering()
|
msgChan := m.srcEvents.SubscribeSteering()
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
@ -107,12 +107,13 @@ func (m *MsgPublisher) listenSteering() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *MsgPublisher) listenFrame() {
|
func (m *MsgPublisher) listenFrame() {
|
||||||
logr := log.WithField("msg_type", "frame")
|
logr := zap.S().With("msg_type", "frame")
|
||||||
msgChan := m.srcEvents.SubscribeFrame()
|
msgChan := m.srcEvents.SubscribeFrame()
|
||||||
for {
|
for {
|
||||||
msg := <-msgChan
|
msg := <-msgChan
|
||||||
if msg == nil {
|
if msg == nil {
|
||||||
// channel closed
|
// channel closed
|
||||||
|
logr.Info("received empty message, channel closed")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
logr.Debugf("new frame %v", msg.Id)
|
logr.Debugf("new frame %v", msg.Id)
|
||||||
|
@ -3,8 +3,8 @@ package events
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/cyrilix/robocar-protobuf/go/events"
|
"github.com/cyrilix/robocar-protobuf/go/events"
|
||||||
"github.com/golang/protobuf/proto"
|
|
||||||
"github.com/golang/protobuf/ptypes/timestamp"
|
"github.com/golang/protobuf/ptypes/timestamp"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
@ -40,7 +40,7 @@ func TestGateway_Start(t *testing.T) {
|
|||||||
OffsetZ: "0",
|
OffsetZ: "0",
|
||||||
RotX: "0",
|
RotX: "0",
|
||||||
}
|
}
|
||||||
gw := New(simulatorMock.Addr(),
|
gw, err := New(simulatorMock.Addr(),
|
||||||
&carConfig,
|
&carConfig,
|
||||||
&simulator.RacerBioMsg{},
|
&simulator.RacerBioMsg{},
|
||||||
&camConfig,
|
&camConfig,
|
||||||
@ -130,10 +130,13 @@ func TestGateway_WriteSteering(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
gw := New(simulatorMock.Addr(),
|
gw, err := New(simulatorMock.Addr(),
|
||||||
&simulator.CarConfigMsg{MsgType: simulator.MsgTypeCarConfig},
|
&simulator.CarConfigMsg{MsgType: simulator.MsgTypeCarConfig},
|
||||||
&simulator.RacerBioMsg{},
|
&simulator.RacerBioMsg{},
|
||||||
&simulator.CamConfigMsg{})
|
&simulator.CamConfigMsg{
|
||||||
|
ImgH: "128",
|
||||||
|
ImgW: "160",
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to init simulator gateway: %v", err)
|
t.Fatalf("unable to init simulator gateway: %v", err)
|
||||||
}
|
}
|
||||||
@ -254,10 +257,16 @@ func TestGateway_WriteThrottle(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
gw := New(simulatorMock.Addr(), &simulator.CarConfigMsg{MsgType: simulator.MsgTypeCarConfig},
|
gw, err := New(simulatorMock.Addr(), &simulator.CarConfigMsg{MsgType: simulator.MsgTypeCarConfig},
|
||||||
&simulator.RacerBioMsg{},
|
&simulator.RacerBioMsg{},
|
||||||
&simulator.CamConfigMsg{})
|
&simulator.CamConfigMsg{
|
||||||
|
ImgH: "128",
|
||||||
|
ImgW: "160",
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to init simulator gateway: %v", err)
|
||||||
|
}
|
||||||
//go gw.Start()
|
//go gw.Start()
|
||||||
|
|
||||||
//<- simulatorMock.NotifyCar()
|
//<- simulatorMock.NotifyCar()
|
||||||
|
@ -2,15 +2,20 @@ package gateway
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/avast/retry-go"
|
"github.com/avast/retry-go"
|
||||||
"github.com/cyrilix/robocar-protobuf/go/events"
|
"github.com/cyrilix/robocar-protobuf/go/events"
|
||||||
"github.com/cyrilix/robocar-simulator/pkg/simulator"
|
"github.com/cyrilix/robocar-simulator/pkg/simulator"
|
||||||
|
"github.com/disintegration/imaging"
|
||||||
"github.com/golang/protobuf/ptypes/timestamp"
|
"github.com/golang/protobuf/ptypes/timestamp"
|
||||||
log "github.com/sirupsen/logrus"
|
"go.uber.org/zap"
|
||||||
|
"image"
|
||||||
|
"image/jpeg"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@ -31,10 +36,19 @@ type ThrottleSource interface {
|
|||||||
SubscribeThrottle() <-chan *events.ThrottleMessage
|
SubscribeThrottle() <-chan *events.ThrottleMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(addressSimulator string, car *simulator.CarConfigMsg, racer *simulator.RacerBioMsg, camera *simulator.CamConfigMsg) *Gateway {
|
func New(addressSimulator string, car *simulator.CarConfigMsg, racer *simulator.RacerBioMsg, camera *simulator.CamConfigMsg) (*Gateway, error) {
|
||||||
l := log.WithField("simulator", addressSimulator)
|
l := zap.S().With("simulator", addressSimulator)
|
||||||
l.Info("run gateway from simulator")
|
l.Info("run gateway from simulator")
|
||||||
|
|
||||||
|
imgHeight, err := strconv.Atoi(camera.ImgH)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid camera configuration, height should be a number: %w", err)
|
||||||
|
}
|
||||||
|
imgWidth, err := strconv.Atoi(camera.ImgW)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid camera configuration, width should be a number: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
return &Gateway{
|
return &Gateway{
|
||||||
address: addressSimulator,
|
address: addressSimulator,
|
||||||
log: l,
|
log: l,
|
||||||
@ -46,12 +60,14 @@ func New(addressSimulator string, car *simulator.CarConfigMsg, racer *simulator.
|
|||||||
racerSubscribers: make(map[chan *simulator.Msg]interface{}),
|
racerSubscribers: make(map[chan *simulator.Msg]interface{}),
|
||||||
cameraSubscribers: make(map[chan *simulator.Msg]interface{}),
|
cameraSubscribers: make(map[chan *simulator.Msg]interface{}),
|
||||||
carConfig: car,
|
carConfig: car,
|
||||||
|
imgWidth: imgWidth,
|
||||||
|
imgHeight: imgHeight,
|
||||||
racer: racer,
|
racer: racer,
|
||||||
cameraConfig: camera,
|
cameraConfig: camera,
|
||||||
}
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Simulator interface to events gateway frames into events topicFrame */
|
// Gateway is Simulator interface to events gateway frames into events topicFrame
|
||||||
type Gateway struct {
|
type Gateway struct {
|
||||||
cancel chan interface{}
|
cancel chan interface{}
|
||||||
|
|
||||||
@ -64,7 +80,7 @@ type Gateway struct {
|
|||||||
muControl sync.Mutex
|
muControl sync.Mutex
|
||||||
lastControl *simulator.ControlMsg
|
lastControl *simulator.ControlMsg
|
||||||
|
|
||||||
log *log.Entry
|
log *zap.SugaredLogger
|
||||||
|
|
||||||
frameSubscribers map[chan<- *events.FrameMessage]interface{}
|
frameSubscribers map[chan<- *events.FrameMessage]interface{}
|
||||||
steeringSubscribers map[chan<- *events.SteeringMessage]interface{}
|
steeringSubscribers map[chan<- *events.SteeringMessage]interface{}
|
||||||
@ -76,34 +92,46 @@ type Gateway struct {
|
|||||||
cameraSubscribers map[chan *simulator.Msg]interface{}
|
cameraSubscribers map[chan *simulator.Msg]interface{}
|
||||||
|
|
||||||
carConfig *simulator.CarConfigMsg
|
carConfig *simulator.CarConfigMsg
|
||||||
|
imgHeight int
|
||||||
|
imgWidth int
|
||||||
racer *simulator.RacerBioMsg
|
racer *simulator.RacerBioMsg
|
||||||
cameraConfig *simulator.CamConfigMsg
|
cameraConfig *simulator.CamConfigMsg
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Gateway) Start() error {
|
func (g *Gateway) Start() error {
|
||||||
g.log.Info("connect to simulator")
|
g.log.Info("connect to simulator")
|
||||||
g.cancel = make(chan interface{})
|
g.cancel = make(chan interface{})
|
||||||
msgChan := g.subscribeTelemetryEvents()
|
|
||||||
|
|
||||||
go g.run()
|
go g.run()
|
||||||
|
|
||||||
|
|
||||||
err := g.writeRacerConfig()
|
err := g.writeRacerConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to configure racer to server: %v", err)
|
return fmt.Errorf("unable to configure racer to server: %v", err)
|
||||||
}
|
}
|
||||||
err = g.writeCarConfig()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to configure car to server: %v", err)
|
|
||||||
}
|
|
||||||
err = g.writeCameraConfig()
|
err = g.writeCameraConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to configure camera to server: %v", err)
|
return fmt.Errorf("unable to configure camera to server: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
err = g.writeCarConfig()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to configure car to server: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
msgChan := g.subscribeTelemetryEvents()
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case msg := <-msgChan:
|
case msg := <-msgChan:
|
||||||
fr := g.publishFrame(msg)
|
g.log.Debug("try to publish frame")
|
||||||
|
fr, err := g.publishFrame(msg)
|
||||||
|
if err != nil {
|
||||||
|
zap.S().Errorf("unable to publish frame, ignore event %v: %v", msg, err)
|
||||||
|
}
|
||||||
|
g.log.Debugf("frame published: %v", fr)
|
||||||
go g.publishInputSteering(msg, fr)
|
go g.publishInputSteering(msg, fr)
|
||||||
go g.publishInputThrottle(msg, fr)
|
go g.publishInputThrottle(msg, fr)
|
||||||
case <-g.cancel:
|
case <-g.cancel:
|
||||||
@ -195,66 +223,79 @@ func (g *Gateway) listen() error {
|
|||||||
err = json.Unmarshal(rawLine, &msg)
|
err = json.Unmarshal(rawLine, &msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
g.log.Errorf("unable to unmarshal simulator msg '%v': %v", string(rawLine), err)
|
g.log.Errorf("unable to unmarshal simulator msg '%v': %v", string(rawLine), err)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
g.log.Debugf("new message of type %v", string(msg.MsgType))
|
||||||
switch msg.MsgType {
|
switch msg.MsgType {
|
||||||
|
case "":
|
||||||
|
g.log.Debug("ping")
|
||||||
case simulator.MsgTypeTelemetry:
|
case simulator.MsgTypeTelemetry:
|
||||||
g.broadcastTelemetryMsg(rawLine)
|
g.broadcastTelemetryMsg(rawLine)
|
||||||
case simulator.MsgTypeCarLoaded:
|
case simulator.MsgTypeCarLoaded:
|
||||||
|
g.log.Debugf("car loaded: %v",string(rawLine))
|
||||||
|
if err := g.writeCarConfig(); err != nil{
|
||||||
|
zap.S().Errorf("unable to send car config: %v", err)
|
||||||
|
}
|
||||||
g.broadcastCarMsg(rawLine)
|
g.broadcastCarMsg(rawLine)
|
||||||
case simulator.MsgTypeRacerInfo:
|
case simulator.MsgTypeRacerInfo:
|
||||||
g.broadcastRacerMsg(rawLine)
|
g.broadcastRacerMsg(rawLine)
|
||||||
default:
|
default:
|
||||||
log.Warnf("unmanaged simulator message: %v", string(rawLine))
|
g.log.Warnf("unmanaged simulator message: %v", string(rawLine))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Gateway) broadcastTelemetryMsg(rawLine []byte) {
|
func (g *Gateway) broadcastTelemetryMsg(rawLine []byte) {
|
||||||
for c := range g.telemetrySubscribers {
|
|
||||||
|
|
||||||
var tMsg simulator.TelemetryMsg
|
var tMsg simulator.TelemetryMsg
|
||||||
err := json.Unmarshal(rawLine, &tMsg)
|
err := json.Unmarshal(rawLine, &tMsg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
g.log.Errorf("unable to unmarshal telemetry simulator msg '%v': %v", string(rawLine), err)
|
g.log.Errorf("unable to unmarshal telemetry simulator msg '%v': %v", string(rawLine), err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
g.log.Debugf("broadcast %d telemetry'", len(g.telemetrySubscribers))
|
||||||
|
for c := range g.telemetrySubscribers {
|
||||||
|
g.log.Debugf("broadcast telemetry ")
|
||||||
c <- &tMsg
|
c <- &tMsg
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Gateway) broadcastCarMsg(rawLine []byte) {
|
func (g *Gateway) broadcastCarMsg(rawLine []byte) {
|
||||||
for c := range g.carSubscribers {
|
|
||||||
var tMsg simulator.Msg
|
var tMsg simulator.Msg
|
||||||
err := json.Unmarshal(rawLine, &tMsg)
|
err := json.Unmarshal(rawLine, &tMsg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
g.log.Errorf("unable to unmarshal car simulator msg '%v': %v", string(rawLine), err)
|
g.log.Errorf("unable to unmarshal car simulator msg '%v': %v", string(rawLine), err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
for c := range g.carSubscribers {
|
||||||
c <- &tMsg
|
c <- &tMsg
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Gateway) broadcastRacerMsg(rawLine []byte) {
|
func (g *Gateway) broadcastRacerMsg(rawLine []byte) {
|
||||||
for c := range g.racerSubscribers {
|
|
||||||
var tMsg simulator.Msg
|
var tMsg simulator.Msg
|
||||||
err := json.Unmarshal(rawLine, &tMsg)
|
err := json.Unmarshal(rawLine, &tMsg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
g.log.Errorf("unable to unmarshal racer simulator msg '%v': %v", string(rawLine), err)
|
g.log.Errorf("unable to unmarshal racer simulator msg '%v': %v", string(rawLine), err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
for c := range g.racerSubscribers {
|
||||||
c <- &tMsg
|
c <- &tMsg
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func (g *Gateway) broadcastCameraMsg(rawLine []byte) {
|
func (g *Gateway) broadcastCameraMsg(rawLine []byte) {
|
||||||
for c := range g.cameraSubscribers {
|
|
||||||
var tMsg simulator.Msg
|
var tMsg simulator.Msg
|
||||||
err := json.Unmarshal(rawLine, &tMsg)
|
err := json.Unmarshal(rawLine, &tMsg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
g.log.Errorf("unable to unmarshal camera simulator msg '%v': %v", string(rawLine), err)
|
g.log.Errorf("unable to unmarshal camera simulator msg '%v': %v", string(rawLine), err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
for c := range g.cameraSubscribers {
|
||||||
c <- &tMsg
|
c <- &tMsg
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Gateway) publishFrame(msgSim *simulator.TelemetryMsg) *events.FrameRef {
|
func (g *Gateway) publishFrame(msgSim *simulator.TelemetryMsg) (*events.FrameRef, error) {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
frameRef := &events.FrameRef{
|
frameRef := &events.FrameRef{
|
||||||
Name: "gateway",
|
Name: "gateway",
|
||||||
@ -264,17 +305,32 @@ func (g *Gateway) publishFrame(msgSim *simulator.TelemetryMsg) *events.FrameRef
|
|||||||
Nanos: int32(now.Nanosecond()),
|
Nanos: int32(now.Nanosecond()),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
msg := &events.FrameMessage{
|
msg := &events.FrameMessage{
|
||||||
Id: frameRef,
|
Id: frameRef,
|
||||||
Frame: msgSim.Image,
|
Frame: msgSim.Image,
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("events frame '%v/%v'", msg.Id.Name, msg.Id.Id)
|
img, _, err := image.Decode(bytes.NewReader(msgSim.Image))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("bad image, skip simulator event: %w", err)
|
||||||
|
}
|
||||||
|
if img.Bounds().Dy() != g.imgHeight || img.Bounds().Dx() != g.imgWidth {
|
||||||
|
zap.S().Debugf("resize simulator image from (%vx%v) -> (%dx%d)", img.Bounds().Dx(), img.Bounds().Dy(), g.imgWidth, g.imgHeight)
|
||||||
|
img = imaging.Resize(img, g.imgWidth, g.imgHeight, imaging.NearestNeighbor)
|
||||||
|
var imgBuf bytes.Buffer
|
||||||
|
err = jpeg.Encode(&imgBuf, img, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to encode to peg resized image: %w", err)
|
||||||
|
}
|
||||||
|
msg.Frame = imgBuf.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
g.log.Debugf("events frame '%v/%v'", msg.Id.Name, msg.Id.Id)
|
||||||
for fs := range g.frameSubscribers {
|
for fs := range g.frameSubscribers {
|
||||||
fs <- msg
|
fs <- msg
|
||||||
}
|
}
|
||||||
return frameRef
|
return frameRef, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Gateway) publishInputSteering(msgSim *simulator.TelemetryMsg, frameRef *events.FrameRef) {
|
func (g *Gateway) publishInputSteering(msgSim *simulator.TelemetryMsg, frameRef *events.FrameRef) {
|
||||||
@ -284,7 +340,7 @@ func (g *Gateway) publishInputSteering(msgSim *simulator.TelemetryMsg, frameRef
|
|||||||
Confidence: 1.0,
|
Confidence: 1.0,
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("events steering '%v'", steering.Steering)
|
g.log.Debugf("events steering '%v'", steering.Steering)
|
||||||
for ss := range g.steeringSubscribers {
|
for ss := range g.steeringSubscribers {
|
||||||
ss <- steering
|
ss <- steering
|
||||||
}
|
}
|
||||||
@ -297,7 +353,7 @@ func (g *Gateway) publishInputThrottle(msgSim *simulator.TelemetryMsg, frameRef
|
|||||||
Confidence: 1.0,
|
Confidence: 1.0,
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("events throttle '%v'", msg.Throttle)
|
g.log.Debugf("events throttle '%v'", msg.Throttle)
|
||||||
for ts := range g.throttleSubscribers {
|
for ts := range g.throttleSubscribers {
|
||||||
ts <- msg
|
ts <- msg
|
||||||
}
|
}
|
||||||
@ -374,6 +430,7 @@ func (g *Gateway) WriteSteering(message *events.SteeringMessage) {
|
|||||||
g.initLastControlMsg()
|
g.initLastControlMsg()
|
||||||
|
|
||||||
g.lastControl.Steering = fmt.Sprintf("%.2f", message.Steering)
|
g.lastControl.Steering = fmt.Sprintf("%.2f", message.Steering)
|
||||||
|
zap.S().Debugf("steering updated: %v", g.lastControl.Steering)
|
||||||
g.writeControlCommandToSimulator()
|
g.writeControlCommandToSimulator()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -398,10 +455,10 @@ func (g *Gateway) writeCommand(content []byte) error {
|
|||||||
g.log.Errorf("unable to connect to simulator to send control command: %v", err)
|
g.log.Errorf("unable to connect to simulator to send control command: %v", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
log.Debugf("write command to simulator: %v", string(content))
|
g.log.Debugf("write command to simulator: %v", string(content))
|
||||||
w := bufio.NewWriter(g.conn)
|
w := bufio.NewWriter(g.conn)
|
||||||
|
|
||||||
_, err := w.Write(append(content, '\n'))
|
_, err := w.Write(content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to write control msg \"%#v\" to simulator: %v", g.lastControl, err)
|
return fmt.Errorf("unable to write control msg \"%#v\" to simulator: %v", g.lastControl, err)
|
||||||
}
|
}
|
||||||
@ -425,6 +482,7 @@ func (g *Gateway) WriteThrottle(message *events.ThrottleMessage) {
|
|||||||
g.lastControl.Brake = fmt.Sprintf("%.2f", -1*message.Throttle)
|
g.lastControl.Brake = fmt.Sprintf("%.2f", -1*message.Throttle)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
zap.S().Debugf("throttle updated: %v, brake: %v", g.lastControl.Throttle, g.lastControl.Brake)
|
||||||
g.writeControlCommandToSimulator()
|
g.writeControlCommandToSimulator()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -456,9 +514,13 @@ func (g *Gateway) writeCarConfig() error {
|
|||||||
return fmt.Errorf("unable to send car config to simulator: %v", err)
|
return fmt.Errorf("unable to send car config to simulator: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
msg := <-carChan
|
select {
|
||||||
|
case msg := <-carChan:
|
||||||
g.log.Infof("Car loaded: %v", msg)
|
g.log.Infof("Car loaded: %v", msg)
|
||||||
time.Sleep(250 * time.Millisecond)
|
case <-time.Tick(250 * time.Millisecond):
|
||||||
|
g.log.Info("no response after car loading")
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -482,6 +544,7 @@ func (g *Gateway) writeRacerConfig() error {
|
|||||||
case msg := <-racerChan:
|
case msg := <-racerChan:
|
||||||
g.log.Infof("Racer loaded: %v", msg)
|
g.log.Infof("Racer loaded: %v", msg)
|
||||||
case <-time.Tick(250 * time.Millisecond):
|
case <-time.Tick(250 * time.Millisecond):
|
||||||
|
g.log.Info("no response after racer loading")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -506,6 +569,7 @@ func (g *Gateway) writeCameraConfig() error {
|
|||||||
case msg := <-cameraChan:
|
case msg := <-cameraChan:
|
||||||
g.log.Infof("Camera configured: %v", msg)
|
g.log.Infof("Camera configured: %v", msg)
|
||||||
case <-time.Tick(250 * time.Millisecond):
|
case <-time.Tick(250 * time.Millisecond):
|
||||||
|
g.log.Info("no response after camera loading")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"github.com/cyrilix/robocar-protobuf/go/events"
|
"github.com/cyrilix/robocar-protobuf/go/events"
|
||||||
"github.com/cyrilix/robocar-simulator/pkg/simulator"
|
"github.com/cyrilix/robocar-simulator/pkg/simulator"
|
||||||
log "github.com/sirupsen/logrus"
|
"go.uber.org/zap"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@ -25,11 +25,14 @@ func TestGateway_ListenEvents(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
gw := New(simulatorMock.Addr(),
|
gw, err := New(simulatorMock.Addr(),
|
||||||
&simulator.CarConfigMsg{MsgType: simulator.MsgTypeCarConfig},
|
&simulator.CarConfigMsg{MsgType: simulator.MsgTypeCarConfig},
|
||||||
&simulator.RacerBioMsg{},
|
&simulator.RacerBioMsg{},
|
||||||
&simulator.CamConfigMsg{},
|
&simulator.CamConfigMsg{},
|
||||||
)
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to init test: %v", err)
|
||||||
|
}
|
||||||
go func() {
|
go func() {
|
||||||
err := gw.Start()
|
err := gw.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -48,7 +51,7 @@ func TestGateway_ListenEvents(t *testing.T) {
|
|||||||
simulatorMock.WaitConnection()
|
simulatorMock.WaitConnection()
|
||||||
simulatorMock.EmitMsg(fmt.Sprintf("{\"msg_type\": \"%s\"}", simulator.MsgTypeCarLoaded))
|
simulatorMock.EmitMsg(fmt.Sprintf("{\"msg_type\": \"%s\"}", simulator.MsgTypeCarLoaded))
|
||||||
|
|
||||||
log.Trace("read test data")
|
zap.S().Debug("read test data")
|
||||||
testContent, err := ioutil.ReadFile("testdata/msg.json")
|
testContent, err := ioutil.ReadFile("testdata/msg.json")
|
||||||
lines := strings.Split(string(testContent), "\n")
|
lines := strings.Split(string(testContent), "\n")
|
||||||
|
|
||||||
@ -93,7 +96,7 @@ func TestGateway_ListenEvents(t *testing.T) {
|
|||||||
throttleIdRef = msg.FrameRef
|
throttleIdRef = msg.FrameRef
|
||||||
wg.Done()
|
wg.Done()
|
||||||
case <-finished:
|
case <-finished:
|
||||||
log.Trace("loop ended")
|
zap.S().Debugf("loop ended")
|
||||||
endLoop = true
|
endLoop = true
|
||||||
case <-timeout:
|
case <-timeout:
|
||||||
t.Errorf("not all event are published")
|
t.Errorf("not all event are published")
|
||||||
|
@ -5,7 +5,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/cyrilix/robocar-simulator/pkg/simulator"
|
"github.com/cyrilix/robocar-simulator/pkg/simulator"
|
||||||
log "github.com/sirupsen/logrus"
|
"go.uber.org/zap"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
@ -51,7 +51,7 @@ func (c *Gw2SimMock) listen() error {
|
|||||||
for {
|
for {
|
||||||
conn, err := c.ln.Accept()
|
conn, err := c.ln.Accept()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debugf("connection close: %v", err)
|
zap.S().Debugf("connection close: %v", err)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
go c.handleConnection(conn)
|
go c.handleConnection(conn)
|
||||||
@ -65,6 +65,7 @@ func (c *Gw2SimMock) Addr() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Gw2SimMock) handleConnection(conn net.Conn) {
|
func (c *Gw2SimMock) handleConnection(conn net.Conn) {
|
||||||
|
log := zap.S()
|
||||||
c.initOnce.Do(c.init)
|
c.initOnce.Do(c.init)
|
||||||
reader := bufio.NewReader(conn)
|
reader := bufio.NewReader(conn)
|
||||||
writer := bufio.NewWriter(conn)
|
writer := bufio.NewWriter(conn)
|
||||||
@ -131,7 +132,7 @@ func (c *Gw2SimMock) handleConnection(conn net.Conn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Gw2SimMock) Close() error {
|
func (c *Gw2SimMock) Close() error {
|
||||||
log.Debugf("close mock server")
|
zap.S().Debugf("close mock server")
|
||||||
err := c.ln.Close()
|
err := c.ln.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to close mock server: %v", err)
|
return fmt.Errorf("unable to close mock server: %v", err)
|
||||||
@ -154,7 +155,7 @@ type Sim2GwMock struct {
|
|||||||
conn net.Conn
|
conn net.Conn
|
||||||
writer *bufio.Writer
|
writer *bufio.Writer
|
||||||
newConnection chan net.Conn
|
newConnection chan net.Conn
|
||||||
logger *log.Entry
|
logger *zap.SugaredLogger
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Sim2GwMock) EmitMsg(p string) (err error) {
|
func (c *Sim2GwMock) EmitMsg(p string) (err error) {
|
||||||
@ -187,7 +188,7 @@ func (c *Sim2GwMock) WaitConnection() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Sim2GwMock) Start() error {
|
func (c *Sim2GwMock) Start() error {
|
||||||
c.logger = log.WithField("simulator", "mock")
|
c.logger = zap.S().With("simulator", "mock")
|
||||||
c.newConnection = make(chan net.Conn)
|
c.newConnection = make(chan net.Conn)
|
||||||
ln, err := net.Listen("tcp", "127.0.0.1:")
|
ln, err := net.Listen("tcp", "127.0.0.1:")
|
||||||
c.ln = ln
|
c.ln = ln
|
||||||
|
@ -29,7 +29,7 @@ type TelemetryMsg struct {
|
|||||||
Cte float64 `json:"cte"`
|
Cte float64 `json:"cte"`
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Json msg used to control cars. MsgType must be filled with "control" */
|
// ControlMsg is json msg used to control cars. MsgType must be filled with "control"
|
||||||
type ControlMsg struct {
|
type ControlMsg struct {
|
||||||
MsgType MsgType `json:"msg_type"`
|
MsgType MsgType `json:"msg_type"`
|
||||||
Steering string `json:"steering"`
|
Steering string `json:"steering"`
|
||||||
@ -113,4 +113,6 @@ type CamConfigMsg struct {
|
|||||||
OffsetY string `json:"offset_y"`
|
OffsetY string `json:"offset_y"`
|
||||||
OffsetZ string `json:"offset_z"`
|
OffsetZ string `json:"offset_z"`
|
||||||
RotX string `json:"rot_x"`
|
RotX string `json:"rot_x"`
|
||||||
|
RotY string `json:"rot_y"`
|
||||||
|
RotZ string `json:"rot_z"`
|
||||||
}
|
}
|
||||||
|
14
vendor/github.com/cyrilix/robocar-base/cli/cli.go
generated
vendored
14
vendor/github.com/cyrilix/robocar-base/cli/cli.go
generated
vendored
@ -5,7 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"github.com/cyrilix/robocar-base/service"
|
"github.com/cyrilix/robocar-base/service"
|
||||||
MQTT "github.com/eclipse/paho.mqtt.golang"
|
MQTT "github.com/eclipse/paho.mqtt.golang"
|
||||||
"log"
|
"go.uber.org/zap"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -25,7 +25,7 @@ func SetIntDefaultValueFromEnv(value *int, key string, defaultValue int) error {
|
|||||||
sVal = os.Getenv(key)
|
sVal = os.Getenv(key)
|
||||||
val, err := strconv.Atoi(sVal)
|
val, err := strconv.Atoi(sVal)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("unable to convert string to int: %v", err)
|
zap.S().Errorf("unable to convert string to int: %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
*value = val
|
*value = val
|
||||||
@ -40,7 +40,7 @@ func SetFloat64DefaultValueFromEnv(value *float64, key string, defaultValue floa
|
|||||||
sVal = os.Getenv(key)
|
sVal = os.Getenv(key)
|
||||||
val, err := strconv.ParseFloat(sVal, 64)
|
val, err := strconv.ParseFloat(sVal, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("unable to convert string to float: %v", err)
|
zap.S().Errorf("unable to convert string to float: %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
*value = val
|
*value = val
|
||||||
@ -81,7 +81,7 @@ func InitIntFlag(key string, defValue int) int {
|
|||||||
var value int
|
var value int
|
||||||
err := SetIntDefaultValueFromEnv(&value, key, defValue)
|
err := SetIntDefaultValueFromEnv(&value, key, defValue)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panicf("invalid int value: %v", err)
|
zap.S().Panicf("invalid int value: %v", err)
|
||||||
}
|
}
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
@ -90,7 +90,7 @@ func InitFloat64Flag(key string, defValue float64) float64 {
|
|||||||
var value float64
|
var value float64
|
||||||
err := SetFloat64DefaultValueFromEnv(&value, key, defValue)
|
err := SetFloat64DefaultValueFromEnv(&value, key, defValue)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panicf("invalid value: %v", err)
|
zap.S().Panicf("invalid value: %v", err)
|
||||||
}
|
}
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
@ -106,8 +106,8 @@ func Connect(uri, username, password, clientId string) (MQTT.Client, error) {
|
|||||||
opts.SetDefaultPublishHandler(
|
opts.SetDefaultPublishHandler(
|
||||||
//define a function for the default message handler
|
//define a function for the default message handler
|
||||||
func(client MQTT.Client, msg MQTT.Message) {
|
func(client MQTT.Client, msg MQTT.Message) {
|
||||||
fmt.Printf("TOPIC: %s\n", msg.Topic())
|
zap.S().Infof("TOPIC: %s", msg.Topic())
|
||||||
fmt.Printf("MSG: %s\n", msg.Payload())
|
zap.S().Infof("MSG: %s", msg.Payload())
|
||||||
})
|
})
|
||||||
|
|
||||||
//create and start a client using the above ClientOptions
|
//create and start a client using the above ClientOptions
|
||||||
|
8
vendor/github.com/cyrilix/robocar-base/service/part.go
generated
vendored
8
vendor/github.com/cyrilix/robocar-base/service/part.go
generated
vendored
@ -3,21 +3,21 @@ package service
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
mqtt "github.com/eclipse/paho.mqtt.golang"
|
mqtt "github.com/eclipse/paho.mqtt.golang"
|
||||||
"log"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
func StopService(name string, client mqtt.Client, topics ...string) {
|
func StopService(name string, client mqtt.Client, topics ...string) {
|
||||||
log.Printf("Stop %s service", name)
|
zap.S().Infof("Stop %s service", name)
|
||||||
token := client.Unsubscribe(topics...)
|
token := client.Unsubscribe(topics...)
|
||||||
token.Wait()
|
token.Wait()
|
||||||
if token.Error() != nil {
|
if token.Error() != nil {
|
||||||
log.Printf("unable to unsubscribe service: %v", token.Error())
|
zap.S().Errorf("unable to unsubscribe service: %v", token.Error())
|
||||||
}
|
}
|
||||||
client.Disconnect(50)
|
client.Disconnect(50)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RegisterCallback(client mqtt.Client, topic string, callback mqtt.MessageHandler) error {
|
func RegisterCallback(client mqtt.Client, topic string, callback mqtt.MessageHandler) error {
|
||||||
log.Printf("Register callback on topic %v", topic)
|
zap.S().Infof("Register callback on topic %v", topic)
|
||||||
token := client.Subscribe(topic, 0, callback)
|
token := client.Subscribe(topic, 0, callback)
|
||||||
token.Wait()
|
token.Wait()
|
||||||
if token.Error() != nil {
|
if token.Error() != nil {
|
||||||
|
2
vendor/github.com/cyrilix/robocar-protobuf/go/events/events.pb.go
generated
vendored
2
vendor/github.com/cyrilix/robocar-protobuf/go/events/events.pb.go
generated
vendored
@ -1,6 +1,6 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.26.0
|
// protoc-gen-go v1.27.1
|
||||||
// protoc v3.12.4
|
// protoc v3.12.4
|
||||||
// source: events/events.proto
|
// source: events/events.proto
|
||||||
|
|
||||||
|
12
vendor/github.com/disintegration/imaging/.travis.yml
generated
vendored
Normal file
12
vendor/github.com/disintegration/imaging/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
language: go
|
||||||
|
go:
|
||||||
|
- "1.10.x"
|
||||||
|
- "1.11.x"
|
||||||
|
- "1.12.x"
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- go get github.com/mattn/goveralls
|
||||||
|
|
||||||
|
script:
|
||||||
|
- go test -v -race -cover
|
||||||
|
- $GOPATH/bin/goveralls -service=travis-ci
|
@ -1,6 +1,6 @@
|
|||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2014 Simon Eskildsen
|
Copyright (c) 2012 Grigory Dryapak
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@ -9,13 +9,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||||||
copies of the Software, and to permit persons to whom the Software is
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
furnished to do so, subject to the following conditions:
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
The above copyright notice and this permission notice shall be included in all
|
||||||
all copies or substantial portions of the Software.
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
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
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
THE SOFTWARE.
|
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
|
||||||
|
}
|
4
vendor/github.com/sirupsen/logrus/.gitignore
generated
vendored
4
vendor/github.com/sirupsen/logrus/.gitignore
generated
vendored
@ -1,4 +0,0 @@
|
|||||||
logrus
|
|
||||||
vendor
|
|
||||||
|
|
||||||
.idea/
|
|
40
vendor/github.com/sirupsen/logrus/.golangci.yml
generated
vendored
40
vendor/github.com/sirupsen/logrus/.golangci.yml
generated
vendored
@ -1,40 +0,0 @@
|
|||||||
run:
|
|
||||||
# do not run on test files yet
|
|
||||||
tests: false
|
|
||||||
|
|
||||||
# all available settings of specific linters
|
|
||||||
linters-settings:
|
|
||||||
errcheck:
|
|
||||||
# report about not checking of errors in type assetions: `a := b.(MyStruct)`;
|
|
||||||
# default is false: such cases aren't reported by default.
|
|
||||||
check-type-assertions: false
|
|
||||||
|
|
||||||
# report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`;
|
|
||||||
# default is false: such cases aren't reported by default.
|
|
||||||
check-blank: false
|
|
||||||
|
|
||||||
lll:
|
|
||||||
line-length: 100
|
|
||||||
tab-width: 4
|
|
||||||
|
|
||||||
prealloc:
|
|
||||||
simple: false
|
|
||||||
range-loops: false
|
|
||||||
for-loops: false
|
|
||||||
|
|
||||||
whitespace:
|
|
||||||
multi-if: false # Enforces newlines (or comments) after every multi-line if statement
|
|
||||||
multi-func: false # Enforces newlines (or comments) after every multi-line function signature
|
|
||||||
|
|
||||||
linters:
|
|
||||||
enable:
|
|
||||||
- megacheck
|
|
||||||
- govet
|
|
||||||
disable:
|
|
||||||
- maligned
|
|
||||||
- prealloc
|
|
||||||
disable-all: false
|
|
||||||
presets:
|
|
||||||
- bugs
|
|
||||||
- unused
|
|
||||||
fast: false
|
|
15
vendor/github.com/sirupsen/logrus/.travis.yml
generated
vendored
15
vendor/github.com/sirupsen/logrus/.travis.yml
generated
vendored
@ -1,15 +0,0 @@
|
|||||||
language: go
|
|
||||||
go_import_path: github.com/sirupsen/logrus
|
|
||||||
git:
|
|
||||||
depth: 1
|
|
||||||
env:
|
|
||||||
- GO111MODULE=on
|
|
||||||
go: 1.15.x
|
|
||||||
os: linux
|
|
||||||
install:
|
|
||||||
- ./travis/install.sh
|
|
||||||
script:
|
|
||||||
- cd ci
|
|
||||||
- go run mage.go -v -w ../ crossBuild
|
|
||||||
- go run mage.go -v -w ../ lint
|
|
||||||
- go run mage.go -v -w ../ test
|
|
259
vendor/github.com/sirupsen/logrus/CHANGELOG.md
generated
vendored
259
vendor/github.com/sirupsen/logrus/CHANGELOG.md
generated
vendored
@ -1,259 +0,0 @@
|
|||||||
# 1.8.1
|
|
||||||
Code quality:
|
|
||||||
* move magefile in its own subdir/submodule to remove magefile dependency on logrus consumer
|
|
||||||
* improve timestamp format documentation
|
|
||||||
|
|
||||||
Fixes:
|
|
||||||
* fix race condition on logger hooks
|
|
||||||
|
|
||||||
|
|
||||||
# 1.8.0
|
|
||||||
|
|
||||||
Correct versioning number replacing v1.7.1.
|
|
||||||
|
|
||||||
# 1.7.1
|
|
||||||
|
|
||||||
Beware this release has introduced a new public API and its semver is therefore incorrect.
|
|
||||||
|
|
||||||
Code quality:
|
|
||||||
* use go 1.15 in travis
|
|
||||||
* use magefile as task runner
|
|
||||||
|
|
||||||
Fixes:
|
|
||||||
* small fixes about new go 1.13 error formatting system
|
|
||||||
* Fix for long time race condiction with mutating data hooks
|
|
||||||
|
|
||||||
Features:
|
|
||||||
* build support for zos
|
|
||||||
|
|
||||||
# 1.7.0
|
|
||||||
Fixes:
|
|
||||||
* the dependency toward a windows terminal library has been removed
|
|
||||||
|
|
||||||
Features:
|
|
||||||
* a new buffer pool management API has been added
|
|
||||||
* a set of `<LogLevel>Fn()` functions have been added
|
|
||||||
|
|
||||||
# 1.6.0
|
|
||||||
Fixes:
|
|
||||||
* end of line cleanup
|
|
||||||
* revert the entry concurrency bug fix whic leads to deadlock under some circumstances
|
|
||||||
* update dependency on go-windows-terminal-sequences to fix a crash with go 1.14
|
|
||||||
|
|
||||||
Features:
|
|
||||||
* add an option to the `TextFormatter` to completely disable fields quoting
|
|
||||||
|
|
||||||
# 1.5.0
|
|
||||||
Code quality:
|
|
||||||
* add golangci linter run on travis
|
|
||||||
|
|
||||||
Fixes:
|
|
||||||
* add mutex for hooks concurrent access on `Entry` data
|
|
||||||
* caller function field for go1.14
|
|
||||||
* fix build issue for gopherjs target
|
|
||||||
|
|
||||||
Feature:
|
|
||||||
* add an hooks/writer sub-package whose goal is to split output on different stream depending on the trace level
|
|
||||||
* add a `DisableHTMLEscape` option in the `JSONFormatter`
|
|
||||||
* add `ForceQuote` and `PadLevelText` options in the `TextFormatter`
|
|
||||||
|
|
||||||
# 1.4.2
|
|
||||||
* Fixes build break for plan9, nacl, solaris
|
|
||||||
# 1.4.1
|
|
||||||
This new release introduces:
|
|
||||||
* Enhance TextFormatter to not print caller information when they are empty (#944)
|
|
||||||
* Remove dependency on golang.org/x/crypto (#932, #943)
|
|
||||||
|
|
||||||
Fixes:
|
|
||||||
* Fix Entry.WithContext method to return a copy of the initial entry (#941)
|
|
||||||
|
|
||||||
# 1.4.0
|
|
||||||
This new release introduces:
|
|
||||||
* Add `DeferExitHandler`, similar to `RegisterExitHandler` but prepending the handler to the list of handlers (semantically like `defer`) (#848).
|
|
||||||
* Add `CallerPrettyfier` to `JSONFormatter` and `TextFormatter` (#909, #911)
|
|
||||||
* Add `Entry.WithContext()` and `Entry.Context`, to set a context on entries to be used e.g. in hooks (#919).
|
|
||||||
|
|
||||||
Fixes:
|
|
||||||
* Fix wrong method calls `Logger.Print` and `Logger.Warningln` (#893).
|
|
||||||
* Update `Entry.Logf` to not do string formatting unless the log level is enabled (#903)
|
|
||||||
* Fix infinite recursion on unknown `Level.String()` (#907)
|
|
||||||
* Fix race condition in `getCaller` (#916).
|
|
||||||
|
|
||||||
|
|
||||||
# 1.3.0
|
|
||||||
This new release introduces:
|
|
||||||
* Log, Logf, Logln functions for Logger and Entry that take a Level
|
|
||||||
|
|
||||||
Fixes:
|
|
||||||
* Building prometheus node_exporter on AIX (#840)
|
|
||||||
* Race condition in TextFormatter (#468)
|
|
||||||
* Travis CI import path (#868)
|
|
||||||
* Remove coloured output on Windows (#862)
|
|
||||||
* Pointer to func as field in JSONFormatter (#870)
|
|
||||||
* Properly marshal Levels (#873)
|
|
||||||
|
|
||||||
# 1.2.0
|
|
||||||
This new release introduces:
|
|
||||||
* A new method `SetReportCaller` in the `Logger` to enable the file, line and calling function from which the trace has been issued
|
|
||||||
* A new trace level named `Trace` whose level is below `Debug`
|
|
||||||
* A configurable exit function to be called upon a Fatal trace
|
|
||||||
* The `Level` object now implements `encoding.TextUnmarshaler` interface
|
|
||||||
|
|
||||||
# 1.1.1
|
|
||||||
This is a bug fix release.
|
|
||||||
* fix the build break on Solaris
|
|
||||||
* don't drop a whole trace in JSONFormatter when a field param is a function pointer which can not be serialized
|
|
||||||
|
|
||||||
# 1.1.0
|
|
||||||
This new release introduces:
|
|
||||||
* several fixes:
|
|
||||||
* a fix for a race condition on entry formatting
|
|
||||||
* proper cleanup of previously used entries before putting them back in the pool
|
|
||||||
* the extra new line at the end of message in text formatter has been removed
|
|
||||||
* a new global public API to check if a level is activated: IsLevelEnabled
|
|
||||||
* the following methods have been added to the Logger object
|
|
||||||
* IsLevelEnabled
|
|
||||||
* SetFormatter
|
|
||||||
* SetOutput
|
|
||||||
* ReplaceHooks
|
|
||||||
* introduction of go module
|
|
||||||
* an indent configuration for the json formatter
|
|
||||||
* output colour support for windows
|
|
||||||
* the field sort function is now configurable for text formatter
|
|
||||||
* the CLICOLOR and CLICOLOR\_FORCE environment variable support in text formater
|
|
||||||
|
|
||||||
# 1.0.6
|
|
||||||
|
|
||||||
This new release introduces:
|
|
||||||
* a new api WithTime which allows to easily force the time of the log entry
|
|
||||||
which is mostly useful for logger wrapper
|
|
||||||
* a fix reverting the immutability of the entry given as parameter to the hooks
|
|
||||||
a new configuration field of the json formatter in order to put all the fields
|
|
||||||
in a nested dictionnary
|
|
||||||
* a new SetOutput method in the Logger
|
|
||||||
* a new configuration of the textformatter to configure the name of the default keys
|
|
||||||
* a new configuration of the text formatter to disable the level truncation
|
|
||||||
|
|
||||||
# 1.0.5
|
|
||||||
|
|
||||||
* Fix hooks race (#707)
|
|
||||||
* Fix panic deadlock (#695)
|
|
||||||
|
|
||||||
# 1.0.4
|
|
||||||
|
|
||||||
* Fix race when adding hooks (#612)
|
|
||||||
* Fix terminal check in AppEngine (#635)
|
|
||||||
|
|
||||||
# 1.0.3
|
|
||||||
|
|
||||||
* Replace example files with testable examples
|
|
||||||
|
|
||||||
# 1.0.2
|
|
||||||
|
|
||||||
* bug: quote non-string values in text formatter (#583)
|
|
||||||
* Make (*Logger) SetLevel a public method
|
|
||||||
|
|
||||||
# 1.0.1
|
|
||||||
|
|
||||||
* bug: fix escaping in text formatter (#575)
|
|
||||||
|
|
||||||
# 1.0.0
|
|
||||||
|
|
||||||
* Officially changed name to lower-case
|
|
||||||
* bug: colors on Windows 10 (#541)
|
|
||||||
* bug: fix race in accessing level (#512)
|
|
||||||
|
|
||||||
# 0.11.5
|
|
||||||
|
|
||||||
* feature: add writer and writerlevel to entry (#372)
|
|
||||||
|
|
||||||
# 0.11.4
|
|
||||||
|
|
||||||
* bug: fix undefined variable on solaris (#493)
|
|
||||||
|
|
||||||
# 0.11.3
|
|
||||||
|
|
||||||
* formatter: configure quoting of empty values (#484)
|
|
||||||
* formatter: configure quoting character (default is `"`) (#484)
|
|
||||||
* bug: fix not importing io correctly in non-linux environments (#481)
|
|
||||||
|
|
||||||
# 0.11.2
|
|
||||||
|
|
||||||
* bug: fix windows terminal detection (#476)
|
|
||||||
|
|
||||||
# 0.11.1
|
|
||||||
|
|
||||||
* bug: fix tty detection with custom out (#471)
|
|
||||||
|
|
||||||
# 0.11.0
|
|
||||||
|
|
||||||
* performance: Use bufferpool to allocate (#370)
|
|
||||||
* terminal: terminal detection for app-engine (#343)
|
|
||||||
* feature: exit handler (#375)
|
|
||||||
|
|
||||||
# 0.10.0
|
|
||||||
|
|
||||||
* feature: Add a test hook (#180)
|
|
||||||
* feature: `ParseLevel` is now case-insensitive (#326)
|
|
||||||
* feature: `FieldLogger` interface that generalizes `Logger` and `Entry` (#308)
|
|
||||||
* performance: avoid re-allocations on `WithFields` (#335)
|
|
||||||
|
|
||||||
# 0.9.0
|
|
||||||
|
|
||||||
* logrus/text_formatter: don't emit empty msg
|
|
||||||
* logrus/hooks/airbrake: move out of main repository
|
|
||||||
* logrus/hooks/sentry: move out of main repository
|
|
||||||
* logrus/hooks/papertrail: move out of main repository
|
|
||||||
* logrus/hooks/bugsnag: move out of main repository
|
|
||||||
* logrus/core: run tests with `-race`
|
|
||||||
* logrus/core: detect TTY based on `stderr`
|
|
||||||
* logrus/core: support `WithError` on logger
|
|
||||||
* logrus/core: Solaris support
|
|
||||||
|
|
||||||
# 0.8.7
|
|
||||||
|
|
||||||
* logrus/core: fix possible race (#216)
|
|
||||||
* logrus/doc: small typo fixes and doc improvements
|
|
||||||
|
|
||||||
|
|
||||||
# 0.8.6
|
|
||||||
|
|
||||||
* hooks/raven: allow passing an initialized client
|
|
||||||
|
|
||||||
# 0.8.5
|
|
||||||
|
|
||||||
* logrus/core: revert #208
|
|
||||||
|
|
||||||
# 0.8.4
|
|
||||||
|
|
||||||
* formatter/text: fix data race (#218)
|
|
||||||
|
|
||||||
# 0.8.3
|
|
||||||
|
|
||||||
* logrus/core: fix entry log level (#208)
|
|
||||||
* logrus/core: improve performance of text formatter by 40%
|
|
||||||
* logrus/core: expose `LevelHooks` type
|
|
||||||
* logrus/core: add support for DragonflyBSD and NetBSD
|
|
||||||
* formatter/text: print structs more verbosely
|
|
||||||
|
|
||||||
# 0.8.2
|
|
||||||
|
|
||||||
* logrus: fix more Fatal family functions
|
|
||||||
|
|
||||||
# 0.8.1
|
|
||||||
|
|
||||||
* logrus: fix not exiting on `Fatalf` and `Fatalln`
|
|
||||||
|
|
||||||
# 0.8.0
|
|
||||||
|
|
||||||
* logrus: defaults to stderr instead of stdout
|
|
||||||
* hooks/sentry: add special field for `*http.Request`
|
|
||||||
* formatter/text: ignore Windows for colors
|
|
||||||
|
|
||||||
# 0.7.3
|
|
||||||
|
|
||||||
* formatter/\*: allow configuration of timestamp layout
|
|
||||||
|
|
||||||
# 0.7.2
|
|
||||||
|
|
||||||
* formatter/text: Add configuration option for time format (#158)
|
|
513
vendor/github.com/sirupsen/logrus/README.md
generated
vendored
513
vendor/github.com/sirupsen/logrus/README.md
generated
vendored
@ -1,513 +0,0 @@
|
|||||||
# Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:"/> [](https://travis-ci.org/sirupsen/logrus) [](https://godoc.org/github.com/sirupsen/logrus)
|
|
||||||
|
|
||||||
Logrus is a structured logger for Go (golang), completely API compatible with
|
|
||||||
the standard library logger.
|
|
||||||
|
|
||||||
**Logrus is in maintenance-mode.** We will not be introducing new features. It's
|
|
||||||
simply too hard to do in a way that won't break many people's projects, which is
|
|
||||||
the last thing you want from your Logging library (again...).
|
|
||||||
|
|
||||||
This does not mean Logrus is dead. Logrus will continue to be maintained for
|
|
||||||
security, (backwards compatible) bug fixes, and performance (where we are
|
|
||||||
limited by the interface).
|
|
||||||
|
|
||||||
I believe Logrus' biggest contribution is to have played a part in today's
|
|
||||||
widespread use of structured logging in Golang. There doesn't seem to be a
|
|
||||||
reason to do a major, breaking iteration into Logrus V2, since the fantastic Go
|
|
||||||
community has built those independently. Many fantastic alternatives have sprung
|
|
||||||
up. Logrus would look like those, had it been re-designed with what we know
|
|
||||||
about structured logging in Go today. Check out, for example,
|
|
||||||
[Zerolog][zerolog], [Zap][zap], and [Apex][apex].
|
|
||||||
|
|
||||||
[zerolog]: https://github.com/rs/zerolog
|
|
||||||
[zap]: https://github.com/uber-go/zap
|
|
||||||
[apex]: https://github.com/apex/log
|
|
||||||
|
|
||||||
**Seeing weird case-sensitive problems?** It's in the past been possible to
|
|
||||||
import Logrus as both upper- and lower-case. Due to the Go package environment,
|
|
||||||
this caused issues in the community and we needed a standard. Some environments
|
|
||||||
experienced problems with the upper-case variant, so the lower-case was decided.
|
|
||||||
Everything using `logrus` will need to use the lower-case:
|
|
||||||
`github.com/sirupsen/logrus`. Any package that isn't, should be changed.
|
|
||||||
|
|
||||||
To fix Glide, see [these
|
|
||||||
comments](https://github.com/sirupsen/logrus/issues/553#issuecomment-306591437).
|
|
||||||
For an in-depth explanation of the casing issue, see [this
|
|
||||||
comment](https://github.com/sirupsen/logrus/issues/570#issuecomment-313933276).
|
|
||||||
|
|
||||||
Nicely color-coded in development (when a TTY is attached, otherwise just
|
|
||||||
plain text):
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
With `log.SetFormatter(&log.JSONFormatter{})`, for easy parsing by logstash
|
|
||||||
or Splunk:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{"animal":"walrus","level":"info","msg":"A group of walrus emerges from the
|
|
||||||
ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"}
|
|
||||||
|
|
||||||
{"level":"warning","msg":"The group's number increased tremendously!",
|
|
||||||
"number":122,"omg":true,"time":"2014-03-10 19:57:38.562471297 -0400 EDT"}
|
|
||||||
|
|
||||||
{"animal":"walrus","level":"info","msg":"A giant walrus appears!",
|
|
||||||
"size":10,"time":"2014-03-10 19:57:38.562500591 -0400 EDT"}
|
|
||||||
|
|
||||||
{"animal":"walrus","level":"info","msg":"Tremendously sized cow enters the ocean.",
|
|
||||||
"size":9,"time":"2014-03-10 19:57:38.562527896 -0400 EDT"}
|
|
||||||
|
|
||||||
{"level":"fatal","msg":"The ice breaks!","number":100,"omg":true,
|
|
||||||
"time":"2014-03-10 19:57:38.562543128 -0400 EDT"}
|
|
||||||
```
|
|
||||||
|
|
||||||
With the default `log.SetFormatter(&log.TextFormatter{})` when a TTY is not
|
|
||||||
attached, the output is compatible with the
|
|
||||||
[logfmt](http://godoc.org/github.com/kr/logfmt) format:
|
|
||||||
|
|
||||||
```text
|
|
||||||
time="2015-03-26T01:27:38-04:00" level=debug msg="Started observing beach" animal=walrus number=8
|
|
||||||
time="2015-03-26T01:27:38-04:00" level=info msg="A group of walrus emerges from the ocean" animal=walrus size=10
|
|
||||||
time="2015-03-26T01:27:38-04:00" level=warning msg="The group's number increased tremendously!" number=122 omg=true
|
|
||||||
time="2015-03-26T01:27:38-04:00" level=debug msg="Temperature changes" temperature=-4
|
|
||||||
time="2015-03-26T01:27:38-04:00" level=panic msg="It's over 9000!" animal=orca size=9009
|
|
||||||
time="2015-03-26T01:27:38-04:00" level=fatal msg="The ice breaks!" err=&{0x2082280c0 map[animal:orca size:9009] 2015-03-26 01:27:38.441574009 -0400 EDT panic It's over 9000!} number=100 omg=true
|
|
||||||
```
|
|
||||||
To ensure this behaviour even if a TTY is attached, set your formatter as follows:
|
|
||||||
|
|
||||||
```go
|
|
||||||
log.SetFormatter(&log.TextFormatter{
|
|
||||||
DisableColors: true,
|
|
||||||
FullTimestamp: true,
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Logging Method Name
|
|
||||||
|
|
||||||
If you wish to add the calling method as a field, instruct the logger via:
|
|
||||||
```go
|
|
||||||
log.SetReportCaller(true)
|
|
||||||
```
|
|
||||||
This adds the caller as 'method' like so:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{"animal":"penguin","level":"fatal","method":"github.com/sirupsen/arcticcreatures.migrate","msg":"a penguin swims by",
|
|
||||||
"time":"2014-03-10 19:57:38.562543129 -0400 EDT"}
|
|
||||||
```
|
|
||||||
|
|
||||||
```text
|
|
||||||
time="2015-03-26T01:27:38-04:00" level=fatal method=github.com/sirupsen/arcticcreatures.migrate msg="a penguin swims by" animal=penguin
|
|
||||||
```
|
|
||||||
Note that this does add measurable overhead - the cost will depend on the version of Go, but is
|
|
||||||
between 20 and 40% in recent tests with 1.6 and 1.7. You can validate this in your
|
|
||||||
environment via benchmarks:
|
|
||||||
```
|
|
||||||
go test -bench=.*CallerTracing
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
#### Case-sensitivity
|
|
||||||
|
|
||||||
The organization's name was changed to lower-case--and this will not be changed
|
|
||||||
back. If you are getting import conflicts due to case sensitivity, please use
|
|
||||||
the lower-case import: `github.com/sirupsen/logrus`.
|
|
||||||
|
|
||||||
#### Example
|
|
||||||
|
|
||||||
The simplest way to use Logrus is simply the package-level exported logger:
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
log.WithFields(log.Fields{
|
|
||||||
"animal": "walrus",
|
|
||||||
}).Info("A walrus appears")
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that it's completely api-compatible with the stdlib logger, so you can
|
|
||||||
replace your `log` imports everywhere with `log "github.com/sirupsen/logrus"`
|
|
||||||
and you'll now have the flexibility of Logrus. You can customize it all you
|
|
||||||
want:
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
// Log as JSON instead of the default ASCII formatter.
|
|
||||||
log.SetFormatter(&log.JSONFormatter{})
|
|
||||||
|
|
||||||
// Output to stdout instead of the default stderr
|
|
||||||
// Can be any io.Writer, see below for File example
|
|
||||||
log.SetOutput(os.Stdout)
|
|
||||||
|
|
||||||
// Only log the warning severity or above.
|
|
||||||
log.SetLevel(log.WarnLevel)
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
log.WithFields(log.Fields{
|
|
||||||
"animal": "walrus",
|
|
||||||
"size": 10,
|
|
||||||
}).Info("A group of walrus emerges from the ocean")
|
|
||||||
|
|
||||||
log.WithFields(log.Fields{
|
|
||||||
"omg": true,
|
|
||||||
"number": 122,
|
|
||||||
}).Warn("The group's number increased tremendously!")
|
|
||||||
|
|
||||||
log.WithFields(log.Fields{
|
|
||||||
"omg": true,
|
|
||||||
"number": 100,
|
|
||||||
}).Fatal("The ice breaks!")
|
|
||||||
|
|
||||||
// A common pattern is to re-use fields between logging statements by re-using
|
|
||||||
// the logrus.Entry returned from WithFields()
|
|
||||||
contextLogger := log.WithFields(log.Fields{
|
|
||||||
"common": "this is a common field",
|
|
||||||
"other": "I also should be logged always",
|
|
||||||
})
|
|
||||||
|
|
||||||
contextLogger.Info("I'll be logged with common and other field")
|
|
||||||
contextLogger.Info("Me too")
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
For more advanced usage such as logging to multiple locations from the same
|
|
||||||
application, you can also create an instance of the `logrus` Logger:
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Create a new instance of the logger. You can have any number of instances.
|
|
||||||
var log = logrus.New()
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
// The API for setting attributes is a little different than the package level
|
|
||||||
// exported logger. See Godoc.
|
|
||||||
log.Out = os.Stdout
|
|
||||||
|
|
||||||
// You could set this to any `io.Writer` such as a file
|
|
||||||
// file, err := os.OpenFile("logrus.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
|
||||||
// if err == nil {
|
|
||||||
// log.Out = file
|
|
||||||
// } else {
|
|
||||||
// log.Info("Failed to log to file, using default stderr")
|
|
||||||
// }
|
|
||||||
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"animal": "walrus",
|
|
||||||
"size": 10,
|
|
||||||
}).Info("A group of walrus emerges from the ocean")
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Fields
|
|
||||||
|
|
||||||
Logrus encourages careful, structured logging through logging fields instead of
|
|
||||||
long, unparseable error messages. For example, instead of: `log.Fatalf("Failed
|
|
||||||
to send event %s to topic %s with key %d")`, you should log the much more
|
|
||||||
discoverable:
|
|
||||||
|
|
||||||
```go
|
|
||||||
log.WithFields(log.Fields{
|
|
||||||
"event": event,
|
|
||||||
"topic": topic,
|
|
||||||
"key": key,
|
|
||||||
}).Fatal("Failed to send event")
|
|
||||||
```
|
|
||||||
|
|
||||||
We've found this API forces you to think about logging in a way that produces
|
|
||||||
much more useful logging messages. We've been in countless situations where just
|
|
||||||
a single added field to a log statement that was already there would've saved us
|
|
||||||
hours. The `WithFields` call is optional.
|
|
||||||
|
|
||||||
In general, with Logrus using any of the `printf`-family functions should be
|
|
||||||
seen as a hint you should add a field, however, you can still use the
|
|
||||||
`printf`-family functions with Logrus.
|
|
||||||
|
|
||||||
#### Default Fields
|
|
||||||
|
|
||||||
Often it's helpful to have fields _always_ attached to log statements in an
|
|
||||||
application or parts of one. For example, you may want to always log the
|
|
||||||
`request_id` and `user_ip` in the context of a request. Instead of writing
|
|
||||||
`log.WithFields(log.Fields{"request_id": request_id, "user_ip": user_ip})` on
|
|
||||||
every line, you can create a `logrus.Entry` to pass around instead:
|
|
||||||
|
|
||||||
```go
|
|
||||||
requestLogger := log.WithFields(log.Fields{"request_id": request_id, "user_ip": user_ip})
|
|
||||||
requestLogger.Info("something happened on that request") # will log request_id and user_ip
|
|
||||||
requestLogger.Warn("something not great happened")
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Hooks
|
|
||||||
|
|
||||||
You can add hooks for logging levels. For example to send errors to an exception
|
|
||||||
tracking service on `Error`, `Fatal` and `Panic`, info to StatsD or log to
|
|
||||||
multiple places simultaneously, e.g. syslog.
|
|
||||||
|
|
||||||
Logrus comes with [built-in hooks](hooks/). Add those, or your custom hook, in
|
|
||||||
`init`:
|
|
||||||
|
|
||||||
```go
|
|
||||||
import (
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"gopkg.in/gemnasium/logrus-airbrake-hook.v2" // the package is named "airbrake"
|
|
||||||
logrus_syslog "github.com/sirupsen/logrus/hooks/syslog"
|
|
||||||
"log/syslog"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
|
|
||||||
// Use the Airbrake hook to report errors that have Error severity or above to
|
|
||||||
// an exception tracker. You can create custom hooks, see the Hooks section.
|
|
||||||
log.AddHook(airbrake.NewHook(123, "xyz", "production"))
|
|
||||||
|
|
||||||
hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Unable to connect to local syslog daemon")
|
|
||||||
} else {
|
|
||||||
log.AddHook(hook)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). For the detail, please check the [syslog hook README](hooks/syslog/README.md).
|
|
||||||
|
|
||||||
A list of currently known service hooks can be found in this wiki [page](https://github.com/sirupsen/logrus/wiki/Hooks)
|
|
||||||
|
|
||||||
|
|
||||||
#### Level logging
|
|
||||||
|
|
||||||
Logrus has seven logging levels: Trace, Debug, Info, Warning, Error, Fatal and Panic.
|
|
||||||
|
|
||||||
```go
|
|
||||||
log.Trace("Something very low level.")
|
|
||||||
log.Debug("Useful debugging information.")
|
|
||||||
log.Info("Something noteworthy happened!")
|
|
||||||
log.Warn("You should probably take a look at this.")
|
|
||||||
log.Error("Something failed but I'm not quitting.")
|
|
||||||
// Calls os.Exit(1) after logging
|
|
||||||
log.Fatal("Bye.")
|
|
||||||
// Calls panic() after logging
|
|
||||||
log.Panic("I'm bailing.")
|
|
||||||
```
|
|
||||||
|
|
||||||
You can set the logging level on a `Logger`, then it will only log entries with
|
|
||||||
that severity or anything above it:
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Will log anything that is info or above (warn, error, fatal, panic). Default.
|
|
||||||
log.SetLevel(log.InfoLevel)
|
|
||||||
```
|
|
||||||
|
|
||||||
It may be useful to set `log.Level = logrus.DebugLevel` in a debug or verbose
|
|
||||||
environment if your application has that.
|
|
||||||
|
|
||||||
#### Entries
|
|
||||||
|
|
||||||
Besides the fields added with `WithField` or `WithFields` some fields are
|
|
||||||
automatically added to all logging events:
|
|
||||||
|
|
||||||
1. `time`. The timestamp when the entry was created.
|
|
||||||
2. `msg`. The logging message passed to `{Info,Warn,Error,Fatal,Panic}` after
|
|
||||||
the `AddFields` call. E.g. `Failed to send event.`
|
|
||||||
3. `level`. The logging level. E.g. `info`.
|
|
||||||
|
|
||||||
#### Environments
|
|
||||||
|
|
||||||
Logrus has no notion of environment.
|
|
||||||
|
|
||||||
If you wish for hooks and formatters to only be used in specific environments,
|
|
||||||
you should handle that yourself. For example, if your application has a global
|
|
||||||
variable `Environment`, which is a string representation of the environment you
|
|
||||||
could do:
|
|
||||||
|
|
||||||
```go
|
|
||||||
import (
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
init() {
|
|
||||||
// do something here to set environment depending on an environment variable
|
|
||||||
// or command-line flag
|
|
||||||
if Environment == "production" {
|
|
||||||
log.SetFormatter(&log.JSONFormatter{})
|
|
||||||
} else {
|
|
||||||
// The TextFormatter is default, you don't actually have to do this.
|
|
||||||
log.SetFormatter(&log.TextFormatter{})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
This configuration is how `logrus` was intended to be used, but JSON in
|
|
||||||
production is mostly only useful if you do log aggregation with tools like
|
|
||||||
Splunk or Logstash.
|
|
||||||
|
|
||||||
#### Formatters
|
|
||||||
|
|
||||||
The built-in logging formatters are:
|
|
||||||
|
|
||||||
* `logrus.TextFormatter`. Logs the event in colors if stdout is a tty, otherwise
|
|
||||||
without colors.
|
|
||||||
* *Note:* to force colored output when there is no TTY, set the `ForceColors`
|
|
||||||
field to `true`. To force no colored output even if there is a TTY set the
|
|
||||||
`DisableColors` field to `true`. For Windows, see
|
|
||||||
[github.com/mattn/go-colorable](https://github.com/mattn/go-colorable).
|
|
||||||
* When colors are enabled, levels are truncated to 4 characters by default. To disable
|
|
||||||
truncation set the `DisableLevelTruncation` field to `true`.
|
|
||||||
* When outputting to a TTY, it's often helpful to visually scan down a column where all the levels are the same width. Setting the `PadLevelText` field to `true` enables this behavior, by adding padding to the level text.
|
|
||||||
* All options are listed in the [generated docs](https://godoc.org/github.com/sirupsen/logrus#TextFormatter).
|
|
||||||
* `logrus.JSONFormatter`. Logs fields as JSON.
|
|
||||||
* All options are listed in the [generated docs](https://godoc.org/github.com/sirupsen/logrus#JSONFormatter).
|
|
||||||
|
|
||||||
Third party logging formatters:
|
|
||||||
|
|
||||||
* [`FluentdFormatter`](https://github.com/joonix/log). Formats entries that can be parsed by Kubernetes and Google Container Engine.
|
|
||||||
* [`GELF`](https://github.com/fabienm/go-logrus-formatters). Formats entries so they comply to Graylog's [GELF 1.1 specification](http://docs.graylog.org/en/2.4/pages/gelf.html).
|
|
||||||
* [`logstash`](https://github.com/bshuster-repo/logrus-logstash-hook). Logs fields as [Logstash](http://logstash.net) Events.
|
|
||||||
* [`prefixed`](https://github.com/x-cray/logrus-prefixed-formatter). Displays log entry source along with alternative layout.
|
|
||||||
* [`zalgo`](https://github.com/aybabtme/logzalgo). Invoking the Power of Zalgo.
|
|
||||||
* [`nested-logrus-formatter`](https://github.com/antonfisher/nested-logrus-formatter). Converts logrus fields to a nested structure.
|
|
||||||
* [`powerful-logrus-formatter`](https://github.com/zput/zxcTool). get fileName, log's line number and the latest function's name when print log; Sava log to files.
|
|
||||||
* [`caption-json-formatter`](https://github.com/nolleh/caption_json_formatter). logrus's message json formatter with human-readable caption added.
|
|
||||||
|
|
||||||
You can define your formatter by implementing the `Formatter` interface,
|
|
||||||
requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a
|
|
||||||
`Fields` type (`map[string]interface{}`) with all your fields as well as the
|
|
||||||
default ones (see Entries section above):
|
|
||||||
|
|
||||||
```go
|
|
||||||
type MyJSONFormatter struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
log.SetFormatter(new(MyJSONFormatter))
|
|
||||||
|
|
||||||
func (f *MyJSONFormatter) Format(entry *Entry) ([]byte, error) {
|
|
||||||
// Note this doesn't include Time, Level and Message which are available on
|
|
||||||
// the Entry. Consult `godoc` on information about those fields or read the
|
|
||||||
// source of the official loggers.
|
|
||||||
serialized, err := json.Marshal(entry.Data)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Failed to marshal fields to JSON, %w", err)
|
|
||||||
}
|
|
||||||
return append(serialized, '\n'), nil
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Logger as an `io.Writer`
|
|
||||||
|
|
||||||
Logrus can be transformed into an `io.Writer`. That writer is the end of an `io.Pipe` and it is your responsibility to close it.
|
|
||||||
|
|
||||||
```go
|
|
||||||
w := logger.Writer()
|
|
||||||
defer w.Close()
|
|
||||||
|
|
||||||
srv := http.Server{
|
|
||||||
// create a stdlib log.Logger that writes to
|
|
||||||
// logrus.Logger.
|
|
||||||
ErrorLog: log.New(w, "", 0),
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Each line written to that writer will be printed the usual way, using formatters
|
|
||||||
and hooks. The level for those entries is `info`.
|
|
||||||
|
|
||||||
This means that we can override the standard library logger easily:
|
|
||||||
|
|
||||||
```go
|
|
||||||
logger := logrus.New()
|
|
||||||
logger.Formatter = &logrus.JSONFormatter{}
|
|
||||||
|
|
||||||
// Use logrus for standard log output
|
|
||||||
// Note that `log` here references stdlib's log
|
|
||||||
// Not logrus imported under the name `log`.
|
|
||||||
log.SetOutput(logger.Writer())
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Rotation
|
|
||||||
|
|
||||||
Log rotation is not provided with Logrus. Log rotation should be done by an
|
|
||||||
external program (like `logrotate(8)`) that can compress and delete old log
|
|
||||||
entries. It should not be a feature of the application-level logger.
|
|
||||||
|
|
||||||
#### Tools
|
|
||||||
|
|
||||||
| Tool | Description |
|
|
||||||
| ---- | ----------- |
|
|
||||||
|[Logrus Mate](https://github.com/gogap/logrus_mate)|Logrus mate is a tool for Logrus to manage loggers, you can initial logger's level, hook and formatter by config file, the logger will be generated with different configs in different environments.|
|
|
||||||
|[Logrus Viper Helper](https://github.com/heirko/go-contrib/tree/master/logrusHelper)|An Helper around Logrus to wrap with spf13/Viper to load configuration with fangs! And to simplify Logrus configuration use some behavior of [Logrus Mate](https://github.com/gogap/logrus_mate). [sample](https://github.com/heirko/iris-contrib/blob/master/middleware/logrus-logger/example) |
|
|
||||||
|
|
||||||
#### Testing
|
|
||||||
|
|
||||||
Logrus has a built in facility for asserting the presence of log messages. This is implemented through the `test` hook and provides:
|
|
||||||
|
|
||||||
* decorators for existing logger (`test.NewLocal` and `test.NewGlobal`) which basically just adds the `test` hook
|
|
||||||
* a test logger (`test.NewNullLogger`) that just records log messages (and does not output any):
|
|
||||||
|
|
||||||
```go
|
|
||||||
import(
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"github.com/sirupsen/logrus/hooks/test"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSomething(t*testing.T){
|
|
||||||
logger, hook := test.NewNullLogger()
|
|
||||||
logger.Error("Helloerror")
|
|
||||||
|
|
||||||
assert.Equal(t, 1, len(hook.Entries))
|
|
||||||
assert.Equal(t, logrus.ErrorLevel, hook.LastEntry().Level)
|
|
||||||
assert.Equal(t, "Helloerror", hook.LastEntry().Message)
|
|
||||||
|
|
||||||
hook.Reset()
|
|
||||||
assert.Nil(t, hook.LastEntry())
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Fatal handlers
|
|
||||||
|
|
||||||
Logrus can register one or more functions that will be called when any `fatal`
|
|
||||||
level message is logged. The registered handlers will be executed before
|
|
||||||
logrus performs an `os.Exit(1)`. This behavior may be helpful if callers need
|
|
||||||
to gracefully shutdown. Unlike a `panic("Something went wrong...")` call which can be intercepted with a deferred `recover` a call to `os.Exit(1)` can not be intercepted.
|
|
||||||
|
|
||||||
```
|
|
||||||
...
|
|
||||||
handler := func() {
|
|
||||||
// gracefully shutdown something...
|
|
||||||
}
|
|
||||||
logrus.RegisterExitHandler(handler)
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Thread safety
|
|
||||||
|
|
||||||
By default, Logger is protected by a mutex for concurrent writes. The mutex is held when calling hooks and writing logs.
|
|
||||||
If you are sure such locking is not needed, you can call logger.SetNoLock() to disable the locking.
|
|
||||||
|
|
||||||
Situation when locking is not needed includes:
|
|
||||||
|
|
||||||
* You have no hooks registered, or hooks calling is already thread-safe.
|
|
||||||
|
|
||||||
* Writing to logger.Out is already thread-safe, for example:
|
|
||||||
|
|
||||||
1) logger.Out is protected by locks.
|
|
||||||
|
|
||||||
2) logger.Out is an os.File handler opened with `O_APPEND` flag, and every write is smaller than 4k. (This allows multi-thread/multi-process writing)
|
|
||||||
|
|
||||||
(Refer to http://www.notthewizard.com/2014/06/17/are-files-appends-really-atomic/)
|
|
76
vendor/github.com/sirupsen/logrus/alt_exit.go
generated
vendored
76
vendor/github.com/sirupsen/logrus/alt_exit.go
generated
vendored
@ -1,76 +0,0 @@
|
|||||||
package logrus
|
|
||||||
|
|
||||||
// The following code was sourced and modified from the
|
|
||||||
// https://github.com/tebeka/atexit package governed by the following license:
|
|
||||||
//
|
|
||||||
// Copyright (c) 2012 Miki Tebeka <miki.tebeka@gmail.com>.
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
||||||
// this software and associated documentation files (the "Software"), to deal in
|
|
||||||
// the Software without restriction, including without limitation the rights to
|
|
||||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
||||||
// the Software, and to permit persons to whom the Software is furnished to do so,
|
|
||||||
// subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in all
|
|
||||||
// copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
||||||
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
||||||
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
||||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
var handlers = []func(){}
|
|
||||||
|
|
||||||
func runHandler(handler func()) {
|
|
||||||
defer func() {
|
|
||||||
if err := recover(); err != nil {
|
|
||||||
fmt.Fprintln(os.Stderr, "Error: Logrus exit handler error:", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
handler()
|
|
||||||
}
|
|
||||||
|
|
||||||
func runHandlers() {
|
|
||||||
for _, handler := range handlers {
|
|
||||||
runHandler(handler)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exit runs all the Logrus atexit handlers and then terminates the program using os.Exit(code)
|
|
||||||
func Exit(code int) {
|
|
||||||
runHandlers()
|
|
||||||
os.Exit(code)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterExitHandler appends a Logrus Exit handler to the list of handlers,
|
|
||||||
// call logrus.Exit to invoke all handlers. The handlers will also be invoked when
|
|
||||||
// any Fatal log entry is made.
|
|
||||||
//
|
|
||||||
// This method is useful when a caller wishes to use logrus to log a fatal
|
|
||||||
// message but also needs to gracefully shutdown. An example usecase could be
|
|
||||||
// closing database connections, or sending a alert that the application is
|
|
||||||
// closing.
|
|
||||||
func RegisterExitHandler(handler func()) {
|
|
||||||
handlers = append(handlers, handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeferExitHandler prepends a Logrus Exit handler to the list of handlers,
|
|
||||||
// call logrus.Exit to invoke all handlers. The handlers will also be invoked when
|
|
||||||
// any Fatal log entry is made.
|
|
||||||
//
|
|
||||||
// This method is useful when a caller wishes to use logrus to log a fatal
|
|
||||||
// message but also needs to gracefully shutdown. An example usecase could be
|
|
||||||
// closing database connections, or sending a alert that the application is
|
|
||||||
// closing.
|
|
||||||
func DeferExitHandler(handler func()) {
|
|
||||||
handlers = append([]func(){handler}, handlers...)
|
|
||||||
}
|
|
14
vendor/github.com/sirupsen/logrus/appveyor.yml
generated
vendored
14
vendor/github.com/sirupsen/logrus/appveyor.yml
generated
vendored
@ -1,14 +0,0 @@
|
|||||||
version: "{build}"
|
|
||||||
platform: x64
|
|
||||||
clone_folder: c:\gopath\src\github.com\sirupsen\logrus
|
|
||||||
environment:
|
|
||||||
GOPATH: c:\gopath
|
|
||||||
branches:
|
|
||||||
only:
|
|
||||||
- master
|
|
||||||
install:
|
|
||||||
- set PATH=%GOPATH%\bin;c:\go\bin;%PATH%
|
|
||||||
- go version
|
|
||||||
build_script:
|
|
||||||
- go get -t
|
|
||||||
- go test
|
|
52
vendor/github.com/sirupsen/logrus/buffer_pool.go
generated
vendored
52
vendor/github.com/sirupsen/logrus/buffer_pool.go
generated
vendored
@ -1,52 +0,0 @@
|
|||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
bufferPool BufferPool
|
|
||||||
)
|
|
||||||
|
|
||||||
type BufferPool interface {
|
|
||||||
Put(*bytes.Buffer)
|
|
||||||
Get() *bytes.Buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
type defaultPool struct {
|
|
||||||
pool *sync.Pool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *defaultPool) Put(buf *bytes.Buffer) {
|
|
||||||
p.pool.Put(buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *defaultPool) Get() *bytes.Buffer {
|
|
||||||
return p.pool.Get().(*bytes.Buffer)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getBuffer() *bytes.Buffer {
|
|
||||||
return bufferPool.Get()
|
|
||||||
}
|
|
||||||
|
|
||||||
func putBuffer(buf *bytes.Buffer) {
|
|
||||||
buf.Reset()
|
|
||||||
bufferPool.Put(buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetBufferPool allows to replace the default logrus buffer pool
|
|
||||||
// to better meets the specific needs of an application.
|
|
||||||
func SetBufferPool(bp BufferPool) {
|
|
||||||
bufferPool = bp
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
SetBufferPool(&defaultPool{
|
|
||||||
pool: &sync.Pool{
|
|
||||||
New: func() interface{} {
|
|
||||||
return new(bytes.Buffer)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
26
vendor/github.com/sirupsen/logrus/doc.go
generated
vendored
26
vendor/github.com/sirupsen/logrus/doc.go
generated
vendored
@ -1,26 +0,0 @@
|
|||||||
/*
|
|
||||||
Package logrus is a structured logger for Go, completely API compatible with the standard library logger.
|
|
||||||
|
|
||||||
|
|
||||||
The simplest way to use Logrus is simply the package-level exported logger:
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
log.WithFields(log.Fields{
|
|
||||||
"animal": "walrus",
|
|
||||||
"number": 1,
|
|
||||||
"size": 10,
|
|
||||||
}).Info("A walrus appears")
|
|
||||||
}
|
|
||||||
|
|
||||||
Output:
|
|
||||||
time="2015-09-07T08:48:33Z" level=info msg="A walrus appears" animal=walrus number=1 size=10
|
|
||||||
|
|
||||||
For a full guide visit https://github.com/sirupsen/logrus
|
|
||||||
*/
|
|
||||||
package logrus
|
|
431
vendor/github.com/sirupsen/logrus/entry.go
generated
vendored
431
vendor/github.com/sirupsen/logrus/entry.go
generated
vendored
@ -1,431 +0,0 @@
|
|||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"reflect"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
|
|
||||||
// qualified package name, cached at first use
|
|
||||||
logrusPackage string
|
|
||||||
|
|
||||||
// Positions in the call stack when tracing to report the calling method
|
|
||||||
minimumCallerDepth int
|
|
||||||
|
|
||||||
// Used for caller information initialisation
|
|
||||||
callerInitOnce sync.Once
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
maximumCallerDepth int = 25
|
|
||||||
knownLogrusFrames int = 4
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
// start at the bottom of the stack before the package-name cache is primed
|
|
||||||
minimumCallerDepth = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Defines the key when adding errors using WithError.
|
|
||||||
var ErrorKey = "error"
|
|
||||||
|
|
||||||
// An entry is the final or intermediate Logrus logging entry. It contains all
|
|
||||||
// the fields passed with WithField{,s}. It's finally logged when Trace, Debug,
|
|
||||||
// Info, Warn, Error, Fatal or Panic is called on it. These objects can be
|
|
||||||
// reused and passed around as much as you wish to avoid field duplication.
|
|
||||||
type Entry struct {
|
|
||||||
Logger *Logger
|
|
||||||
|
|
||||||
// Contains all the fields set by the user.
|
|
||||||
Data Fields
|
|
||||||
|
|
||||||
// Time at which the log entry was created
|
|
||||||
Time time.Time
|
|
||||||
|
|
||||||
// Level the log entry was logged at: Trace, Debug, Info, Warn, Error, Fatal or Panic
|
|
||||||
// This field will be set on entry firing and the value will be equal to the one in Logger struct field.
|
|
||||||
Level Level
|
|
||||||
|
|
||||||
// Calling method, with package name
|
|
||||||
Caller *runtime.Frame
|
|
||||||
|
|
||||||
// Message passed to Trace, Debug, Info, Warn, Error, Fatal or Panic
|
|
||||||
Message string
|
|
||||||
|
|
||||||
// When formatter is called in entry.log(), a Buffer may be set to entry
|
|
||||||
Buffer *bytes.Buffer
|
|
||||||
|
|
||||||
// Contains the context set by the user. Useful for hook processing etc.
|
|
||||||
Context context.Context
|
|
||||||
|
|
||||||
// err may contain a field formatting error
|
|
||||||
err string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewEntry(logger *Logger) *Entry {
|
|
||||||
return &Entry{
|
|
||||||
Logger: logger,
|
|
||||||
// Default is three fields, plus one optional. Give a little extra room.
|
|
||||||
Data: make(Fields, 6),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Dup() *Entry {
|
|
||||||
data := make(Fields, len(entry.Data))
|
|
||||||
for k, v := range entry.Data {
|
|
||||||
data[k] = v
|
|
||||||
}
|
|
||||||
return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, Context: entry.Context, err: entry.err}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the bytes representation of this entry from the formatter.
|
|
||||||
func (entry *Entry) Bytes() ([]byte, error) {
|
|
||||||
return entry.Logger.Formatter.Format(entry)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the string representation from the reader and ultimately the
|
|
||||||
// formatter.
|
|
||||||
func (entry *Entry) String() (string, error) {
|
|
||||||
serialized, err := entry.Bytes()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
str := string(serialized)
|
|
||||||
return str, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add an error as single field (using the key defined in ErrorKey) to the Entry.
|
|
||||||
func (entry *Entry) WithError(err error) *Entry {
|
|
||||||
return entry.WithField(ErrorKey, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a context to the Entry.
|
|
||||||
func (entry *Entry) WithContext(ctx context.Context) *Entry {
|
|
||||||
dataCopy := make(Fields, len(entry.Data))
|
|
||||||
for k, v := range entry.Data {
|
|
||||||
dataCopy[k] = v
|
|
||||||
}
|
|
||||||
return &Entry{Logger: entry.Logger, Data: dataCopy, Time: entry.Time, err: entry.err, Context: ctx}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a single field to the Entry.
|
|
||||||
func (entry *Entry) WithField(key string, value interface{}) *Entry {
|
|
||||||
return entry.WithFields(Fields{key: value})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a map of fields to the Entry.
|
|
||||||
func (entry *Entry) WithFields(fields Fields) *Entry {
|
|
||||||
data := make(Fields, len(entry.Data)+len(fields))
|
|
||||||
for k, v := range entry.Data {
|
|
||||||
data[k] = v
|
|
||||||
}
|
|
||||||
fieldErr := entry.err
|
|
||||||
for k, v := range fields {
|
|
||||||
isErrField := false
|
|
||||||
if t := reflect.TypeOf(v); t != nil {
|
|
||||||
switch {
|
|
||||||
case t.Kind() == reflect.Func, t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Func:
|
|
||||||
isErrField = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if isErrField {
|
|
||||||
tmp := fmt.Sprintf("can not add field %q", k)
|
|
||||||
if fieldErr != "" {
|
|
||||||
fieldErr = entry.err + ", " + tmp
|
|
||||||
} else {
|
|
||||||
fieldErr = tmp
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
data[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, err: fieldErr, Context: entry.Context}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Overrides the time of the Entry.
|
|
||||||
func (entry *Entry) WithTime(t time.Time) *Entry {
|
|
||||||
dataCopy := make(Fields, len(entry.Data))
|
|
||||||
for k, v := range entry.Data {
|
|
||||||
dataCopy[k] = v
|
|
||||||
}
|
|
||||||
return &Entry{Logger: entry.Logger, Data: dataCopy, Time: t, err: entry.err, Context: entry.Context}
|
|
||||||
}
|
|
||||||
|
|
||||||
// getPackageName reduces a fully qualified function name to the package name
|
|
||||||
// There really ought to be to be a better way...
|
|
||||||
func getPackageName(f string) string {
|
|
||||||
for {
|
|
||||||
lastPeriod := strings.LastIndex(f, ".")
|
|
||||||
lastSlash := strings.LastIndex(f, "/")
|
|
||||||
if lastPeriod > lastSlash {
|
|
||||||
f = f[:lastPeriod]
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
// getCaller retrieves the name of the first non-logrus calling function
|
|
||||||
func getCaller() *runtime.Frame {
|
|
||||||
// cache this package's fully-qualified name
|
|
||||||
callerInitOnce.Do(func() {
|
|
||||||
pcs := make([]uintptr, maximumCallerDepth)
|
|
||||||
_ = runtime.Callers(0, pcs)
|
|
||||||
|
|
||||||
// dynamic get the package name and the minimum caller depth
|
|
||||||
for i := 0; i < maximumCallerDepth; i++ {
|
|
||||||
funcName := runtime.FuncForPC(pcs[i]).Name()
|
|
||||||
if strings.Contains(funcName, "getCaller") {
|
|
||||||
logrusPackage = getPackageName(funcName)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
minimumCallerDepth = knownLogrusFrames
|
|
||||||
})
|
|
||||||
|
|
||||||
// Restrict the lookback frames to avoid runaway lookups
|
|
||||||
pcs := make([]uintptr, maximumCallerDepth)
|
|
||||||
depth := runtime.Callers(minimumCallerDepth, pcs)
|
|
||||||
frames := runtime.CallersFrames(pcs[:depth])
|
|
||||||
|
|
||||||
for f, again := frames.Next(); again; f, again = frames.Next() {
|
|
||||||
pkg := getPackageName(f.Function)
|
|
||||||
|
|
||||||
// If the caller isn't part of this package, we're done
|
|
||||||
if pkg != logrusPackage {
|
|
||||||
return &f //nolint:scopelint
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we got here, we failed to find the caller's context
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry Entry) HasCaller() (has bool) {
|
|
||||||
return entry.Logger != nil &&
|
|
||||||
entry.Logger.ReportCaller &&
|
|
||||||
entry.Caller != nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) log(level Level, msg string) {
|
|
||||||
var buffer *bytes.Buffer
|
|
||||||
|
|
||||||
newEntry := entry.Dup()
|
|
||||||
|
|
||||||
if newEntry.Time.IsZero() {
|
|
||||||
newEntry.Time = time.Now()
|
|
||||||
}
|
|
||||||
|
|
||||||
newEntry.Level = level
|
|
||||||
newEntry.Message = msg
|
|
||||||
|
|
||||||
newEntry.Logger.mu.Lock()
|
|
||||||
reportCaller := newEntry.Logger.ReportCaller
|
|
||||||
newEntry.Logger.mu.Unlock()
|
|
||||||
|
|
||||||
if reportCaller {
|
|
||||||
newEntry.Caller = getCaller()
|
|
||||||
}
|
|
||||||
|
|
||||||
newEntry.fireHooks()
|
|
||||||
|
|
||||||
buffer = getBuffer()
|
|
||||||
defer func() {
|
|
||||||
newEntry.Buffer = nil
|
|
||||||
putBuffer(buffer)
|
|
||||||
}()
|
|
||||||
buffer.Reset()
|
|
||||||
newEntry.Buffer = buffer
|
|
||||||
|
|
||||||
newEntry.write()
|
|
||||||
|
|
||||||
newEntry.Buffer = nil
|
|
||||||
|
|
||||||
// To avoid Entry#log() returning a value that only would make sense for
|
|
||||||
// panic() to use in Entry#Panic(), we avoid the allocation by checking
|
|
||||||
// directly here.
|
|
||||||
if level <= PanicLevel {
|
|
||||||
panic(newEntry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) fireHooks() {
|
|
||||||
var tmpHooks LevelHooks
|
|
||||||
entry.Logger.mu.Lock()
|
|
||||||
tmpHooks = make(LevelHooks, len(entry.Logger.Hooks))
|
|
||||||
for k, v := range entry.Logger.Hooks {
|
|
||||||
tmpHooks[k] = v
|
|
||||||
}
|
|
||||||
entry.Logger.mu.Unlock()
|
|
||||||
|
|
||||||
err := tmpHooks.Fire(entry.Level, entry)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) write() {
|
|
||||||
serialized, err := entry.Logger.Formatter.Format(entry)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
entry.Logger.mu.Lock()
|
|
||||||
defer entry.Logger.mu.Unlock()
|
|
||||||
if _, err := entry.Logger.Out.Write(serialized); err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Log(level Level, args ...interface{}) {
|
|
||||||
if entry.Logger.IsLevelEnabled(level) {
|
|
||||||
entry.log(level, fmt.Sprint(args...))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Trace(args ...interface{}) {
|
|
||||||
entry.Log(TraceLevel, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Debug(args ...interface{}) {
|
|
||||||
entry.Log(DebugLevel, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Print(args ...interface{}) {
|
|
||||||
entry.Info(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Info(args ...interface{}) {
|
|
||||||
entry.Log(InfoLevel, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Warn(args ...interface{}) {
|
|
||||||
entry.Log(WarnLevel, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Warning(args ...interface{}) {
|
|
||||||
entry.Warn(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Error(args ...interface{}) {
|
|
||||||
entry.Log(ErrorLevel, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Fatal(args ...interface{}) {
|
|
||||||
entry.Log(FatalLevel, args...)
|
|
||||||
entry.Logger.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Panic(args ...interface{}) {
|
|
||||||
entry.Log(PanicLevel, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Entry Printf family functions
|
|
||||||
|
|
||||||
func (entry *Entry) Logf(level Level, format string, args ...interface{}) {
|
|
||||||
if entry.Logger.IsLevelEnabled(level) {
|
|
||||||
entry.Log(level, fmt.Sprintf(format, args...))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Tracef(format string, args ...interface{}) {
|
|
||||||
entry.Logf(TraceLevel, format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Debugf(format string, args ...interface{}) {
|
|
||||||
entry.Logf(DebugLevel, format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Infof(format string, args ...interface{}) {
|
|
||||||
entry.Logf(InfoLevel, format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Printf(format string, args ...interface{}) {
|
|
||||||
entry.Infof(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Warnf(format string, args ...interface{}) {
|
|
||||||
entry.Logf(WarnLevel, format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Warningf(format string, args ...interface{}) {
|
|
||||||
entry.Warnf(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Errorf(format string, args ...interface{}) {
|
|
||||||
entry.Logf(ErrorLevel, format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Fatalf(format string, args ...interface{}) {
|
|
||||||
entry.Logf(FatalLevel, format, args...)
|
|
||||||
entry.Logger.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Panicf(format string, args ...interface{}) {
|
|
||||||
entry.Logf(PanicLevel, format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Entry Println family functions
|
|
||||||
|
|
||||||
func (entry *Entry) Logln(level Level, args ...interface{}) {
|
|
||||||
if entry.Logger.IsLevelEnabled(level) {
|
|
||||||
entry.Log(level, entry.sprintlnn(args...))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Traceln(args ...interface{}) {
|
|
||||||
entry.Logln(TraceLevel, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Debugln(args ...interface{}) {
|
|
||||||
entry.Logln(DebugLevel, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Infoln(args ...interface{}) {
|
|
||||||
entry.Logln(InfoLevel, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Println(args ...interface{}) {
|
|
||||||
entry.Infoln(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Warnln(args ...interface{}) {
|
|
||||||
entry.Logln(WarnLevel, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Warningln(args ...interface{}) {
|
|
||||||
entry.Warnln(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Errorln(args ...interface{}) {
|
|
||||||
entry.Logln(ErrorLevel, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Fatalln(args ...interface{}) {
|
|
||||||
entry.Logln(FatalLevel, args...)
|
|
||||||
entry.Logger.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Panicln(args ...interface{}) {
|
|
||||||
entry.Logln(PanicLevel, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sprintlnn => Sprint no newline. This is to get the behavior of how
|
|
||||||
// fmt.Sprintln where spaces are always added between operands, regardless of
|
|
||||||
// their type. Instead of vendoring the Sprintln implementation to spare a
|
|
||||||
// string allocation, we do the simplest thing.
|
|
||||||
func (entry *Entry) sprintlnn(args ...interface{}) string {
|
|
||||||
msg := fmt.Sprintln(args...)
|
|
||||||
return msg[:len(msg)-1]
|
|
||||||
}
|
|
270
vendor/github.com/sirupsen/logrus/exported.go
generated
vendored
270
vendor/github.com/sirupsen/logrus/exported.go
generated
vendored
@ -1,270 +0,0 @@
|
|||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"io"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// std is the name of the standard logger in stdlib `log`
|
|
||||||
std = New()
|
|
||||||
)
|
|
||||||
|
|
||||||
func StandardLogger() *Logger {
|
|
||||||
return std
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetOutput sets the standard logger output.
|
|
||||||
func SetOutput(out io.Writer) {
|
|
||||||
std.SetOutput(out)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetFormatter sets the standard logger formatter.
|
|
||||||
func SetFormatter(formatter Formatter) {
|
|
||||||
std.SetFormatter(formatter)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetReportCaller sets whether the standard logger will include the calling
|
|
||||||
// method as a field.
|
|
||||||
func SetReportCaller(include bool) {
|
|
||||||
std.SetReportCaller(include)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetLevel sets the standard logger level.
|
|
||||||
func SetLevel(level Level) {
|
|
||||||
std.SetLevel(level)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetLevel returns the standard logger level.
|
|
||||||
func GetLevel() Level {
|
|
||||||
return std.GetLevel()
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsLevelEnabled checks if the log level of the standard logger is greater than the level param
|
|
||||||
func IsLevelEnabled(level Level) bool {
|
|
||||||
return std.IsLevelEnabled(level)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddHook adds a hook to the standard logger hooks.
|
|
||||||
func AddHook(hook Hook) {
|
|
||||||
std.AddHook(hook)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithError creates an entry from the standard logger and adds an error to it, using the value defined in ErrorKey as key.
|
|
||||||
func WithError(err error) *Entry {
|
|
||||||
return std.WithField(ErrorKey, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithContext creates an entry from the standard logger and adds a context to it.
|
|
||||||
func WithContext(ctx context.Context) *Entry {
|
|
||||||
return std.WithContext(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithField creates an entry from the standard logger and adds a field to
|
|
||||||
// it. If you want multiple fields, use `WithFields`.
|
|
||||||
//
|
|
||||||
// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
|
|
||||||
// or Panic on the Entry it returns.
|
|
||||||
func WithField(key string, value interface{}) *Entry {
|
|
||||||
return std.WithField(key, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithFields creates an entry from the standard logger and adds multiple
|
|
||||||
// fields to it. This is simply a helper for `WithField`, invoking it
|
|
||||||
// once for each field.
|
|
||||||
//
|
|
||||||
// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
|
|
||||||
// or Panic on the Entry it returns.
|
|
||||||
func WithFields(fields Fields) *Entry {
|
|
||||||
return std.WithFields(fields)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithTime creates an entry from the standard logger and overrides the time of
|
|
||||||
// logs generated with it.
|
|
||||||
//
|
|
||||||
// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
|
|
||||||
// or Panic on the Entry it returns.
|
|
||||||
func WithTime(t time.Time) *Entry {
|
|
||||||
return std.WithTime(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Trace logs a message at level Trace on the standard logger.
|
|
||||||
func Trace(args ...interface{}) {
|
|
||||||
std.Trace(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Debug logs a message at level Debug on the standard logger.
|
|
||||||
func Debug(args ...interface{}) {
|
|
||||||
std.Debug(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print logs a message at level Info on the standard logger.
|
|
||||||
func Print(args ...interface{}) {
|
|
||||||
std.Print(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Info logs a message at level Info on the standard logger.
|
|
||||||
func Info(args ...interface{}) {
|
|
||||||
std.Info(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Warn logs a message at level Warn on the standard logger.
|
|
||||||
func Warn(args ...interface{}) {
|
|
||||||
std.Warn(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Warning logs a message at level Warn on the standard logger.
|
|
||||||
func Warning(args ...interface{}) {
|
|
||||||
std.Warning(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error logs a message at level Error on the standard logger.
|
|
||||||
func Error(args ...interface{}) {
|
|
||||||
std.Error(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Panic logs a message at level Panic on the standard logger.
|
|
||||||
func Panic(args ...interface{}) {
|
|
||||||
std.Panic(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fatal logs a message at level Fatal on the standard logger then the process will exit with status set to 1.
|
|
||||||
func Fatal(args ...interface{}) {
|
|
||||||
std.Fatal(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TraceFn logs a message from a func at level Trace on the standard logger.
|
|
||||||
func TraceFn(fn LogFunction) {
|
|
||||||
std.TraceFn(fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DebugFn logs a message from a func at level Debug on the standard logger.
|
|
||||||
func DebugFn(fn LogFunction) {
|
|
||||||
std.DebugFn(fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrintFn logs a message from a func at level Info on the standard logger.
|
|
||||||
func PrintFn(fn LogFunction) {
|
|
||||||
std.PrintFn(fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// InfoFn logs a message from a func at level Info on the standard logger.
|
|
||||||
func InfoFn(fn LogFunction) {
|
|
||||||
std.InfoFn(fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WarnFn logs a message from a func at level Warn on the standard logger.
|
|
||||||
func WarnFn(fn LogFunction) {
|
|
||||||
std.WarnFn(fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WarningFn logs a message from a func at level Warn on the standard logger.
|
|
||||||
func WarningFn(fn LogFunction) {
|
|
||||||
std.WarningFn(fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrorFn logs a message from a func at level Error on the standard logger.
|
|
||||||
func ErrorFn(fn LogFunction) {
|
|
||||||
std.ErrorFn(fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// PanicFn logs a message from a func at level Panic on the standard logger.
|
|
||||||
func PanicFn(fn LogFunction) {
|
|
||||||
std.PanicFn(fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FatalFn logs a message from a func at level Fatal on the standard logger then the process will exit with status set to 1.
|
|
||||||
func FatalFn(fn LogFunction) {
|
|
||||||
std.FatalFn(fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tracef logs a message at level Trace on the standard logger.
|
|
||||||
func Tracef(format string, args ...interface{}) {
|
|
||||||
std.Tracef(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Debugf logs a message at level Debug on the standard logger.
|
|
||||||
func Debugf(format string, args ...interface{}) {
|
|
||||||
std.Debugf(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Printf logs a message at level Info on the standard logger.
|
|
||||||
func Printf(format string, args ...interface{}) {
|
|
||||||
std.Printf(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Infof logs a message at level Info on the standard logger.
|
|
||||||
func Infof(format string, args ...interface{}) {
|
|
||||||
std.Infof(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Warnf logs a message at level Warn on the standard logger.
|
|
||||||
func Warnf(format string, args ...interface{}) {
|
|
||||||
std.Warnf(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Warningf logs a message at level Warn on the standard logger.
|
|
||||||
func Warningf(format string, args ...interface{}) {
|
|
||||||
std.Warningf(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Errorf logs a message at level Error on the standard logger.
|
|
||||||
func Errorf(format string, args ...interface{}) {
|
|
||||||
std.Errorf(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Panicf logs a message at level Panic on the standard logger.
|
|
||||||
func Panicf(format string, args ...interface{}) {
|
|
||||||
std.Panicf(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fatalf logs a message at level Fatal on the standard logger then the process will exit with status set to 1.
|
|
||||||
func Fatalf(format string, args ...interface{}) {
|
|
||||||
std.Fatalf(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Traceln logs a message at level Trace on the standard logger.
|
|
||||||
func Traceln(args ...interface{}) {
|
|
||||||
std.Traceln(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Debugln logs a message at level Debug on the standard logger.
|
|
||||||
func Debugln(args ...interface{}) {
|
|
||||||
std.Debugln(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Println logs a message at level Info on the standard logger.
|
|
||||||
func Println(args ...interface{}) {
|
|
||||||
std.Println(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Infoln logs a message at level Info on the standard logger.
|
|
||||||
func Infoln(args ...interface{}) {
|
|
||||||
std.Infoln(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Warnln logs a message at level Warn on the standard logger.
|
|
||||||
func Warnln(args ...interface{}) {
|
|
||||||
std.Warnln(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Warningln logs a message at level Warn on the standard logger.
|
|
||||||
func Warningln(args ...interface{}) {
|
|
||||||
std.Warningln(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Errorln logs a message at level Error on the standard logger.
|
|
||||||
func Errorln(args ...interface{}) {
|
|
||||||
std.Errorln(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Panicln logs a message at level Panic on the standard logger.
|
|
||||||
func Panicln(args ...interface{}) {
|
|
||||||
std.Panicln(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fatalln logs a message at level Fatal on the standard logger then the process will exit with status set to 1.
|
|
||||||
func Fatalln(args ...interface{}) {
|
|
||||||
std.Fatalln(args...)
|
|
||||||
}
|
|
78
vendor/github.com/sirupsen/logrus/formatter.go
generated
vendored
78
vendor/github.com/sirupsen/logrus/formatter.go
generated
vendored
@ -1,78 +0,0 @@
|
|||||||
package logrus
|
|
||||||
|
|
||||||
import "time"
|
|
||||||
|
|
||||||
// Default key names for the default fields
|
|
||||||
const (
|
|
||||||
defaultTimestampFormat = time.RFC3339
|
|
||||||
FieldKeyMsg = "msg"
|
|
||||||
FieldKeyLevel = "level"
|
|
||||||
FieldKeyTime = "time"
|
|
||||||
FieldKeyLogrusError = "logrus_error"
|
|
||||||
FieldKeyFunc = "func"
|
|
||||||
FieldKeyFile = "file"
|
|
||||||
)
|
|
||||||
|
|
||||||
// The Formatter interface is used to implement a custom Formatter. It takes an
|
|
||||||
// `Entry`. It exposes all the fields, including the default ones:
|
|
||||||
//
|
|
||||||
// * `entry.Data["msg"]`. The message passed from Info, Warn, Error ..
|
|
||||||
// * `entry.Data["time"]`. The timestamp.
|
|
||||||
// * `entry.Data["level"]. The level the entry was logged at.
|
|
||||||
//
|
|
||||||
// Any additional fields added with `WithField` or `WithFields` are also in
|
|
||||||
// `entry.Data`. Format is expected to return an array of bytes which are then
|
|
||||||
// logged to `logger.Out`.
|
|
||||||
type Formatter interface {
|
|
||||||
Format(*Entry) ([]byte, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is to not silently overwrite `time`, `msg`, `func` and `level` fields when
|
|
||||||
// dumping it. If this code wasn't there doing:
|
|
||||||
//
|
|
||||||
// logrus.WithField("level", 1).Info("hello")
|
|
||||||
//
|
|
||||||
// Would just silently drop the user provided level. Instead with this code
|
|
||||||
// it'll logged as:
|
|
||||||
//
|
|
||||||
// {"level": "info", "fields.level": 1, "msg": "hello", "time": "..."}
|
|
||||||
//
|
|
||||||
// It's not exported because it's still using Data in an opinionated way. It's to
|
|
||||||
// avoid code duplication between the two default formatters.
|
|
||||||
func prefixFieldClashes(data Fields, fieldMap FieldMap, reportCaller bool) {
|
|
||||||
timeKey := fieldMap.resolve(FieldKeyTime)
|
|
||||||
if t, ok := data[timeKey]; ok {
|
|
||||||
data["fields."+timeKey] = t
|
|
||||||
delete(data, timeKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
msgKey := fieldMap.resolve(FieldKeyMsg)
|
|
||||||
if m, ok := data[msgKey]; ok {
|
|
||||||
data["fields."+msgKey] = m
|
|
||||||
delete(data, msgKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
levelKey := fieldMap.resolve(FieldKeyLevel)
|
|
||||||
if l, ok := data[levelKey]; ok {
|
|
||||||
data["fields."+levelKey] = l
|
|
||||||
delete(data, levelKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
logrusErrKey := fieldMap.resolve(FieldKeyLogrusError)
|
|
||||||
if l, ok := data[logrusErrKey]; ok {
|
|
||||||
data["fields."+logrusErrKey] = l
|
|
||||||
delete(data, logrusErrKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If reportCaller is not set, 'func' will not conflict.
|
|
||||||
if reportCaller {
|
|
||||||
funcKey := fieldMap.resolve(FieldKeyFunc)
|
|
||||||
if l, ok := data[funcKey]; ok {
|
|
||||||
data["fields."+funcKey] = l
|
|
||||||
}
|
|
||||||
fileKey := fieldMap.resolve(FieldKeyFile)
|
|
||||||
if l, ok := data[fileKey]; ok {
|
|
||||||
data["fields."+fileKey] = l
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
34
vendor/github.com/sirupsen/logrus/hooks.go
generated
vendored
34
vendor/github.com/sirupsen/logrus/hooks.go
generated
vendored
@ -1,34 +0,0 @@
|
|||||||
package logrus
|
|
||||||
|
|
||||||
// A hook to be fired when logging on the logging levels returned from
|
|
||||||
// `Levels()` on your implementation of the interface. Note that this is not
|
|
||||||
// fired in a goroutine or a channel with workers, you should handle such
|
|
||||||
// functionality yourself if your call is non-blocking and you don't wish for
|
|
||||||
// the logging calls for levels returned from `Levels()` to block.
|
|
||||||
type Hook interface {
|
|
||||||
Levels() []Level
|
|
||||||
Fire(*Entry) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Internal type for storing the hooks on a logger instance.
|
|
||||||
type LevelHooks map[Level][]Hook
|
|
||||||
|
|
||||||
// Add a hook to an instance of logger. This is called with
|
|
||||||
// `log.Hooks.Add(new(MyHook))` where `MyHook` implements the `Hook` interface.
|
|
||||||
func (hooks LevelHooks) Add(hook Hook) {
|
|
||||||
for _, level := range hook.Levels() {
|
|
||||||
hooks[level] = append(hooks[level], hook)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fire all the hooks for the passed level. Used by `entry.log` to fire
|
|
||||||
// appropriate hooks for a log entry.
|
|
||||||
func (hooks LevelHooks) Fire(level Level, entry *Entry) error {
|
|
||||||
for _, hook := range hooks[level] {
|
|
||||||
if err := hook.Fire(entry); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
128
vendor/github.com/sirupsen/logrus/json_formatter.go
generated
vendored
128
vendor/github.com/sirupsen/logrus/json_formatter.go
generated
vendored
@ -1,128 +0,0 @@
|
|||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"runtime"
|
|
||||||
)
|
|
||||||
|
|
||||||
type fieldKey string
|
|
||||||
|
|
||||||
// FieldMap allows customization of the key names for default fields.
|
|
||||||
type FieldMap map[fieldKey]string
|
|
||||||
|
|
||||||
func (f FieldMap) resolve(key fieldKey) string {
|
|
||||||
if k, ok := f[key]; ok {
|
|
||||||
return k
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
// JSONFormatter formats logs into parsable json
|
|
||||||
type JSONFormatter struct {
|
|
||||||
// TimestampFormat sets the format used for marshaling timestamps.
|
|
||||||
// The format to use is the same than for time.Format or time.Parse from the standard
|
|
||||||
// library.
|
|
||||||
// The standard Library already provides a set of predefined format.
|
|
||||||
TimestampFormat string
|
|
||||||
|
|
||||||
// DisableTimestamp allows disabling automatic timestamps in output
|
|
||||||
DisableTimestamp bool
|
|
||||||
|
|
||||||
// DisableHTMLEscape allows disabling html escaping in output
|
|
||||||
DisableHTMLEscape bool
|
|
||||||
|
|
||||||
// DataKey allows users to put all the log entry parameters into a nested dictionary at a given key.
|
|
||||||
DataKey string
|
|
||||||
|
|
||||||
// FieldMap allows users to customize the names of keys for default fields.
|
|
||||||
// As an example:
|
|
||||||
// formatter := &JSONFormatter{
|
|
||||||
// FieldMap: FieldMap{
|
|
||||||
// FieldKeyTime: "@timestamp",
|
|
||||||
// FieldKeyLevel: "@level",
|
|
||||||
// FieldKeyMsg: "@message",
|
|
||||||
// FieldKeyFunc: "@caller",
|
|
||||||
// },
|
|
||||||
// }
|
|
||||||
FieldMap FieldMap
|
|
||||||
|
|
||||||
// CallerPrettyfier can be set by the user to modify the content
|
|
||||||
// of the function and file keys in the json data when ReportCaller is
|
|
||||||
// activated. If any of the returned value is the empty string the
|
|
||||||
// corresponding key will be removed from json fields.
|
|
||||||
CallerPrettyfier func(*runtime.Frame) (function string, file string)
|
|
||||||
|
|
||||||
// PrettyPrint will indent all json logs
|
|
||||||
PrettyPrint bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Format renders a single log entry
|
|
||||||
func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
|
|
||||||
data := make(Fields, len(entry.Data)+4)
|
|
||||||
for k, v := range entry.Data {
|
|
||||||
switch v := v.(type) {
|
|
||||||
case error:
|
|
||||||
// Otherwise errors are ignored by `encoding/json`
|
|
||||||
// https://github.com/sirupsen/logrus/issues/137
|
|
||||||
data[k] = v.Error()
|
|
||||||
default:
|
|
||||||
data[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.DataKey != "" {
|
|
||||||
newData := make(Fields, 4)
|
|
||||||
newData[f.DataKey] = data
|
|
||||||
data = newData
|
|
||||||
}
|
|
||||||
|
|
||||||
prefixFieldClashes(data, f.FieldMap, entry.HasCaller())
|
|
||||||
|
|
||||||
timestampFormat := f.TimestampFormat
|
|
||||||
if timestampFormat == "" {
|
|
||||||
timestampFormat = defaultTimestampFormat
|
|
||||||
}
|
|
||||||
|
|
||||||
if entry.err != "" {
|
|
||||||
data[f.FieldMap.resolve(FieldKeyLogrusError)] = entry.err
|
|
||||||
}
|
|
||||||
if !f.DisableTimestamp {
|
|
||||||
data[f.FieldMap.resolve(FieldKeyTime)] = entry.Time.Format(timestampFormat)
|
|
||||||
}
|
|
||||||
data[f.FieldMap.resolve(FieldKeyMsg)] = entry.Message
|
|
||||||
data[f.FieldMap.resolve(FieldKeyLevel)] = entry.Level.String()
|
|
||||||
if entry.HasCaller() {
|
|
||||||
funcVal := entry.Caller.Function
|
|
||||||
fileVal := fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line)
|
|
||||||
if f.CallerPrettyfier != nil {
|
|
||||||
funcVal, fileVal = f.CallerPrettyfier(entry.Caller)
|
|
||||||
}
|
|
||||||
if funcVal != "" {
|
|
||||||
data[f.FieldMap.resolve(FieldKeyFunc)] = funcVal
|
|
||||||
}
|
|
||||||
if fileVal != "" {
|
|
||||||
data[f.FieldMap.resolve(FieldKeyFile)] = fileVal
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var b *bytes.Buffer
|
|
||||||
if entry.Buffer != nil {
|
|
||||||
b = entry.Buffer
|
|
||||||
} else {
|
|
||||||
b = &bytes.Buffer{}
|
|
||||||
}
|
|
||||||
|
|
||||||
encoder := json.NewEncoder(b)
|
|
||||||
encoder.SetEscapeHTML(!f.DisableHTMLEscape)
|
|
||||||
if f.PrettyPrint {
|
|
||||||
encoder.SetIndent("", " ")
|
|
||||||
}
|
|
||||||
if err := encoder.Encode(data); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to marshal fields to JSON, %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return b.Bytes(), nil
|
|
||||||
}
|
|
404
vendor/github.com/sirupsen/logrus/logger.go
generated
vendored
404
vendor/github.com/sirupsen/logrus/logger.go
generated
vendored
@ -1,404 +0,0 @@
|
|||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"sync"
|
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// LogFunction For big messages, it can be more efficient to pass a function
|
|
||||||
// and only call it if the log level is actually enables rather than
|
|
||||||
// generating the log message and then checking if the level is enabled
|
|
||||||
type LogFunction func() []interface{}
|
|
||||||
|
|
||||||
type Logger struct {
|
|
||||||
// The logs are `io.Copy`'d to this in a mutex. It's common to set this to a
|
|
||||||
// file, or leave it default which is `os.Stderr`. You can also set this to
|
|
||||||
// something more adventurous, such as logging to Kafka.
|
|
||||||
Out io.Writer
|
|
||||||
// Hooks for the logger instance. These allow firing events based on logging
|
|
||||||
// levels and log entries. For example, to send errors to an error tracking
|
|
||||||
// service, log to StatsD or dump the core on fatal errors.
|
|
||||||
Hooks LevelHooks
|
|
||||||
// All log entries pass through the formatter before logged to Out. The
|
|
||||||
// included formatters are `TextFormatter` and `JSONFormatter` for which
|
|
||||||
// TextFormatter is the default. In development (when a TTY is attached) it
|
|
||||||
// logs with colors, but to a file it wouldn't. You can easily implement your
|
|
||||||
// own that implements the `Formatter` interface, see the `README` or included
|
|
||||||
// formatters for examples.
|
|
||||||
Formatter Formatter
|
|
||||||
|
|
||||||
// Flag for whether to log caller info (off by default)
|
|
||||||
ReportCaller bool
|
|
||||||
|
|
||||||
// The logging level the logger should log at. This is typically (and defaults
|
|
||||||
// to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be
|
|
||||||
// logged.
|
|
||||||
Level Level
|
|
||||||
// Used to sync writing to the log. Locking is enabled by Default
|
|
||||||
mu MutexWrap
|
|
||||||
// Reusable empty entry
|
|
||||||
entryPool sync.Pool
|
|
||||||
// Function to exit the application, defaults to `os.Exit()`
|
|
||||||
ExitFunc exitFunc
|
|
||||||
}
|
|
||||||
|
|
||||||
type exitFunc func(int)
|
|
||||||
|
|
||||||
type MutexWrap struct {
|
|
||||||
lock sync.Mutex
|
|
||||||
disabled bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mw *MutexWrap) Lock() {
|
|
||||||
if !mw.disabled {
|
|
||||||
mw.lock.Lock()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mw *MutexWrap) Unlock() {
|
|
||||||
if !mw.disabled {
|
|
||||||
mw.lock.Unlock()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mw *MutexWrap) Disable() {
|
|
||||||
mw.disabled = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates a new logger. Configuration should be set by changing `Formatter`,
|
|
||||||
// `Out` and `Hooks` directly on the default logger instance. You can also just
|
|
||||||
// instantiate your own:
|
|
||||||
//
|
|
||||||
// var log = &logrus.Logger{
|
|
||||||
// Out: os.Stderr,
|
|
||||||
// Formatter: new(logrus.TextFormatter),
|
|
||||||
// Hooks: make(logrus.LevelHooks),
|
|
||||||
// Level: logrus.DebugLevel,
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// It's recommended to make this a global instance called `log`.
|
|
||||||
func New() *Logger {
|
|
||||||
return &Logger{
|
|
||||||
Out: os.Stderr,
|
|
||||||
Formatter: new(TextFormatter),
|
|
||||||
Hooks: make(LevelHooks),
|
|
||||||
Level: InfoLevel,
|
|
||||||
ExitFunc: os.Exit,
|
|
||||||
ReportCaller: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) newEntry() *Entry {
|
|
||||||
entry, ok := logger.entryPool.Get().(*Entry)
|
|
||||||
if ok {
|
|
||||||
return entry
|
|
||||||
}
|
|
||||||
return NewEntry(logger)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) releaseEntry(entry *Entry) {
|
|
||||||
entry.Data = map[string]interface{}{}
|
|
||||||
logger.entryPool.Put(entry)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithField allocates a new entry and adds a field to it.
|
|
||||||
// Debug, Print, Info, Warn, Error, Fatal or Panic must be then applied to
|
|
||||||
// this new returned entry.
|
|
||||||
// If you want multiple fields, use `WithFields`.
|
|
||||||
func (logger *Logger) WithField(key string, value interface{}) *Entry {
|
|
||||||
entry := logger.newEntry()
|
|
||||||
defer logger.releaseEntry(entry)
|
|
||||||
return entry.WithField(key, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adds a struct of fields to the log entry. All it does is call `WithField` for
|
|
||||||
// each `Field`.
|
|
||||||
func (logger *Logger) WithFields(fields Fields) *Entry {
|
|
||||||
entry := logger.newEntry()
|
|
||||||
defer logger.releaseEntry(entry)
|
|
||||||
return entry.WithFields(fields)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add an error as single field to the log entry. All it does is call
|
|
||||||
// `WithError` for the given `error`.
|
|
||||||
func (logger *Logger) WithError(err error) *Entry {
|
|
||||||
entry := logger.newEntry()
|
|
||||||
defer logger.releaseEntry(entry)
|
|
||||||
return entry.WithError(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a context to the log entry.
|
|
||||||
func (logger *Logger) WithContext(ctx context.Context) *Entry {
|
|
||||||
entry := logger.newEntry()
|
|
||||||
defer logger.releaseEntry(entry)
|
|
||||||
return entry.WithContext(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Overrides the time of the log entry.
|
|
||||||
func (logger *Logger) WithTime(t time.Time) *Entry {
|
|
||||||
entry := logger.newEntry()
|
|
||||||
defer logger.releaseEntry(entry)
|
|
||||||
return entry.WithTime(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Logf(level Level, format string, args ...interface{}) {
|
|
||||||
if logger.IsLevelEnabled(level) {
|
|
||||||
entry := logger.newEntry()
|
|
||||||
entry.Logf(level, format, args...)
|
|
||||||
logger.releaseEntry(entry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Tracef(format string, args ...interface{}) {
|
|
||||||
logger.Logf(TraceLevel, format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Debugf(format string, args ...interface{}) {
|
|
||||||
logger.Logf(DebugLevel, format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Infof(format string, args ...interface{}) {
|
|
||||||
logger.Logf(InfoLevel, format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Printf(format string, args ...interface{}) {
|
|
||||||
entry := logger.newEntry()
|
|
||||||
entry.Printf(format, args...)
|
|
||||||
logger.releaseEntry(entry)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Warnf(format string, args ...interface{}) {
|
|
||||||
logger.Logf(WarnLevel, format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Warningf(format string, args ...interface{}) {
|
|
||||||
logger.Warnf(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Errorf(format string, args ...interface{}) {
|
|
||||||
logger.Logf(ErrorLevel, format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Fatalf(format string, args ...interface{}) {
|
|
||||||
logger.Logf(FatalLevel, format, args...)
|
|
||||||
logger.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Panicf(format string, args ...interface{}) {
|
|
||||||
logger.Logf(PanicLevel, format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Log(level Level, args ...interface{}) {
|
|
||||||
if logger.IsLevelEnabled(level) {
|
|
||||||
entry := logger.newEntry()
|
|
||||||
entry.Log(level, args...)
|
|
||||||
logger.releaseEntry(entry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) LogFn(level Level, fn LogFunction) {
|
|
||||||
if logger.IsLevelEnabled(level) {
|
|
||||||
entry := logger.newEntry()
|
|
||||||
entry.Log(level, fn()...)
|
|
||||||
logger.releaseEntry(entry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Trace(args ...interface{}) {
|
|
||||||
logger.Log(TraceLevel, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Debug(args ...interface{}) {
|
|
||||||
logger.Log(DebugLevel, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Info(args ...interface{}) {
|
|
||||||
logger.Log(InfoLevel, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Print(args ...interface{}) {
|
|
||||||
entry := logger.newEntry()
|
|
||||||
entry.Print(args...)
|
|
||||||
logger.releaseEntry(entry)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Warn(args ...interface{}) {
|
|
||||||
logger.Log(WarnLevel, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Warning(args ...interface{}) {
|
|
||||||
logger.Warn(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Error(args ...interface{}) {
|
|
||||||
logger.Log(ErrorLevel, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Fatal(args ...interface{}) {
|
|
||||||
logger.Log(FatalLevel, args...)
|
|
||||||
logger.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Panic(args ...interface{}) {
|
|
||||||
logger.Log(PanicLevel, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) TraceFn(fn LogFunction) {
|
|
||||||
logger.LogFn(TraceLevel, fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) DebugFn(fn LogFunction) {
|
|
||||||
logger.LogFn(DebugLevel, fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) InfoFn(fn LogFunction) {
|
|
||||||
logger.LogFn(InfoLevel, fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) PrintFn(fn LogFunction) {
|
|
||||||
entry := logger.newEntry()
|
|
||||||
entry.Print(fn()...)
|
|
||||||
logger.releaseEntry(entry)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) WarnFn(fn LogFunction) {
|
|
||||||
logger.LogFn(WarnLevel, fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) WarningFn(fn LogFunction) {
|
|
||||||
logger.WarnFn(fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) ErrorFn(fn LogFunction) {
|
|
||||||
logger.LogFn(ErrorLevel, fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) FatalFn(fn LogFunction) {
|
|
||||||
logger.LogFn(FatalLevel, fn)
|
|
||||||
logger.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) PanicFn(fn LogFunction) {
|
|
||||||
logger.LogFn(PanicLevel, fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Logln(level Level, args ...interface{}) {
|
|
||||||
if logger.IsLevelEnabled(level) {
|
|
||||||
entry := logger.newEntry()
|
|
||||||
entry.Logln(level, args...)
|
|
||||||
logger.releaseEntry(entry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Traceln(args ...interface{}) {
|
|
||||||
logger.Logln(TraceLevel, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Debugln(args ...interface{}) {
|
|
||||||
logger.Logln(DebugLevel, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Infoln(args ...interface{}) {
|
|
||||||
logger.Logln(InfoLevel, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Println(args ...interface{}) {
|
|
||||||
entry := logger.newEntry()
|
|
||||||
entry.Println(args...)
|
|
||||||
logger.releaseEntry(entry)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Warnln(args ...interface{}) {
|
|
||||||
logger.Logln(WarnLevel, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Warningln(args ...interface{}) {
|
|
||||||
logger.Warnln(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Errorln(args ...interface{}) {
|
|
||||||
logger.Logln(ErrorLevel, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Fatalln(args ...interface{}) {
|
|
||||||
logger.Logln(FatalLevel, args...)
|
|
||||||
logger.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Panicln(args ...interface{}) {
|
|
||||||
logger.Logln(PanicLevel, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Exit(code int) {
|
|
||||||
runHandlers()
|
|
||||||
if logger.ExitFunc == nil {
|
|
||||||
logger.ExitFunc = os.Exit
|
|
||||||
}
|
|
||||||
logger.ExitFunc(code)
|
|
||||||
}
|
|
||||||
|
|
||||||
//When file is opened with appending mode, it's safe to
|
|
||||||
//write concurrently to a file (within 4k message on Linux).
|
|
||||||
//In these cases user can choose to disable the lock.
|
|
||||||
func (logger *Logger) SetNoLock() {
|
|
||||||
logger.mu.Disable()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) level() Level {
|
|
||||||
return Level(atomic.LoadUint32((*uint32)(&logger.Level)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetLevel sets the logger level.
|
|
||||||
func (logger *Logger) SetLevel(level Level) {
|
|
||||||
atomic.StoreUint32((*uint32)(&logger.Level), uint32(level))
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetLevel returns the logger level.
|
|
||||||
func (logger *Logger) GetLevel() Level {
|
|
||||||
return logger.level()
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddHook adds a hook to the logger hooks.
|
|
||||||
func (logger *Logger) AddHook(hook Hook) {
|
|
||||||
logger.mu.Lock()
|
|
||||||
defer logger.mu.Unlock()
|
|
||||||
logger.Hooks.Add(hook)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsLevelEnabled checks if the log level of the logger is greater than the level param
|
|
||||||
func (logger *Logger) IsLevelEnabled(level Level) bool {
|
|
||||||
return logger.level() >= level
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetFormatter sets the logger formatter.
|
|
||||||
func (logger *Logger) SetFormatter(formatter Formatter) {
|
|
||||||
logger.mu.Lock()
|
|
||||||
defer logger.mu.Unlock()
|
|
||||||
logger.Formatter = formatter
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetOutput sets the logger output.
|
|
||||||
func (logger *Logger) SetOutput(output io.Writer) {
|
|
||||||
logger.mu.Lock()
|
|
||||||
defer logger.mu.Unlock()
|
|
||||||
logger.Out = output
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) SetReportCaller(reportCaller bool) {
|
|
||||||
logger.mu.Lock()
|
|
||||||
defer logger.mu.Unlock()
|
|
||||||
logger.ReportCaller = reportCaller
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReplaceHooks replaces the logger hooks and returns the old ones
|
|
||||||
func (logger *Logger) ReplaceHooks(hooks LevelHooks) LevelHooks {
|
|
||||||
logger.mu.Lock()
|
|
||||||
oldHooks := logger.Hooks
|
|
||||||
logger.Hooks = hooks
|
|
||||||
logger.mu.Unlock()
|
|
||||||
return oldHooks
|
|
||||||
}
|
|
186
vendor/github.com/sirupsen/logrus/logrus.go
generated
vendored
186
vendor/github.com/sirupsen/logrus/logrus.go
generated
vendored
@ -1,186 +0,0 @@
|
|||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Fields type, used to pass to `WithFields`.
|
|
||||||
type Fields map[string]interface{}
|
|
||||||
|
|
||||||
// Level type
|
|
||||||
type Level uint32
|
|
||||||
|
|
||||||
// Convert the Level to a string. E.g. PanicLevel becomes "panic".
|
|
||||||
func (level Level) String() string {
|
|
||||||
if b, err := level.MarshalText(); err == nil {
|
|
||||||
return string(b)
|
|
||||||
} else {
|
|
||||||
return "unknown"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseLevel takes a string level and returns the Logrus log level constant.
|
|
||||||
func ParseLevel(lvl string) (Level, error) {
|
|
||||||
switch strings.ToLower(lvl) {
|
|
||||||
case "panic":
|
|
||||||
return PanicLevel, nil
|
|
||||||
case "fatal":
|
|
||||||
return FatalLevel, nil
|
|
||||||
case "error":
|
|
||||||
return ErrorLevel, nil
|
|
||||||
case "warn", "warning":
|
|
||||||
return WarnLevel, nil
|
|
||||||
case "info":
|
|
||||||
return InfoLevel, nil
|
|
||||||
case "debug":
|
|
||||||
return DebugLevel, nil
|
|
||||||
case "trace":
|
|
||||||
return TraceLevel, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var l Level
|
|
||||||
return l, fmt.Errorf("not a valid logrus Level: %q", lvl)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalText implements encoding.TextUnmarshaler.
|
|
||||||
func (level *Level) UnmarshalText(text []byte) error {
|
|
||||||
l, err := ParseLevel(string(text))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
*level = l
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (level Level) MarshalText() ([]byte, error) {
|
|
||||||
switch level {
|
|
||||||
case TraceLevel:
|
|
||||||
return []byte("trace"), nil
|
|
||||||
case DebugLevel:
|
|
||||||
return []byte("debug"), nil
|
|
||||||
case InfoLevel:
|
|
||||||
return []byte("info"), nil
|
|
||||||
case WarnLevel:
|
|
||||||
return []byte("warning"), nil
|
|
||||||
case ErrorLevel:
|
|
||||||
return []byte("error"), nil
|
|
||||||
case FatalLevel:
|
|
||||||
return []byte("fatal"), nil
|
|
||||||
case PanicLevel:
|
|
||||||
return []byte("panic"), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, fmt.Errorf("not a valid logrus level %d", level)
|
|
||||||
}
|
|
||||||
|
|
||||||
// A constant exposing all logging levels
|
|
||||||
var AllLevels = []Level{
|
|
||||||
PanicLevel,
|
|
||||||
FatalLevel,
|
|
||||||
ErrorLevel,
|
|
||||||
WarnLevel,
|
|
||||||
InfoLevel,
|
|
||||||
DebugLevel,
|
|
||||||
TraceLevel,
|
|
||||||
}
|
|
||||||
|
|
||||||
// These are the different logging levels. You can set the logging level to log
|
|
||||||
// on your instance of logger, obtained with `logrus.New()`.
|
|
||||||
const (
|
|
||||||
// PanicLevel level, highest level of severity. Logs and then calls panic with the
|
|
||||||
// message passed to Debug, Info, ...
|
|
||||||
PanicLevel Level = iota
|
|
||||||
// FatalLevel level. Logs and then calls `logger.Exit(1)`. It will exit even if the
|
|
||||||
// logging level is set to Panic.
|
|
||||||
FatalLevel
|
|
||||||
// ErrorLevel level. Logs. Used for errors that should definitely be noted.
|
|
||||||
// Commonly used for hooks to send errors to an error tracking service.
|
|
||||||
ErrorLevel
|
|
||||||
// WarnLevel level. Non-critical entries that deserve eyes.
|
|
||||||
WarnLevel
|
|
||||||
// InfoLevel level. General operational entries about what's going on inside the
|
|
||||||
// application.
|
|
||||||
InfoLevel
|
|
||||||
// DebugLevel level. Usually only enabled when debugging. Very verbose logging.
|
|
||||||
DebugLevel
|
|
||||||
// TraceLevel level. Designates finer-grained informational events than the Debug.
|
|
||||||
TraceLevel
|
|
||||||
)
|
|
||||||
|
|
||||||
// Won't compile if StdLogger can't be realized by a log.Logger
|
|
||||||
var (
|
|
||||||
_ StdLogger = &log.Logger{}
|
|
||||||
_ StdLogger = &Entry{}
|
|
||||||
_ StdLogger = &Logger{}
|
|
||||||
)
|
|
||||||
|
|
||||||
// StdLogger is what your logrus-enabled library should take, that way
|
|
||||||
// it'll accept a stdlib logger and a logrus logger. There's no standard
|
|
||||||
// interface, this is the closest we get, unfortunately.
|
|
||||||
type StdLogger interface {
|
|
||||||
Print(...interface{})
|
|
||||||
Printf(string, ...interface{})
|
|
||||||
Println(...interface{})
|
|
||||||
|
|
||||||
Fatal(...interface{})
|
|
||||||
Fatalf(string, ...interface{})
|
|
||||||
Fatalln(...interface{})
|
|
||||||
|
|
||||||
Panic(...interface{})
|
|
||||||
Panicf(string, ...interface{})
|
|
||||||
Panicln(...interface{})
|
|
||||||
}
|
|
||||||
|
|
||||||
// The FieldLogger interface generalizes the Entry and Logger types
|
|
||||||
type FieldLogger interface {
|
|
||||||
WithField(key string, value interface{}) *Entry
|
|
||||||
WithFields(fields Fields) *Entry
|
|
||||||
WithError(err error) *Entry
|
|
||||||
|
|
||||||
Debugf(format string, args ...interface{})
|
|
||||||
Infof(format string, args ...interface{})
|
|
||||||
Printf(format string, args ...interface{})
|
|
||||||
Warnf(format string, args ...interface{})
|
|
||||||
Warningf(format string, args ...interface{})
|
|
||||||
Errorf(format string, args ...interface{})
|
|
||||||
Fatalf(format string, args ...interface{})
|
|
||||||
Panicf(format string, args ...interface{})
|
|
||||||
|
|
||||||
Debug(args ...interface{})
|
|
||||||
Info(args ...interface{})
|
|
||||||
Print(args ...interface{})
|
|
||||||
Warn(args ...interface{})
|
|
||||||
Warning(args ...interface{})
|
|
||||||
Error(args ...interface{})
|
|
||||||
Fatal(args ...interface{})
|
|
||||||
Panic(args ...interface{})
|
|
||||||
|
|
||||||
Debugln(args ...interface{})
|
|
||||||
Infoln(args ...interface{})
|
|
||||||
Println(args ...interface{})
|
|
||||||
Warnln(args ...interface{})
|
|
||||||
Warningln(args ...interface{})
|
|
||||||
Errorln(args ...interface{})
|
|
||||||
Fatalln(args ...interface{})
|
|
||||||
Panicln(args ...interface{})
|
|
||||||
|
|
||||||
// IsDebugEnabled() bool
|
|
||||||
// IsInfoEnabled() bool
|
|
||||||
// IsWarnEnabled() bool
|
|
||||||
// IsErrorEnabled() bool
|
|
||||||
// IsFatalEnabled() bool
|
|
||||||
// IsPanicEnabled() bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ext1FieldLogger (the first extension to FieldLogger) is superfluous, it is
|
|
||||||
// here for consistancy. Do not use. Use Logger or Entry instead.
|
|
||||||
type Ext1FieldLogger interface {
|
|
||||||
FieldLogger
|
|
||||||
Tracef(format string, args ...interface{})
|
|
||||||
Trace(args ...interface{})
|
|
||||||
Traceln(args ...interface{})
|
|
||||||
}
|
|
11
vendor/github.com/sirupsen/logrus/terminal_check_appengine.go
generated
vendored
11
vendor/github.com/sirupsen/logrus/terminal_check_appengine.go
generated
vendored
@ -1,11 +0,0 @@
|
|||||||
// +build appengine
|
|
||||||
|
|
||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
func checkIfTerminal(w io.Writer) bool {
|
|
||||||
return true
|
|
||||||
}
|
|
13
vendor/github.com/sirupsen/logrus/terminal_check_bsd.go
generated
vendored
13
vendor/github.com/sirupsen/logrus/terminal_check_bsd.go
generated
vendored
@ -1,13 +0,0 @@
|
|||||||
// +build darwin dragonfly freebsd netbsd openbsd
|
|
||||||
// +build !js
|
|
||||||
|
|
||||||
package logrus
|
|
||||||
|
|
||||||
import "golang.org/x/sys/unix"
|
|
||||||
|
|
||||||
const ioctlReadTermios = unix.TIOCGETA
|
|
||||||
|
|
||||||
func isTerminal(fd int) bool {
|
|
||||||
_, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
|
|
||||||
return err == nil
|
|
||||||
}
|
|
7
vendor/github.com/sirupsen/logrus/terminal_check_js.go
generated
vendored
7
vendor/github.com/sirupsen/logrus/terminal_check_js.go
generated
vendored
@ -1,7 +0,0 @@
|
|||||||
// +build js
|
|
||||||
|
|
||||||
package logrus
|
|
||||||
|
|
||||||
func isTerminal(fd int) bool {
|
|
||||||
return false
|
|
||||||
}
|
|
11
vendor/github.com/sirupsen/logrus/terminal_check_no_terminal.go
generated
vendored
11
vendor/github.com/sirupsen/logrus/terminal_check_no_terminal.go
generated
vendored
@ -1,11 +0,0 @@
|
|||||||
// +build js nacl plan9
|
|
||||||
|
|
||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
func checkIfTerminal(w io.Writer) bool {
|
|
||||||
return false
|
|
||||||
}
|
|
17
vendor/github.com/sirupsen/logrus/terminal_check_notappengine.go
generated
vendored
17
vendor/github.com/sirupsen/logrus/terminal_check_notappengine.go
generated
vendored
@ -1,17 +0,0 @@
|
|||||||
// +build !appengine,!js,!windows,!nacl,!plan9
|
|
||||||
|
|
||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
func checkIfTerminal(w io.Writer) bool {
|
|
||||||
switch v := w.(type) {
|
|
||||||
case *os.File:
|
|
||||||
return isTerminal(int(v.Fd()))
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
11
vendor/github.com/sirupsen/logrus/terminal_check_solaris.go
generated
vendored
11
vendor/github.com/sirupsen/logrus/terminal_check_solaris.go
generated
vendored
@ -1,11 +0,0 @@
|
|||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
)
|
|
||||||
|
|
||||||
// IsTerminal returns true if the given file descriptor is a terminal.
|
|
||||||
func isTerminal(fd int) bool {
|
|
||||||
_, err := unix.IoctlGetTermio(fd, unix.TCGETA)
|
|
||||||
return err == nil
|
|
||||||
}
|
|
13
vendor/github.com/sirupsen/logrus/terminal_check_unix.go
generated
vendored
13
vendor/github.com/sirupsen/logrus/terminal_check_unix.go
generated
vendored
@ -1,13 +0,0 @@
|
|||||||
// +build linux aix zos
|
|
||||||
// +build !js
|
|
||||||
|
|
||||||
package logrus
|
|
||||||
|
|
||||||
import "golang.org/x/sys/unix"
|
|
||||||
|
|
||||||
const ioctlReadTermios = unix.TCGETS
|
|
||||||
|
|
||||||
func isTerminal(fd int) bool {
|
|
||||||
_, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
|
|
||||||
return err == nil
|
|
||||||
}
|
|
27
vendor/github.com/sirupsen/logrus/terminal_check_windows.go
generated
vendored
27
vendor/github.com/sirupsen/logrus/terminal_check_windows.go
generated
vendored
@ -1,27 +0,0 @@
|
|||||||
// +build !appengine,!js,windows
|
|
||||||
|
|
||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"golang.org/x/sys/windows"
|
|
||||||
)
|
|
||||||
|
|
||||||
func checkIfTerminal(w io.Writer) bool {
|
|
||||||
switch v := w.(type) {
|
|
||||||
case *os.File:
|
|
||||||
handle := windows.Handle(v.Fd())
|
|
||||||
var mode uint32
|
|
||||||
if err := windows.GetConsoleMode(handle, &mode); err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
mode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING
|
|
||||||
if err := windows.SetConsoleMode(handle, mode); err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
339
vendor/github.com/sirupsen/logrus/text_formatter.go
generated
vendored
339
vendor/github.com/sirupsen/logrus/text_formatter.go
generated
vendored
@ -1,339 +0,0 @@
|
|||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"runtime"
|
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
"unicode/utf8"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
red = 31
|
|
||||||
yellow = 33
|
|
||||||
blue = 36
|
|
||||||
gray = 37
|
|
||||||
)
|
|
||||||
|
|
||||||
var baseTimestamp time.Time
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
baseTimestamp = time.Now()
|
|
||||||
}
|
|
||||||
|
|
||||||
// TextFormatter formats logs into text
|
|
||||||
type TextFormatter struct {
|
|
||||||
// Set to true to bypass checking for a TTY before outputting colors.
|
|
||||||
ForceColors bool
|
|
||||||
|
|
||||||
// Force disabling colors.
|
|
||||||
DisableColors bool
|
|
||||||
|
|
||||||
// Force quoting of all values
|
|
||||||
ForceQuote bool
|
|
||||||
|
|
||||||
// DisableQuote disables quoting for all values.
|
|
||||||
// DisableQuote will have a lower priority than ForceQuote.
|
|
||||||
// If both of them are set to true, quote will be forced on all values.
|
|
||||||
DisableQuote bool
|
|
||||||
|
|
||||||
// Override coloring based on CLICOLOR and CLICOLOR_FORCE. - https://bixense.com/clicolors/
|
|
||||||
EnvironmentOverrideColors bool
|
|
||||||
|
|
||||||
// Disable timestamp logging. useful when output is redirected to logging
|
|
||||||
// system that already adds timestamps.
|
|
||||||
DisableTimestamp bool
|
|
||||||
|
|
||||||
// Enable logging the full timestamp when a TTY is attached instead of just
|
|
||||||
// the time passed since beginning of execution.
|
|
||||||
FullTimestamp bool
|
|
||||||
|
|
||||||
// TimestampFormat to use for display when a full timestamp is printed.
|
|
||||||
// The format to use is the same than for time.Format or time.Parse from the standard
|
|
||||||
// library.
|
|
||||||
// The standard Library already provides a set of predefined format.
|
|
||||||
TimestampFormat string
|
|
||||||
|
|
||||||
// The fields are sorted by default for a consistent output. For applications
|
|
||||||
// that log extremely frequently and don't use the JSON formatter this may not
|
|
||||||
// be desired.
|
|
||||||
DisableSorting bool
|
|
||||||
|
|
||||||
// The keys sorting function, when uninitialized it uses sort.Strings.
|
|
||||||
SortingFunc func([]string)
|
|
||||||
|
|
||||||
// Disables the truncation of the level text to 4 characters.
|
|
||||||
DisableLevelTruncation bool
|
|
||||||
|
|
||||||
// PadLevelText Adds padding the level text so that all the levels output at the same length
|
|
||||||
// PadLevelText is a superset of the DisableLevelTruncation option
|
|
||||||
PadLevelText bool
|
|
||||||
|
|
||||||
// QuoteEmptyFields will wrap empty fields in quotes if true
|
|
||||||
QuoteEmptyFields bool
|
|
||||||
|
|
||||||
// Whether the logger's out is to a terminal
|
|
||||||
isTerminal bool
|
|
||||||
|
|
||||||
// FieldMap allows users to customize the names of keys for default fields.
|
|
||||||
// As an example:
|
|
||||||
// formatter := &TextFormatter{
|
|
||||||
// FieldMap: FieldMap{
|
|
||||||
// FieldKeyTime: "@timestamp",
|
|
||||||
// FieldKeyLevel: "@level",
|
|
||||||
// FieldKeyMsg: "@message"}}
|
|
||||||
FieldMap FieldMap
|
|
||||||
|
|
||||||
// CallerPrettyfier can be set by the user to modify the content
|
|
||||||
// of the function and file keys in the data when ReportCaller is
|
|
||||||
// activated. If any of the returned value is the empty string the
|
|
||||||
// corresponding key will be removed from fields.
|
|
||||||
CallerPrettyfier func(*runtime.Frame) (function string, file string)
|
|
||||||
|
|
||||||
terminalInitOnce sync.Once
|
|
||||||
|
|
||||||
// The max length of the level text, generated dynamically on init
|
|
||||||
levelTextMaxLength int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *TextFormatter) init(entry *Entry) {
|
|
||||||
if entry.Logger != nil {
|
|
||||||
f.isTerminal = checkIfTerminal(entry.Logger.Out)
|
|
||||||
}
|
|
||||||
// Get the max length of the level text
|
|
||||||
for _, level := range AllLevels {
|
|
||||||
levelTextLength := utf8.RuneCount([]byte(level.String()))
|
|
||||||
if levelTextLength > f.levelTextMaxLength {
|
|
||||||
f.levelTextMaxLength = levelTextLength
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *TextFormatter) isColored() bool {
|
|
||||||
isColored := f.ForceColors || (f.isTerminal && (runtime.GOOS != "windows"))
|
|
||||||
|
|
||||||
if f.EnvironmentOverrideColors {
|
|
||||||
switch force, ok := os.LookupEnv("CLICOLOR_FORCE"); {
|
|
||||||
case ok && force != "0":
|
|
||||||
isColored = true
|
|
||||||
case ok && force == "0", os.Getenv("CLICOLOR") == "0":
|
|
||||||
isColored = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return isColored && !f.DisableColors
|
|
||||||
}
|
|
||||||
|
|
||||||
// Format renders a single log entry
|
|
||||||
func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
|
|
||||||
data := make(Fields)
|
|
||||||
for k, v := range entry.Data {
|
|
||||||
data[k] = v
|
|
||||||
}
|
|
||||||
prefixFieldClashes(data, f.FieldMap, entry.HasCaller())
|
|
||||||
keys := make([]string, 0, len(data))
|
|
||||||
for k := range data {
|
|
||||||
keys = append(keys, k)
|
|
||||||
}
|
|
||||||
|
|
||||||
var funcVal, fileVal string
|
|
||||||
|
|
||||||
fixedKeys := make([]string, 0, 4+len(data))
|
|
||||||
if !f.DisableTimestamp {
|
|
||||||
fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyTime))
|
|
||||||
}
|
|
||||||
fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyLevel))
|
|
||||||
if entry.Message != "" {
|
|
||||||
fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyMsg))
|
|
||||||
}
|
|
||||||
if entry.err != "" {
|
|
||||||
fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyLogrusError))
|
|
||||||
}
|
|
||||||
if entry.HasCaller() {
|
|
||||||
if f.CallerPrettyfier != nil {
|
|
||||||
funcVal, fileVal = f.CallerPrettyfier(entry.Caller)
|
|
||||||
} else {
|
|
||||||
funcVal = entry.Caller.Function
|
|
||||||
fileVal = fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line)
|
|
||||||
}
|
|
||||||
|
|
||||||
if funcVal != "" {
|
|
||||||
fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyFunc))
|
|
||||||
}
|
|
||||||
if fileVal != "" {
|
|
||||||
fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyFile))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !f.DisableSorting {
|
|
||||||
if f.SortingFunc == nil {
|
|
||||||
sort.Strings(keys)
|
|
||||||
fixedKeys = append(fixedKeys, keys...)
|
|
||||||
} else {
|
|
||||||
if !f.isColored() {
|
|
||||||
fixedKeys = append(fixedKeys, keys...)
|
|
||||||
f.SortingFunc(fixedKeys)
|
|
||||||
} else {
|
|
||||||
f.SortingFunc(keys)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fixedKeys = append(fixedKeys, keys...)
|
|
||||||
}
|
|
||||||
|
|
||||||
var b *bytes.Buffer
|
|
||||||
if entry.Buffer != nil {
|
|
||||||
b = entry.Buffer
|
|
||||||
} else {
|
|
||||||
b = &bytes.Buffer{}
|
|
||||||
}
|
|
||||||
|
|
||||||
f.terminalInitOnce.Do(func() { f.init(entry) })
|
|
||||||
|
|
||||||
timestampFormat := f.TimestampFormat
|
|
||||||
if timestampFormat == "" {
|
|
||||||
timestampFormat = defaultTimestampFormat
|
|
||||||
}
|
|
||||||
if f.isColored() {
|
|
||||||
f.printColored(b, entry, keys, data, timestampFormat)
|
|
||||||
} else {
|
|
||||||
|
|
||||||
for _, key := range fixedKeys {
|
|
||||||
var value interface{}
|
|
||||||
switch {
|
|
||||||
case key == f.FieldMap.resolve(FieldKeyTime):
|
|
||||||
value = entry.Time.Format(timestampFormat)
|
|
||||||
case key == f.FieldMap.resolve(FieldKeyLevel):
|
|
||||||
value = entry.Level.String()
|
|
||||||
case key == f.FieldMap.resolve(FieldKeyMsg):
|
|
||||||
value = entry.Message
|
|
||||||
case key == f.FieldMap.resolve(FieldKeyLogrusError):
|
|
||||||
value = entry.err
|
|
||||||
case key == f.FieldMap.resolve(FieldKeyFunc) && entry.HasCaller():
|
|
||||||
value = funcVal
|
|
||||||
case key == f.FieldMap.resolve(FieldKeyFile) && entry.HasCaller():
|
|
||||||
value = fileVal
|
|
||||||
default:
|
|
||||||
value = data[key]
|
|
||||||
}
|
|
||||||
f.appendKeyValue(b, key, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
b.WriteByte('\n')
|
|
||||||
return b.Bytes(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, data Fields, timestampFormat string) {
|
|
||||||
var levelColor int
|
|
||||||
switch entry.Level {
|
|
||||||
case DebugLevel, TraceLevel:
|
|
||||||
levelColor = gray
|
|
||||||
case WarnLevel:
|
|
||||||
levelColor = yellow
|
|
||||||
case ErrorLevel, FatalLevel, PanicLevel:
|
|
||||||
levelColor = red
|
|
||||||
case InfoLevel:
|
|
||||||
levelColor = blue
|
|
||||||
default:
|
|
||||||
levelColor = blue
|
|
||||||
}
|
|
||||||
|
|
||||||
levelText := strings.ToUpper(entry.Level.String())
|
|
||||||
if !f.DisableLevelTruncation && !f.PadLevelText {
|
|
||||||
levelText = levelText[0:4]
|
|
||||||
}
|
|
||||||
if f.PadLevelText {
|
|
||||||
// Generates the format string used in the next line, for example "%-6s" or "%-7s".
|
|
||||||
// Based on the max level text length.
|
|
||||||
formatString := "%-" + strconv.Itoa(f.levelTextMaxLength) + "s"
|
|
||||||
// Formats the level text by appending spaces up to the max length, for example:
|
|
||||||
// - "INFO "
|
|
||||||
// - "WARNING"
|
|
||||||
levelText = fmt.Sprintf(formatString, levelText)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove a single newline if it already exists in the message to keep
|
|
||||||
// the behavior of logrus text_formatter the same as the stdlib log package
|
|
||||||
entry.Message = strings.TrimSuffix(entry.Message, "\n")
|
|
||||||
|
|
||||||
caller := ""
|
|
||||||
if entry.HasCaller() {
|
|
||||||
funcVal := fmt.Sprintf("%s()", entry.Caller.Function)
|
|
||||||
fileVal := fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line)
|
|
||||||
|
|
||||||
if f.CallerPrettyfier != nil {
|
|
||||||
funcVal, fileVal = f.CallerPrettyfier(entry.Caller)
|
|
||||||
}
|
|
||||||
|
|
||||||
if fileVal == "" {
|
|
||||||
caller = funcVal
|
|
||||||
} else if funcVal == "" {
|
|
||||||
caller = fileVal
|
|
||||||
} else {
|
|
||||||
caller = fileVal + " " + funcVal
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case f.DisableTimestamp:
|
|
||||||
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m%s %-44s ", levelColor, levelText, caller, entry.Message)
|
|
||||||
case !f.FullTimestamp:
|
|
||||||
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d]%s %-44s ", levelColor, levelText, int(entry.Time.Sub(baseTimestamp)/time.Second), caller, entry.Message)
|
|
||||||
default:
|
|
||||||
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s]%s %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), caller, entry.Message)
|
|
||||||
}
|
|
||||||
for _, k := range keys {
|
|
||||||
v := data[k]
|
|
||||||
fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=", levelColor, k)
|
|
||||||
f.appendValue(b, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *TextFormatter) needsQuoting(text string) bool {
|
|
||||||
if f.ForceQuote {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if f.QuoteEmptyFields && len(text) == 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if f.DisableQuote {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, ch := range text {
|
|
||||||
if !((ch >= 'a' && ch <= 'z') ||
|
|
||||||
(ch >= 'A' && ch <= 'Z') ||
|
|
||||||
(ch >= '0' && ch <= '9') ||
|
|
||||||
ch == '-' || ch == '.' || ch == '_' || ch == '/' || ch == '@' || ch == '^' || ch == '+') {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) {
|
|
||||||
if b.Len() > 0 {
|
|
||||||
b.WriteByte(' ')
|
|
||||||
}
|
|
||||||
b.WriteString(key)
|
|
||||||
b.WriteByte('=')
|
|
||||||
f.appendValue(b, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *TextFormatter) appendValue(b *bytes.Buffer, value interface{}) {
|
|
||||||
stringVal, ok := value.(string)
|
|
||||||
if !ok {
|
|
||||||
stringVal = fmt.Sprint(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !f.needsQuoting(stringVal) {
|
|
||||||
b.WriteString(stringVal)
|
|
||||||
} else {
|
|
||||||
b.WriteString(fmt.Sprintf("%q", stringVal))
|
|
||||||
}
|
|
||||||
}
|
|
70
vendor/github.com/sirupsen/logrus/writer.go
generated
vendored
70
vendor/github.com/sirupsen/logrus/writer.go
generated
vendored
@ -1,70 +0,0 @@
|
|||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"io"
|
|
||||||
"runtime"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Writer at INFO level. See WriterLevel for details.
|
|
||||||
func (logger *Logger) Writer() *io.PipeWriter {
|
|
||||||
return logger.WriterLevel(InfoLevel)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriterLevel returns an io.Writer that can be used to write arbitrary text to
|
|
||||||
// the logger at the given log level. Each line written to the writer will be
|
|
||||||
// printed in the usual way using formatters and hooks. The writer is part of an
|
|
||||||
// io.Pipe and it is the callers responsibility to close the writer when done.
|
|
||||||
// This can be used to override the standard library logger easily.
|
|
||||||
func (logger *Logger) WriterLevel(level Level) *io.PipeWriter {
|
|
||||||
return NewEntry(logger).WriterLevel(level)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Writer() *io.PipeWriter {
|
|
||||||
return entry.WriterLevel(InfoLevel)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) WriterLevel(level Level) *io.PipeWriter {
|
|
||||||
reader, writer := io.Pipe()
|
|
||||||
|
|
||||||
var printFunc func(args ...interface{})
|
|
||||||
|
|
||||||
switch level {
|
|
||||||
case TraceLevel:
|
|
||||||
printFunc = entry.Trace
|
|
||||||
case DebugLevel:
|
|
||||||
printFunc = entry.Debug
|
|
||||||
case InfoLevel:
|
|
||||||
printFunc = entry.Info
|
|
||||||
case WarnLevel:
|
|
||||||
printFunc = entry.Warn
|
|
||||||
case ErrorLevel:
|
|
||||||
printFunc = entry.Error
|
|
||||||
case FatalLevel:
|
|
||||||
printFunc = entry.Fatal
|
|
||||||
case PanicLevel:
|
|
||||||
printFunc = entry.Panic
|
|
||||||
default:
|
|
||||||
printFunc = entry.Print
|
|
||||||
}
|
|
||||||
|
|
||||||
go entry.writerScanner(reader, printFunc)
|
|
||||||
runtime.SetFinalizer(writer, writerFinalizer)
|
|
||||||
|
|
||||||
return writer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) writerScanner(reader *io.PipeReader, printFunc func(args ...interface{})) {
|
|
||||||
scanner := bufio.NewScanner(reader)
|
|
||||||
for scanner.Scan() {
|
|
||||||
printFunc(scanner.Text())
|
|
||||||
}
|
|
||||||
if err := scanner.Err(); err != nil {
|
|
||||||
entry.Errorf("Error while reading from Writer: %s", err)
|
|
||||||
}
|
|
||||||
reader.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func writerFinalizer(writer *io.PipeWriter) {
|
|
||||||
writer.Close()
|
|
||||||
}
|
|
19
vendor/go.uber.org/atomic/.codecov.yml
generated
vendored
Normal file
19
vendor/go.uber.org/atomic/.codecov.yml
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
coverage:
|
||||||
|
range: 80..100
|
||||||
|
round: down
|
||||||
|
precision: 2
|
||||||
|
|
||||||
|
status:
|
||||||
|
project: # measuring the overall project coverage
|
||||||
|
default: # context, you can create multiple ones with custom titles
|
||||||
|
enabled: yes # must be yes|true to enable this status
|
||||||
|
target: 100 # specify the target coverage for each commit status
|
||||||
|
# option: "auto" (must increase from parent commit or pull request base)
|
||||||
|
# option: "X%" a static target percentage to hit
|
||||||
|
if_not_found: success # if parent is not found report status as success, error, or failure
|
||||||
|
if_ci_failed: error # if ci fails report status as success, error, or failure
|
||||||
|
|
||||||
|
# Also update COVER_IGNORE_PKGS in the Makefile.
|
||||||
|
ignore:
|
||||||
|
- /internal/gen-atomicint/
|
||||||
|
- /internal/gen-valuewrapper/
|
12
vendor/go.uber.org/atomic/.gitignore
generated
vendored
Normal file
12
vendor/go.uber.org/atomic/.gitignore
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
/bin
|
||||||
|
.DS_Store
|
||||||
|
/vendor
|
||||||
|
cover.html
|
||||||
|
cover.out
|
||||||
|
lint.log
|
||||||
|
|
||||||
|
# Binaries
|
||||||
|
*.test
|
||||||
|
|
||||||
|
# Profiling output
|
||||||
|
*.prof
|
27
vendor/go.uber.org/atomic/.travis.yml
generated
vendored
Normal file
27
vendor/go.uber.org/atomic/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
sudo: false
|
||||||
|
language: go
|
||||||
|
go_import_path: go.uber.org/atomic
|
||||||
|
|
||||||
|
env:
|
||||||
|
global:
|
||||||
|
- GO111MODULE=on
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- go: oldstable
|
||||||
|
- go: stable
|
||||||
|
env: LINT=1
|
||||||
|
|
||||||
|
cache:
|
||||||
|
directories:
|
||||||
|
- vendor
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- go version
|
||||||
|
|
||||||
|
script:
|
||||||
|
- test -z "$LINT" || make lint
|
||||||
|
- make cover
|
||||||
|
|
||||||
|
after_success:
|
||||||
|
- bash <(curl -s https://codecov.io/bash)
|
76
vendor/go.uber.org/atomic/CHANGELOG.md
generated
vendored
Normal file
76
vendor/go.uber.org/atomic/CHANGELOG.md
generated
vendored
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
# Changelog
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [1.7.0] - 2020-09-14
|
||||||
|
### Added
|
||||||
|
- Support JSON serialization and deserialization of primitive atomic types.
|
||||||
|
- Support Text marshalling and unmarshalling for string atomics.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Disallow incorrect comparison of atomic values in a non-atomic way.
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
- Remove dependency on `golang.org/x/{lint, tools}`.
|
||||||
|
|
||||||
|
## [1.6.0] - 2020-02-24
|
||||||
|
### Changed
|
||||||
|
- Drop library dependency on `golang.org/x/{lint, tools}`.
|
||||||
|
|
||||||
|
## [1.5.1] - 2019-11-19
|
||||||
|
- Fix bug where `Bool.CAS` and `Bool.Toggle` do work correctly together
|
||||||
|
causing `CAS` to fail even though the old value matches.
|
||||||
|
|
||||||
|
## [1.5.0] - 2019-10-29
|
||||||
|
### Changed
|
||||||
|
- With Go modules, only the `go.uber.org/atomic` import path is supported now.
|
||||||
|
If you need to use the old import path, please add a `replace` directive to
|
||||||
|
your `go.mod`.
|
||||||
|
|
||||||
|
## [1.4.0] - 2019-05-01
|
||||||
|
### Added
|
||||||
|
- Add `atomic.Error` type for atomic operations on `error` values.
|
||||||
|
|
||||||
|
## [1.3.2] - 2018-05-02
|
||||||
|
### Added
|
||||||
|
- Add `atomic.Duration` type for atomic operations on `time.Duration` values.
|
||||||
|
|
||||||
|
## [1.3.1] - 2017-11-14
|
||||||
|
### Fixed
|
||||||
|
- Revert optimization for `atomic.String.Store("")` which caused data races.
|
||||||
|
|
||||||
|
## [1.3.0] - 2017-11-13
|
||||||
|
### Added
|
||||||
|
- Add `atomic.Bool.CAS` for compare-and-swap semantics on bools.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Optimize `atomic.String.Store("")` by avoiding an allocation.
|
||||||
|
|
||||||
|
## [1.2.0] - 2017-04-12
|
||||||
|
### Added
|
||||||
|
- Shadow `atomic.Value` from `sync/atomic`.
|
||||||
|
|
||||||
|
## [1.1.0] - 2017-03-10
|
||||||
|
### Added
|
||||||
|
- Add atomic `Float64` type.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Support new `go.uber.org/atomic` import path.
|
||||||
|
|
||||||
|
## [1.0.0] - 2016-07-18
|
||||||
|
|
||||||
|
- Initial release.
|
||||||
|
|
||||||
|
[1.7.0]: https://github.com/uber-go/atomic/compare/v1.6.0...v1.7.0
|
||||||
|
[1.6.0]: https://github.com/uber-go/atomic/compare/v1.5.1...v1.6.0
|
||||||
|
[1.5.1]: https://github.com/uber-go/atomic/compare/v1.5.0...v1.5.1
|
||||||
|
[1.5.0]: https://github.com/uber-go/atomic/compare/v1.4.0...v1.5.0
|
||||||
|
[1.4.0]: https://github.com/uber-go/atomic/compare/v1.3.2...v1.4.0
|
||||||
|
[1.3.2]: https://github.com/uber-go/atomic/compare/v1.3.1...v1.3.2
|
||||||
|
[1.3.1]: https://github.com/uber-go/atomic/compare/v1.3.0...v1.3.1
|
||||||
|
[1.3.0]: https://github.com/uber-go/atomic/compare/v1.2.0...v1.3.0
|
||||||
|
[1.2.0]: https://github.com/uber-go/atomic/compare/v1.1.0...v1.2.0
|
||||||
|
[1.1.0]: https://github.com/uber-go/atomic/compare/v1.0.0...v1.1.0
|
||||||
|
[1.0.0]: https://github.com/uber-go/atomic/releases/tag/v1.0.0
|
19
vendor/go.uber.org/atomic/LICENSE.txt
generated
vendored
Normal file
19
vendor/go.uber.org/atomic/LICENSE.txt
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
Copyright (c) 2016 Uber Technologies, Inc.
|
||||||
|
|
||||||
|
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.
|
78
vendor/go.uber.org/atomic/Makefile
generated
vendored
Normal file
78
vendor/go.uber.org/atomic/Makefile
generated
vendored
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
# Directory to place `go install`ed binaries into.
|
||||||
|
export GOBIN ?= $(shell pwd)/bin
|
||||||
|
|
||||||
|
GOLINT = $(GOBIN)/golint
|
||||||
|
GEN_ATOMICINT = $(GOBIN)/gen-atomicint
|
||||||
|
GEN_ATOMICWRAPPER = $(GOBIN)/gen-atomicwrapper
|
||||||
|
STATICCHECK = $(GOBIN)/staticcheck
|
||||||
|
|
||||||
|
GO_FILES ?= $(shell find . '(' -path .git -o -path vendor ')' -prune -o -name '*.go' -print)
|
||||||
|
|
||||||
|
# Also update ignore section in .codecov.yml.
|
||||||
|
COVER_IGNORE_PKGS = \
|
||||||
|
go.uber.org/atomic/internal/gen-atomicint \
|
||||||
|
go.uber.org/atomic/internal/gen-atomicwrapper
|
||||||
|
|
||||||
|
.PHONY: build
|
||||||
|
build:
|
||||||
|
go build ./...
|
||||||
|
|
||||||
|
.PHONY: test
|
||||||
|
test:
|
||||||
|
go test -race ./...
|
||||||
|
|
||||||
|
.PHONY: gofmt
|
||||||
|
gofmt:
|
||||||
|
$(eval FMT_LOG := $(shell mktemp -t gofmt.XXXXX))
|
||||||
|
gofmt -e -s -l $(GO_FILES) > $(FMT_LOG) || true
|
||||||
|
@[ ! -s "$(FMT_LOG)" ] || (echo "gofmt failed:" && cat $(FMT_LOG) && false)
|
||||||
|
|
||||||
|
$(GOLINT):
|
||||||
|
cd tools && go install golang.org/x/lint/golint
|
||||||
|
|
||||||
|
$(STATICCHECK):
|
||||||
|
cd tools && go install honnef.co/go/tools/cmd/staticcheck
|
||||||
|
|
||||||
|
$(GEN_ATOMICWRAPPER): $(wildcard ./internal/gen-atomicwrapper/*)
|
||||||
|
go build -o $@ ./internal/gen-atomicwrapper
|
||||||
|
|
||||||
|
$(GEN_ATOMICINT): $(wildcard ./internal/gen-atomicint/*)
|
||||||
|
go build -o $@ ./internal/gen-atomicint
|
||||||
|
|
||||||
|
.PHONY: golint
|
||||||
|
golint: $(GOLINT)
|
||||||
|
$(GOLINT) ./...
|
||||||
|
|
||||||
|
.PHONY: staticcheck
|
||||||
|
staticcheck: $(STATICCHECK)
|
||||||
|
$(STATICCHECK) ./...
|
||||||
|
|
||||||
|
.PHONY: lint
|
||||||
|
lint: gofmt golint staticcheck generatenodirty
|
||||||
|
|
||||||
|
# comma separated list of packages to consider for code coverage.
|
||||||
|
COVER_PKG = $(shell \
|
||||||
|
go list -find ./... | \
|
||||||
|
grep -v $(foreach pkg,$(COVER_IGNORE_PKGS),-e "^$(pkg)$$") | \
|
||||||
|
paste -sd, -)
|
||||||
|
|
||||||
|
.PHONY: cover
|
||||||
|
cover:
|
||||||
|
go test -coverprofile=cover.out -coverpkg $(COVER_PKG) -v ./...
|
||||||
|
go tool cover -html=cover.out -o cover.html
|
||||||
|
|
||||||
|
.PHONY: generate
|
||||||
|
generate: $(GEN_ATOMICINT) $(GEN_ATOMICWRAPPER)
|
||||||
|
go generate ./...
|
||||||
|
|
||||||
|
.PHONY: generatenodirty
|
||||||
|
generatenodirty:
|
||||||
|
@[ -z "$$(git status --porcelain)" ] || ( \
|
||||||
|
echo "Working tree is dirty. Commit your changes first."; \
|
||||||
|
exit 1 )
|
||||||
|
@make generate
|
||||||
|
@status=$$(git status --porcelain); \
|
||||||
|
[ -z "$$status" ] || ( \
|
||||||
|
echo "Working tree is dirty after `make generate`:"; \
|
||||||
|
echo "$$status"; \
|
||||||
|
echo "Please ensure that the generated code is up-to-date." )
|
63
vendor/go.uber.org/atomic/README.md
generated
vendored
Normal file
63
vendor/go.uber.org/atomic/README.md
generated
vendored
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
# atomic [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov] [![Go Report Card][reportcard-img]][reportcard]
|
||||||
|
|
||||||
|
Simple wrappers for primitive types to enforce atomic access.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ go get -u go.uber.org/atomic@v1
|
||||||
|
```
|
||||||
|
|
||||||
|
### Legacy Import Path
|
||||||
|
|
||||||
|
As of v1.5.0, the import path `go.uber.org/atomic` is the only supported way
|
||||||
|
of using this package. If you are using Go modules, this package will fail to
|
||||||
|
compile with the legacy import path path `github.com/uber-go/atomic`.
|
||||||
|
|
||||||
|
We recommend migrating your code to the new import path but if you're unable
|
||||||
|
to do so, or if your dependencies are still using the old import path, you
|
||||||
|
will have to add a `replace` directive to your `go.mod` file downgrading the
|
||||||
|
legacy import path to an older version.
|
||||||
|
|
||||||
|
```
|
||||||
|
replace github.com/uber-go/atomic => github.com/uber-go/atomic v1.4.0
|
||||||
|
```
|
||||||
|
|
||||||
|
You can do so automatically by running the following command.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ go mod edit -replace github.com/uber-go/atomic=github.com/uber-go/atomic@v1.4.0
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
The standard library's `sync/atomic` is powerful, but it's easy to forget which
|
||||||
|
variables must be accessed atomically. `go.uber.org/atomic` preserves all the
|
||||||
|
functionality of the standard library, but wraps the primitive types to
|
||||||
|
provide a safer, more convenient API.
|
||||||
|
|
||||||
|
```go
|
||||||
|
var atom atomic.Uint32
|
||||||
|
atom.Store(42)
|
||||||
|
atom.Sub(2)
|
||||||
|
atom.CAS(40, 11)
|
||||||
|
```
|
||||||
|
|
||||||
|
See the [documentation][doc] for a complete API specification.
|
||||||
|
|
||||||
|
## Development Status
|
||||||
|
|
||||||
|
Stable.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Released under the [MIT License](LICENSE.txt).
|
||||||
|
|
||||||
|
[doc-img]: https://godoc.org/github.com/uber-go/atomic?status.svg
|
||||||
|
[doc]: https://godoc.org/go.uber.org/atomic
|
||||||
|
[ci-img]: https://travis-ci.com/uber-go/atomic.svg?branch=master
|
||||||
|
[ci]: https://travis-ci.com/uber-go/atomic
|
||||||
|
[cov-img]: https://codecov.io/gh/uber-go/atomic/branch/master/graph/badge.svg
|
||||||
|
[cov]: https://codecov.io/gh/uber-go/atomic
|
||||||
|
[reportcard-img]: https://goreportcard.com/badge/go.uber.org/atomic
|
||||||
|
[reportcard]: https://goreportcard.com/report/go.uber.org/atomic
|
81
vendor/go.uber.org/atomic/bool.go
generated
vendored
Normal file
81
vendor/go.uber.org/atomic/bool.go
generated
vendored
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
// @generated Code generated by gen-atomicwrapper.
|
||||||
|
|
||||||
|
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Bool is an atomic type-safe wrapper for bool values.
|
||||||
|
type Bool struct {
|
||||||
|
_ nocmp // disallow non-atomic comparison
|
||||||
|
|
||||||
|
v Uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
var _zeroBool bool
|
||||||
|
|
||||||
|
// NewBool creates a new Bool.
|
||||||
|
func NewBool(v bool) *Bool {
|
||||||
|
x := &Bool{}
|
||||||
|
if v != _zeroBool {
|
||||||
|
x.Store(v)
|
||||||
|
}
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load atomically loads the wrapped bool.
|
||||||
|
func (x *Bool) Load() bool {
|
||||||
|
return truthy(x.v.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store atomically stores the passed bool.
|
||||||
|
func (x *Bool) Store(v bool) {
|
||||||
|
x.v.Store(boolToInt(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CAS is an atomic compare-and-swap for bool values.
|
||||||
|
func (x *Bool) CAS(o, n bool) bool {
|
||||||
|
return x.v.CAS(boolToInt(o), boolToInt(n))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap atomically stores the given bool and returns the old
|
||||||
|
// value.
|
||||||
|
func (x *Bool) Swap(o bool) bool {
|
||||||
|
return truthy(x.v.Swap(boolToInt(o)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON encodes the wrapped bool into JSON.
|
||||||
|
func (x *Bool) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(x.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON decodes a bool from JSON.
|
||||||
|
func (x *Bool) UnmarshalJSON(b []byte) error {
|
||||||
|
var v bool
|
||||||
|
if err := json.Unmarshal(b, &v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
x.Store(v)
|
||||||
|
return nil
|
||||||
|
}
|
53
vendor/go.uber.org/atomic/bool_ext.go
generated
vendored
Normal file
53
vendor/go.uber.org/atomic/bool_ext.go
generated
vendored
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:generate bin/gen-atomicwrapper -name=Bool -type=bool -wrapped=Uint32 -pack=boolToInt -unpack=truthy -cas -swap -json -file=bool.go
|
||||||
|
|
||||||
|
func truthy(n uint32) bool {
|
||||||
|
return n == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func boolToInt(b bool) uint32 {
|
||||||
|
if b {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Toggle atomically negates the Boolean and returns the previous value.
|
||||||
|
func (b *Bool) Toggle() bool {
|
||||||
|
for {
|
||||||
|
old := b.Load()
|
||||||
|
if b.CAS(old, !old) {
|
||||||
|
return old
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// String encodes the wrapped value as a string.
|
||||||
|
func (b *Bool) String() string {
|
||||||
|
return strconv.FormatBool(b.Load())
|
||||||
|
}
|
23
vendor/go.uber.org/atomic/doc.go
generated
vendored
Normal file
23
vendor/go.uber.org/atomic/doc.go
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// Package atomic provides simple wrappers around numerics to enforce atomic
|
||||||
|
// access.
|
||||||
|
package atomic
|
82
vendor/go.uber.org/atomic/duration.go
generated
vendored
Normal file
82
vendor/go.uber.org/atomic/duration.go
generated
vendored
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
// @generated Code generated by gen-atomicwrapper.
|
||||||
|
|
||||||
|
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Duration is an atomic type-safe wrapper for time.Duration values.
|
||||||
|
type Duration struct {
|
||||||
|
_ nocmp // disallow non-atomic comparison
|
||||||
|
|
||||||
|
v Int64
|
||||||
|
}
|
||||||
|
|
||||||
|
var _zeroDuration time.Duration
|
||||||
|
|
||||||
|
// NewDuration creates a new Duration.
|
||||||
|
func NewDuration(v time.Duration) *Duration {
|
||||||
|
x := &Duration{}
|
||||||
|
if v != _zeroDuration {
|
||||||
|
x.Store(v)
|
||||||
|
}
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load atomically loads the wrapped time.Duration.
|
||||||
|
func (x *Duration) Load() time.Duration {
|
||||||
|
return time.Duration(x.v.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store atomically stores the passed time.Duration.
|
||||||
|
func (x *Duration) Store(v time.Duration) {
|
||||||
|
x.v.Store(int64(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CAS is an atomic compare-and-swap for time.Duration values.
|
||||||
|
func (x *Duration) CAS(o, n time.Duration) bool {
|
||||||
|
return x.v.CAS(int64(o), int64(n))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap atomically stores the given time.Duration and returns the old
|
||||||
|
// value.
|
||||||
|
func (x *Duration) Swap(o time.Duration) time.Duration {
|
||||||
|
return time.Duration(x.v.Swap(int64(o)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON encodes the wrapped time.Duration into JSON.
|
||||||
|
func (x *Duration) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(x.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON decodes a time.Duration from JSON.
|
||||||
|
func (x *Duration) UnmarshalJSON(b []byte) error {
|
||||||
|
var v time.Duration
|
||||||
|
if err := json.Unmarshal(b, &v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
x.Store(v)
|
||||||
|
return nil
|
||||||
|
}
|
40
vendor/go.uber.org/atomic/duration_ext.go
generated
vendored
Normal file
40
vendor/go.uber.org/atomic/duration_ext.go
generated
vendored
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
//go:generate bin/gen-atomicwrapper -name=Duration -type=time.Duration -wrapped=Int64 -pack=int64 -unpack=time.Duration -cas -swap -json -imports time -file=duration.go
|
||||||
|
|
||||||
|
// Add atomically adds to the wrapped time.Duration and returns the new value.
|
||||||
|
func (d *Duration) Add(n time.Duration) time.Duration {
|
||||||
|
return time.Duration(d.v.Add(int64(n)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sub atomically subtracts from the wrapped time.Duration and returns the new value.
|
||||||
|
func (d *Duration) Sub(n time.Duration) time.Duration {
|
||||||
|
return time.Duration(d.v.Sub(int64(n)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// String encodes the wrapped value as a string.
|
||||||
|
func (d *Duration) String() string {
|
||||||
|
return d.Load().String()
|
||||||
|
}
|
51
vendor/go.uber.org/atomic/error.go
generated
vendored
Normal file
51
vendor/go.uber.org/atomic/error.go
generated
vendored
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
// @generated Code generated by gen-atomicwrapper.
|
||||||
|
|
||||||
|
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
// Error is an atomic type-safe wrapper for error values.
|
||||||
|
type Error struct {
|
||||||
|
_ nocmp // disallow non-atomic comparison
|
||||||
|
|
||||||
|
v Value
|
||||||
|
}
|
||||||
|
|
||||||
|
var _zeroError error
|
||||||
|
|
||||||
|
// NewError creates a new Error.
|
||||||
|
func NewError(v error) *Error {
|
||||||
|
x := &Error{}
|
||||||
|
if v != _zeroError {
|
||||||
|
x.Store(v)
|
||||||
|
}
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load atomically loads the wrapped error.
|
||||||
|
func (x *Error) Load() error {
|
||||||
|
return unpackError(x.v.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store atomically stores the passed error.
|
||||||
|
func (x *Error) Store(v error) {
|
||||||
|
x.v.Store(packError(v))
|
||||||
|
}
|
39
vendor/go.uber.org/atomic/error_ext.go
generated
vendored
Normal file
39
vendor/go.uber.org/atomic/error_ext.go
generated
vendored
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
// atomic.Value panics on nil inputs, or if the underlying type changes.
|
||||||
|
// Stabilize by always storing a custom struct that we control.
|
||||||
|
|
||||||
|
//go:generate bin/gen-atomicwrapper -name=Error -type=error -wrapped=Value -pack=packError -unpack=unpackError -file=error.go
|
||||||
|
|
||||||
|
type packedError struct{ Value error }
|
||||||
|
|
||||||
|
func packError(v error) interface{} {
|
||||||
|
return packedError{v}
|
||||||
|
}
|
||||||
|
|
||||||
|
func unpackError(v interface{}) error {
|
||||||
|
if err, ok := v.(packedError); ok {
|
||||||
|
return err.Value
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
76
vendor/go.uber.org/atomic/float64.go
generated
vendored
Normal file
76
vendor/go.uber.org/atomic/float64.go
generated
vendored
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
// @generated Code generated by gen-atomicwrapper.
|
||||||
|
|
||||||
|
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Float64 is an atomic type-safe wrapper for float64 values.
|
||||||
|
type Float64 struct {
|
||||||
|
_ nocmp // disallow non-atomic comparison
|
||||||
|
|
||||||
|
v Uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
var _zeroFloat64 float64
|
||||||
|
|
||||||
|
// NewFloat64 creates a new Float64.
|
||||||
|
func NewFloat64(v float64) *Float64 {
|
||||||
|
x := &Float64{}
|
||||||
|
if v != _zeroFloat64 {
|
||||||
|
x.Store(v)
|
||||||
|
}
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load atomically loads the wrapped float64.
|
||||||
|
func (x *Float64) Load() float64 {
|
||||||
|
return math.Float64frombits(x.v.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store atomically stores the passed float64.
|
||||||
|
func (x *Float64) Store(v float64) {
|
||||||
|
x.v.Store(math.Float64bits(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CAS is an atomic compare-and-swap for float64 values.
|
||||||
|
func (x *Float64) CAS(o, n float64) bool {
|
||||||
|
return x.v.CAS(math.Float64bits(o), math.Float64bits(n))
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON encodes the wrapped float64 into JSON.
|
||||||
|
func (x *Float64) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(x.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON decodes a float64 from JSON.
|
||||||
|
func (x *Float64) UnmarshalJSON(b []byte) error {
|
||||||
|
var v float64
|
||||||
|
if err := json.Unmarshal(b, &v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
x.Store(v)
|
||||||
|
return nil
|
||||||
|
}
|
47
vendor/go.uber.org/atomic/float64_ext.go
generated
vendored
Normal file
47
vendor/go.uber.org/atomic/float64_ext.go
generated
vendored
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
//go:generate bin/gen-atomicwrapper -name=Float64 -type=float64 -wrapped=Uint64 -pack=math.Float64bits -unpack=math.Float64frombits -cas -json -imports math -file=float64.go
|
||||||
|
|
||||||
|
// Add atomically adds to the wrapped float64 and returns the new value.
|
||||||
|
func (f *Float64) Add(s float64) float64 {
|
||||||
|
for {
|
||||||
|
old := f.Load()
|
||||||
|
new := old + s
|
||||||
|
if f.CAS(old, new) {
|
||||||
|
return new
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sub atomically subtracts from the wrapped float64 and returns the new value.
|
||||||
|
func (f *Float64) Sub(s float64) float64 {
|
||||||
|
return f.Add(-s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// String encodes the wrapped value as a string.
|
||||||
|
func (f *Float64) String() string {
|
||||||
|
// 'g' is the behavior for floats with %v.
|
||||||
|
return strconv.FormatFloat(f.Load(), 'g', -1, 64)
|
||||||
|
}
|
26
vendor/go.uber.org/atomic/gen.go
generated
vendored
Normal file
26
vendor/go.uber.org/atomic/gen.go
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
//go:generate bin/gen-atomicint -name=Int32 -wrapped=int32 -file=int32.go
|
||||||
|
//go:generate bin/gen-atomicint -name=Int64 -wrapped=int64 -file=int64.go
|
||||||
|
//go:generate bin/gen-atomicint -name=Uint32 -wrapped=uint32 -unsigned -file=uint32.go
|
||||||
|
//go:generate bin/gen-atomicint -name=Uint64 -wrapped=uint64 -unsigned -file=uint64.go
|
102
vendor/go.uber.org/atomic/int32.go
generated
vendored
Normal file
102
vendor/go.uber.org/atomic/int32.go
generated
vendored
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
// @generated Code generated by gen-atomicint.
|
||||||
|
|
||||||
|
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"strconv"
|
||||||
|
"sync/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Int32 is an atomic wrapper around int32.
|
||||||
|
type Int32 struct {
|
||||||
|
_ nocmp // disallow non-atomic comparison
|
||||||
|
|
||||||
|
v int32
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInt32 creates a new Int32.
|
||||||
|
func NewInt32(i int32) *Int32 {
|
||||||
|
return &Int32{v: i}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load atomically loads the wrapped value.
|
||||||
|
func (i *Int32) Load() int32 {
|
||||||
|
return atomic.LoadInt32(&i.v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add atomically adds to the wrapped int32 and returns the new value.
|
||||||
|
func (i *Int32) Add(n int32) int32 {
|
||||||
|
return atomic.AddInt32(&i.v, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sub atomically subtracts from the wrapped int32 and returns the new value.
|
||||||
|
func (i *Int32) Sub(n int32) int32 {
|
||||||
|
return atomic.AddInt32(&i.v, -n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inc atomically increments the wrapped int32 and returns the new value.
|
||||||
|
func (i *Int32) Inc() int32 {
|
||||||
|
return i.Add(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dec atomically decrements the wrapped int32 and returns the new value.
|
||||||
|
func (i *Int32) Dec() int32 {
|
||||||
|
return i.Sub(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CAS is an atomic compare-and-swap.
|
||||||
|
func (i *Int32) CAS(old, new int32) bool {
|
||||||
|
return atomic.CompareAndSwapInt32(&i.v, old, new)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store atomically stores the passed value.
|
||||||
|
func (i *Int32) Store(n int32) {
|
||||||
|
atomic.StoreInt32(&i.v, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap atomically swaps the wrapped int32 and returns the old value.
|
||||||
|
func (i *Int32) Swap(n int32) int32 {
|
||||||
|
return atomic.SwapInt32(&i.v, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON encodes the wrapped int32 into JSON.
|
||||||
|
func (i *Int32) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(i.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON decodes JSON into the wrapped int32.
|
||||||
|
func (i *Int32) UnmarshalJSON(b []byte) error {
|
||||||
|
var v int32
|
||||||
|
if err := json.Unmarshal(b, &v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i.Store(v)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// String encodes the wrapped value as a string.
|
||||||
|
func (i *Int32) String() string {
|
||||||
|
v := i.Load()
|
||||||
|
return strconv.FormatInt(int64(v), 10)
|
||||||
|
}
|
102
vendor/go.uber.org/atomic/int64.go
generated
vendored
Normal file
102
vendor/go.uber.org/atomic/int64.go
generated
vendored
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
// @generated Code generated by gen-atomicint.
|
||||||
|
|
||||||
|
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"strconv"
|
||||||
|
"sync/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Int64 is an atomic wrapper around int64.
|
||||||
|
type Int64 struct {
|
||||||
|
_ nocmp // disallow non-atomic comparison
|
||||||
|
|
||||||
|
v int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInt64 creates a new Int64.
|
||||||
|
func NewInt64(i int64) *Int64 {
|
||||||
|
return &Int64{v: i}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load atomically loads the wrapped value.
|
||||||
|
func (i *Int64) Load() int64 {
|
||||||
|
return atomic.LoadInt64(&i.v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add atomically adds to the wrapped int64 and returns the new value.
|
||||||
|
func (i *Int64) Add(n int64) int64 {
|
||||||
|
return atomic.AddInt64(&i.v, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sub atomically subtracts from the wrapped int64 and returns the new value.
|
||||||
|
func (i *Int64) Sub(n int64) int64 {
|
||||||
|
return atomic.AddInt64(&i.v, -n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inc atomically increments the wrapped int64 and returns the new value.
|
||||||
|
func (i *Int64) Inc() int64 {
|
||||||
|
return i.Add(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dec atomically decrements the wrapped int64 and returns the new value.
|
||||||
|
func (i *Int64) Dec() int64 {
|
||||||
|
return i.Sub(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CAS is an atomic compare-and-swap.
|
||||||
|
func (i *Int64) CAS(old, new int64) bool {
|
||||||
|
return atomic.CompareAndSwapInt64(&i.v, old, new)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store atomically stores the passed value.
|
||||||
|
func (i *Int64) Store(n int64) {
|
||||||
|
atomic.StoreInt64(&i.v, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap atomically swaps the wrapped int64 and returns the old value.
|
||||||
|
func (i *Int64) Swap(n int64) int64 {
|
||||||
|
return atomic.SwapInt64(&i.v, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON encodes the wrapped int64 into JSON.
|
||||||
|
func (i *Int64) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(i.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON decodes JSON into the wrapped int64.
|
||||||
|
func (i *Int64) UnmarshalJSON(b []byte) error {
|
||||||
|
var v int64
|
||||||
|
if err := json.Unmarshal(b, &v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i.Store(v)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// String encodes the wrapped value as a string.
|
||||||
|
func (i *Int64) String() string {
|
||||||
|
v := i.Load()
|
||||||
|
return strconv.FormatInt(int64(v), 10)
|
||||||
|
}
|
35
vendor/go.uber.org/atomic/nocmp.go
generated
vendored
Normal file
35
vendor/go.uber.org/atomic/nocmp.go
generated
vendored
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
// nocmp is an uncomparable struct. Embed this inside another struct to make
|
||||||
|
// it uncomparable.
|
||||||
|
//
|
||||||
|
// type Foo struct {
|
||||||
|
// nocmp
|
||||||
|
// // ...
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// This DOES NOT:
|
||||||
|
//
|
||||||
|
// - Disallow shallow copies of structs
|
||||||
|
// - Disallow comparison of pointers to uncomparable structs
|
||||||
|
type nocmp [0]func()
|
54
vendor/go.uber.org/atomic/string.go
generated
vendored
Normal file
54
vendor/go.uber.org/atomic/string.go
generated
vendored
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
// @generated Code generated by gen-atomicwrapper.
|
||||||
|
|
||||||
|
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
// String is an atomic type-safe wrapper for string values.
|
||||||
|
type String struct {
|
||||||
|
_ nocmp // disallow non-atomic comparison
|
||||||
|
|
||||||
|
v Value
|
||||||
|
}
|
||||||
|
|
||||||
|
var _zeroString string
|
||||||
|
|
||||||
|
// NewString creates a new String.
|
||||||
|
func NewString(v string) *String {
|
||||||
|
x := &String{}
|
||||||
|
if v != _zeroString {
|
||||||
|
x.Store(v)
|
||||||
|
}
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load atomically loads the wrapped string.
|
||||||
|
func (x *String) Load() string {
|
||||||
|
if v := x.v.Load(); v != nil {
|
||||||
|
return v.(string)
|
||||||
|
}
|
||||||
|
return _zeroString
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store atomically stores the passed string.
|
||||||
|
func (x *String) Store(v string) {
|
||||||
|
x.v.Store(v)
|
||||||
|
}
|
43
vendor/go.uber.org/atomic/string_ext.go
generated
vendored
Normal file
43
vendor/go.uber.org/atomic/string_ext.go
generated
vendored
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
//go:generate bin/gen-atomicwrapper -name=String -type=string -wrapped=Value -file=string.go
|
||||||
|
|
||||||
|
// String returns the wrapped value.
|
||||||
|
func (s *String) String() string {
|
||||||
|
return s.Load()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalText encodes the wrapped string into a textual form.
|
||||||
|
//
|
||||||
|
// This makes it encodable as JSON, YAML, XML, and more.
|
||||||
|
func (s *String) MarshalText() ([]byte, error) {
|
||||||
|
return []byte(s.Load()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalText decodes text and replaces the wrapped string with it.
|
||||||
|
//
|
||||||
|
// This makes it decodable from JSON, YAML, XML, and more.
|
||||||
|
func (s *String) UnmarshalText(b []byte) error {
|
||||||
|
s.Store(string(b))
|
||||||
|
return nil
|
||||||
|
}
|
102
vendor/go.uber.org/atomic/uint32.go
generated
vendored
Normal file
102
vendor/go.uber.org/atomic/uint32.go
generated
vendored
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
// @generated Code generated by gen-atomicint.
|
||||||
|
|
||||||
|
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"strconv"
|
||||||
|
"sync/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Uint32 is an atomic wrapper around uint32.
|
||||||
|
type Uint32 struct {
|
||||||
|
_ nocmp // disallow non-atomic comparison
|
||||||
|
|
||||||
|
v uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUint32 creates a new Uint32.
|
||||||
|
func NewUint32(i uint32) *Uint32 {
|
||||||
|
return &Uint32{v: i}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load atomically loads the wrapped value.
|
||||||
|
func (i *Uint32) Load() uint32 {
|
||||||
|
return atomic.LoadUint32(&i.v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add atomically adds to the wrapped uint32 and returns the new value.
|
||||||
|
func (i *Uint32) Add(n uint32) uint32 {
|
||||||
|
return atomic.AddUint32(&i.v, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sub atomically subtracts from the wrapped uint32 and returns the new value.
|
||||||
|
func (i *Uint32) Sub(n uint32) uint32 {
|
||||||
|
return atomic.AddUint32(&i.v, ^(n - 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inc atomically increments the wrapped uint32 and returns the new value.
|
||||||
|
func (i *Uint32) Inc() uint32 {
|
||||||
|
return i.Add(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dec atomically decrements the wrapped uint32 and returns the new value.
|
||||||
|
func (i *Uint32) Dec() uint32 {
|
||||||
|
return i.Sub(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CAS is an atomic compare-and-swap.
|
||||||
|
func (i *Uint32) CAS(old, new uint32) bool {
|
||||||
|
return atomic.CompareAndSwapUint32(&i.v, old, new)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store atomically stores the passed value.
|
||||||
|
func (i *Uint32) Store(n uint32) {
|
||||||
|
atomic.StoreUint32(&i.v, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap atomically swaps the wrapped uint32 and returns the old value.
|
||||||
|
func (i *Uint32) Swap(n uint32) uint32 {
|
||||||
|
return atomic.SwapUint32(&i.v, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON encodes the wrapped uint32 into JSON.
|
||||||
|
func (i *Uint32) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(i.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON decodes JSON into the wrapped uint32.
|
||||||
|
func (i *Uint32) UnmarshalJSON(b []byte) error {
|
||||||
|
var v uint32
|
||||||
|
if err := json.Unmarshal(b, &v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i.Store(v)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// String encodes the wrapped value as a string.
|
||||||
|
func (i *Uint32) String() string {
|
||||||
|
v := i.Load()
|
||||||
|
return strconv.FormatUint(uint64(v), 10)
|
||||||
|
}
|
102
vendor/go.uber.org/atomic/uint64.go
generated
vendored
Normal file
102
vendor/go.uber.org/atomic/uint64.go
generated
vendored
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
// @generated Code generated by gen-atomicint.
|
||||||
|
|
||||||
|
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"strconv"
|
||||||
|
"sync/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Uint64 is an atomic wrapper around uint64.
|
||||||
|
type Uint64 struct {
|
||||||
|
_ nocmp // disallow non-atomic comparison
|
||||||
|
|
||||||
|
v uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUint64 creates a new Uint64.
|
||||||
|
func NewUint64(i uint64) *Uint64 {
|
||||||
|
return &Uint64{v: i}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load atomically loads the wrapped value.
|
||||||
|
func (i *Uint64) Load() uint64 {
|
||||||
|
return atomic.LoadUint64(&i.v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add atomically adds to the wrapped uint64 and returns the new value.
|
||||||
|
func (i *Uint64) Add(n uint64) uint64 {
|
||||||
|
return atomic.AddUint64(&i.v, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sub atomically subtracts from the wrapped uint64 and returns the new value.
|
||||||
|
func (i *Uint64) Sub(n uint64) uint64 {
|
||||||
|
return atomic.AddUint64(&i.v, ^(n - 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inc atomically increments the wrapped uint64 and returns the new value.
|
||||||
|
func (i *Uint64) Inc() uint64 {
|
||||||
|
return i.Add(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dec atomically decrements the wrapped uint64 and returns the new value.
|
||||||
|
func (i *Uint64) Dec() uint64 {
|
||||||
|
return i.Sub(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CAS is an atomic compare-and-swap.
|
||||||
|
func (i *Uint64) CAS(old, new uint64) bool {
|
||||||
|
return atomic.CompareAndSwapUint64(&i.v, old, new)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store atomically stores the passed value.
|
||||||
|
func (i *Uint64) Store(n uint64) {
|
||||||
|
atomic.StoreUint64(&i.v, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap atomically swaps the wrapped uint64 and returns the old value.
|
||||||
|
func (i *Uint64) Swap(n uint64) uint64 {
|
||||||
|
return atomic.SwapUint64(&i.v, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON encodes the wrapped uint64 into JSON.
|
||||||
|
func (i *Uint64) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(i.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON decodes JSON into the wrapped uint64.
|
||||||
|
func (i *Uint64) UnmarshalJSON(b []byte) error {
|
||||||
|
var v uint64
|
||||||
|
if err := json.Unmarshal(b, &v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i.Store(v)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// String encodes the wrapped value as a string.
|
||||||
|
func (i *Uint64) String() string {
|
||||||
|
v := i.Load()
|
||||||
|
return strconv.FormatUint(uint64(v), 10)
|
||||||
|
}
|
31
vendor/go.uber.org/atomic/value.go
generated
vendored
Normal file
31
vendor/go.uber.org/atomic/value.go
generated
vendored
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package atomic
|
||||||
|
|
||||||
|
import "sync/atomic"
|
||||||
|
|
||||||
|
// Value shadows the type of the same name from sync/atomic
|
||||||
|
// https://godoc.org/sync/atomic#Value
|
||||||
|
type Value struct {
|
||||||
|
atomic.Value
|
||||||
|
|
||||||
|
_ nocmp // disallow non-atomic comparison
|
||||||
|
}
|
15
vendor/go.uber.org/multierr/.codecov.yml
generated
vendored
Normal file
15
vendor/go.uber.org/multierr/.codecov.yml
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
coverage:
|
||||||
|
range: 80..100
|
||||||
|
round: down
|
||||||
|
precision: 2
|
||||||
|
|
||||||
|
status:
|
||||||
|
project: # measuring the overall project coverage
|
||||||
|
default: # context, you can create multiple ones with custom titles
|
||||||
|
enabled: yes # must be yes|true to enable this status
|
||||||
|
target: 100 # specify the target coverage for each commit status
|
||||||
|
# option: "auto" (must increase from parent commit or pull request base)
|
||||||
|
# option: "X%" a static target percentage to hit
|
||||||
|
if_not_found: success # if parent is not found report status as success, error, or failure
|
||||||
|
if_ci_failed: error # if ci fails report status as success, error, or failure
|
||||||
|
|
4
vendor/go.uber.org/multierr/.gitignore
generated
vendored
Normal file
4
vendor/go.uber.org/multierr/.gitignore
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
/vendor
|
||||||
|
cover.html
|
||||||
|
cover.out
|
||||||
|
/bin
|
23
vendor/go.uber.org/multierr/.travis.yml
generated
vendored
Normal file
23
vendor/go.uber.org/multierr/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
sudo: false
|
||||||
|
language: go
|
||||||
|
go_import_path: go.uber.org/multierr
|
||||||
|
|
||||||
|
env:
|
||||||
|
global:
|
||||||
|
- GO111MODULE=on
|
||||||
|
|
||||||
|
go:
|
||||||
|
- oldstable
|
||||||
|
- stable
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- go version
|
||||||
|
|
||||||
|
script:
|
||||||
|
- |
|
||||||
|
set -e
|
||||||
|
make lint
|
||||||
|
make cover
|
||||||
|
|
||||||
|
after_success:
|
||||||
|
- bash <(curl -s https://codecov.io/bash)
|
60
vendor/go.uber.org/multierr/CHANGELOG.md
generated
vendored
Normal file
60
vendor/go.uber.org/multierr/CHANGELOG.md
generated
vendored
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
Releases
|
||||||
|
========
|
||||||
|
|
||||||
|
v1.6.0 (2020-09-14)
|
||||||
|
===================
|
||||||
|
|
||||||
|
- Actually drop library dependency on development-time tooling.
|
||||||
|
|
||||||
|
|
||||||
|
v1.5.0 (2020-02-24)
|
||||||
|
===================
|
||||||
|
|
||||||
|
- Drop library dependency on development-time tooling.
|
||||||
|
|
||||||
|
|
||||||
|
v1.4.0 (2019-11-04)
|
||||||
|
===================
|
||||||
|
|
||||||
|
- Add `AppendInto` function to more ergonomically build errors inside a
|
||||||
|
loop.
|
||||||
|
|
||||||
|
|
||||||
|
v1.3.0 (2019-10-29)
|
||||||
|
===================
|
||||||
|
|
||||||
|
- Switch to Go modules.
|
||||||
|
|
||||||
|
|
||||||
|
v1.2.0 (2019-09-26)
|
||||||
|
===================
|
||||||
|
|
||||||
|
- Support extracting and matching against wrapped errors with `errors.As`
|
||||||
|
and `errors.Is`.
|
||||||
|
|
||||||
|
|
||||||
|
v1.1.0 (2017-06-30)
|
||||||
|
===================
|
||||||
|
|
||||||
|
- Added an `Errors(error) []error` function to extract the underlying list of
|
||||||
|
errors for a multierr error.
|
||||||
|
|
||||||
|
|
||||||
|
v1.0.0 (2017-05-31)
|
||||||
|
===================
|
||||||
|
|
||||||
|
No changes since v0.2.0. This release is committing to making no breaking
|
||||||
|
changes to the current API in the 1.X series.
|
||||||
|
|
||||||
|
|
||||||
|
v0.2.0 (2017-04-11)
|
||||||
|
===================
|
||||||
|
|
||||||
|
- Repeatedly appending to the same error is now faster due to fewer
|
||||||
|
allocations.
|
||||||
|
|
||||||
|
|
||||||
|
v0.1.0 (2017-31-03)
|
||||||
|
===================
|
||||||
|
|
||||||
|
- Initial release
|
19
vendor/go.uber.org/multierr/LICENSE.txt
generated
vendored
Normal file
19
vendor/go.uber.org/multierr/LICENSE.txt
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
Copyright (c) 2017 Uber Technologies, Inc.
|
||||||
|
|
||||||
|
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.
|
42
vendor/go.uber.org/multierr/Makefile
generated
vendored
Normal file
42
vendor/go.uber.org/multierr/Makefile
generated
vendored
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
# Directory to put `go install`ed binaries in.
|
||||||
|
export GOBIN ?= $(shell pwd)/bin
|
||||||
|
|
||||||
|
GO_FILES := $(shell \
|
||||||
|
find . '(' -path '*/.*' -o -path './vendor' ')' -prune \
|
||||||
|
-o -name '*.go' -print | cut -b3-)
|
||||||
|
|
||||||
|
.PHONY: build
|
||||||
|
build:
|
||||||
|
go build ./...
|
||||||
|
|
||||||
|
.PHONY: test
|
||||||
|
test:
|
||||||
|
go test -race ./...
|
||||||
|
|
||||||
|
.PHONY: gofmt
|
||||||
|
gofmt:
|
||||||
|
$(eval FMT_LOG := $(shell mktemp -t gofmt.XXXXX))
|
||||||
|
@gofmt -e -s -l $(GO_FILES) > $(FMT_LOG) || true
|
||||||
|
@[ ! -s "$(FMT_LOG)" ] || (echo "gofmt failed:" | cat - $(FMT_LOG) && false)
|
||||||
|
|
||||||
|
.PHONY: golint
|
||||||
|
golint:
|
||||||
|
@cd tools && go install golang.org/x/lint/golint
|
||||||
|
@$(GOBIN)/golint ./...
|
||||||
|
|
||||||
|
.PHONY: staticcheck
|
||||||
|
staticcheck:
|
||||||
|
@cd tools && go install honnef.co/go/tools/cmd/staticcheck
|
||||||
|
@$(GOBIN)/staticcheck ./...
|
||||||
|
|
||||||
|
.PHONY: lint
|
||||||
|
lint: gofmt golint staticcheck
|
||||||
|
|
||||||
|
.PHONY: cover
|
||||||
|
cover:
|
||||||
|
go test -coverprofile=cover.out -coverpkg=./... -v ./...
|
||||||
|
go tool cover -html=cover.out -o cover.html
|
||||||
|
|
||||||
|
update-license:
|
||||||
|
@cd tools && go install go.uber.org/tools/update-license
|
||||||
|
@$(GOBIN)/update-license $(GO_FILES)
|
23
vendor/go.uber.org/multierr/README.md
generated
vendored
Normal file
23
vendor/go.uber.org/multierr/README.md
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# multierr [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov]
|
||||||
|
|
||||||
|
`multierr` allows combining one or more Go `error`s together.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
go get -u go.uber.org/multierr
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
Stable: No breaking changes will be made before 2.0.
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Released under the [MIT License].
|
||||||
|
|
||||||
|
[MIT License]: LICENSE.txt
|
||||||
|
[doc-img]: https://godoc.org/go.uber.org/multierr?status.svg
|
||||||
|
[doc]: https://godoc.org/go.uber.org/multierr
|
||||||
|
[ci-img]: https://travis-ci.com/uber-go/multierr.svg?branch=master
|
||||||
|
[cov-img]: https://codecov.io/gh/uber-go/multierr/branch/master/graph/badge.svg
|
||||||
|
[ci]: https://travis-ci.com/uber-go/multierr
|
||||||
|
[cov]: https://codecov.io/gh/uber-go/multierr
|
449
vendor/go.uber.org/multierr/error.go
generated
vendored
Normal file
449
vendor/go.uber.org/multierr/error.go
generated
vendored
Normal file
@ -0,0 +1,449 @@
|
|||||||
|
// Copyright (c) 2019 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// Package multierr allows combining one or more errors together.
|
||||||
|
//
|
||||||
|
// Overview
|
||||||
|
//
|
||||||
|
// Errors can be combined with the use of the Combine function.
|
||||||
|
//
|
||||||
|
// multierr.Combine(
|
||||||
|
// reader.Close(),
|
||||||
|
// writer.Close(),
|
||||||
|
// conn.Close(),
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// If only two errors are being combined, the Append function may be used
|
||||||
|
// instead.
|
||||||
|
//
|
||||||
|
// err = multierr.Append(reader.Close(), writer.Close())
|
||||||
|
//
|
||||||
|
// This makes it possible to record resource cleanup failures from deferred
|
||||||
|
// blocks with the help of named return values.
|
||||||
|
//
|
||||||
|
// func sendRequest(req Request) (err error) {
|
||||||
|
// conn, err := openConnection()
|
||||||
|
// if err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
// defer func() {
|
||||||
|
// err = multierr.Append(err, conn.Close())
|
||||||
|
// }()
|
||||||
|
// // ...
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// The underlying list of errors for a returned error object may be retrieved
|
||||||
|
// with the Errors function.
|
||||||
|
//
|
||||||
|
// errors := multierr.Errors(err)
|
||||||
|
// if len(errors) > 0 {
|
||||||
|
// fmt.Println("The following errors occurred:", errors)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Advanced Usage
|
||||||
|
//
|
||||||
|
// Errors returned by Combine and Append MAY implement the following
|
||||||
|
// interface.
|
||||||
|
//
|
||||||
|
// type errorGroup interface {
|
||||||
|
// // Returns a slice containing the underlying list of errors.
|
||||||
|
// //
|
||||||
|
// // This slice MUST NOT be modified by the caller.
|
||||||
|
// Errors() []error
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Note that if you need access to list of errors behind a multierr error, you
|
||||||
|
// should prefer using the Errors function. That said, if you need cheap
|
||||||
|
// read-only access to the underlying errors slice, you can attempt to cast
|
||||||
|
// the error to this interface. You MUST handle the failure case gracefully
|
||||||
|
// because errors returned by Combine and Append are not guaranteed to
|
||||||
|
// implement this interface.
|
||||||
|
//
|
||||||
|
// var errors []error
|
||||||
|
// group, ok := err.(errorGroup)
|
||||||
|
// if ok {
|
||||||
|
// errors = group.Errors()
|
||||||
|
// } else {
|
||||||
|
// errors = []error{err}
|
||||||
|
// }
|
||||||
|
package multierr // import "go.uber.org/multierr"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"go.uber.org/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Separator for single-line error messages.
|
||||||
|
_singlelineSeparator = []byte("; ")
|
||||||
|
|
||||||
|
// Prefix for multi-line messages
|
||||||
|
_multilinePrefix = []byte("the following errors occurred:")
|
||||||
|
|
||||||
|
// Prefix for the first and following lines of an item in a list of
|
||||||
|
// multi-line error messages.
|
||||||
|
//
|
||||||
|
// For example, if a single item is:
|
||||||
|
//
|
||||||
|
// foo
|
||||||
|
// bar
|
||||||
|
//
|
||||||
|
// It will become,
|
||||||
|
//
|
||||||
|
// - foo
|
||||||
|
// bar
|
||||||
|
_multilineSeparator = []byte("\n - ")
|
||||||
|
_multilineIndent = []byte(" ")
|
||||||
|
)
|
||||||
|
|
||||||
|
// _bufferPool is a pool of bytes.Buffers.
|
||||||
|
var _bufferPool = sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
return &bytes.Buffer{}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
type errorGroup interface {
|
||||||
|
Errors() []error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Errors returns a slice containing zero or more errors that the supplied
|
||||||
|
// error is composed of. If the error is nil, a nil slice is returned.
|
||||||
|
//
|
||||||
|
// err := multierr.Append(r.Close(), w.Close())
|
||||||
|
// errors := multierr.Errors(err)
|
||||||
|
//
|
||||||
|
// If the error is not composed of other errors, the returned slice contains
|
||||||
|
// just the error that was passed in.
|
||||||
|
//
|
||||||
|
// Callers of this function are free to modify the returned slice.
|
||||||
|
func Errors(err error) []error {
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note that we're casting to multiError, not errorGroup. Our contract is
|
||||||
|
// that returned errors MAY implement errorGroup. Errors, however, only
|
||||||
|
// has special behavior for multierr-specific error objects.
|
||||||
|
//
|
||||||
|
// This behavior can be expanded in the future but I think it's prudent to
|
||||||
|
// start with as little as possible in terms of contract and possibility
|
||||||
|
// of misuse.
|
||||||
|
eg, ok := err.(*multiError)
|
||||||
|
if !ok {
|
||||||
|
return []error{err}
|
||||||
|
}
|
||||||
|
|
||||||
|
errors := eg.Errors()
|
||||||
|
result := make([]error, len(errors))
|
||||||
|
copy(result, errors)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// multiError is an error that holds one or more errors.
|
||||||
|
//
|
||||||
|
// An instance of this is guaranteed to be non-empty and flattened. That is,
|
||||||
|
// none of the errors inside multiError are other multiErrors.
|
||||||
|
//
|
||||||
|
// multiError formats to a semi-colon delimited list of error messages with
|
||||||
|
// %v and with a more readable multi-line format with %+v.
|
||||||
|
type multiError struct {
|
||||||
|
copyNeeded atomic.Bool
|
||||||
|
errors []error
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ errorGroup = (*multiError)(nil)
|
||||||
|
|
||||||
|
// Errors returns the list of underlying errors.
|
||||||
|
//
|
||||||
|
// This slice MUST NOT be modified.
|
||||||
|
func (merr *multiError) Errors() []error {
|
||||||
|
if merr == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return merr.errors
|
||||||
|
}
|
||||||
|
|
||||||
|
func (merr *multiError) Error() string {
|
||||||
|
if merr == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
buff := _bufferPool.Get().(*bytes.Buffer)
|
||||||
|
buff.Reset()
|
||||||
|
|
||||||
|
merr.writeSingleline(buff)
|
||||||
|
|
||||||
|
result := buff.String()
|
||||||
|
_bufferPool.Put(buff)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (merr *multiError) Format(f fmt.State, c rune) {
|
||||||
|
if c == 'v' && f.Flag('+') {
|
||||||
|
merr.writeMultiline(f)
|
||||||
|
} else {
|
||||||
|
merr.writeSingleline(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (merr *multiError) writeSingleline(w io.Writer) {
|
||||||
|
first := true
|
||||||
|
for _, item := range merr.errors {
|
||||||
|
if first {
|
||||||
|
first = false
|
||||||
|
} else {
|
||||||
|
w.Write(_singlelineSeparator)
|
||||||
|
}
|
||||||
|
io.WriteString(w, item.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (merr *multiError) writeMultiline(w io.Writer) {
|
||||||
|
w.Write(_multilinePrefix)
|
||||||
|
for _, item := range merr.errors {
|
||||||
|
w.Write(_multilineSeparator)
|
||||||
|
writePrefixLine(w, _multilineIndent, fmt.Sprintf("%+v", item))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Writes s to the writer with the given prefix added before each line after
|
||||||
|
// the first.
|
||||||
|
func writePrefixLine(w io.Writer, prefix []byte, s string) {
|
||||||
|
first := true
|
||||||
|
for len(s) > 0 {
|
||||||
|
if first {
|
||||||
|
first = false
|
||||||
|
} else {
|
||||||
|
w.Write(prefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
idx := strings.IndexByte(s, '\n')
|
||||||
|
if idx < 0 {
|
||||||
|
idx = len(s) - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
io.WriteString(w, s[:idx+1])
|
||||||
|
s = s[idx+1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type inspectResult struct {
|
||||||
|
// Number of top-level non-nil errors
|
||||||
|
Count int
|
||||||
|
|
||||||
|
// Total number of errors including multiErrors
|
||||||
|
Capacity int
|
||||||
|
|
||||||
|
// Index of the first non-nil error in the list. Value is meaningless if
|
||||||
|
// Count is zero.
|
||||||
|
FirstErrorIdx int
|
||||||
|
|
||||||
|
// Whether the list contains at least one multiError
|
||||||
|
ContainsMultiError bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inspects the given slice of errors so that we can efficiently allocate
|
||||||
|
// space for it.
|
||||||
|
func inspect(errors []error) (res inspectResult) {
|
||||||
|
first := true
|
||||||
|
for i, err := range errors {
|
||||||
|
if err == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
res.Count++
|
||||||
|
if first {
|
||||||
|
first = false
|
||||||
|
res.FirstErrorIdx = i
|
||||||
|
}
|
||||||
|
|
||||||
|
if merr, ok := err.(*multiError); ok {
|
||||||
|
res.Capacity += len(merr.errors)
|
||||||
|
res.ContainsMultiError = true
|
||||||
|
} else {
|
||||||
|
res.Capacity++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// fromSlice converts the given list of errors into a single error.
|
||||||
|
func fromSlice(errors []error) error {
|
||||||
|
res := inspect(errors)
|
||||||
|
switch res.Count {
|
||||||
|
case 0:
|
||||||
|
return nil
|
||||||
|
case 1:
|
||||||
|
// only one non-nil entry
|
||||||
|
return errors[res.FirstErrorIdx]
|
||||||
|
case len(errors):
|
||||||
|
if !res.ContainsMultiError {
|
||||||
|
// already flat
|
||||||
|
return &multiError{errors: errors}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nonNilErrs := make([]error, 0, res.Capacity)
|
||||||
|
for _, err := range errors[res.FirstErrorIdx:] {
|
||||||
|
if err == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if nested, ok := err.(*multiError); ok {
|
||||||
|
nonNilErrs = append(nonNilErrs, nested.errors...)
|
||||||
|
} else {
|
||||||
|
nonNilErrs = append(nonNilErrs, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &multiError{errors: nonNilErrs}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Combine combines the passed errors into a single error.
|
||||||
|
//
|
||||||
|
// If zero arguments were passed or if all items are nil, a nil error is
|
||||||
|
// returned.
|
||||||
|
//
|
||||||
|
// Combine(nil, nil) // == nil
|
||||||
|
//
|
||||||
|
// If only a single error was passed, it is returned as-is.
|
||||||
|
//
|
||||||
|
// Combine(err) // == err
|
||||||
|
//
|
||||||
|
// Combine skips over nil arguments so this function may be used to combine
|
||||||
|
// together errors from operations that fail independently of each other.
|
||||||
|
//
|
||||||
|
// multierr.Combine(
|
||||||
|
// reader.Close(),
|
||||||
|
// writer.Close(),
|
||||||
|
// pipe.Close(),
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// If any of the passed errors is a multierr error, it will be flattened along
|
||||||
|
// with the other errors.
|
||||||
|
//
|
||||||
|
// multierr.Combine(multierr.Combine(err1, err2), err3)
|
||||||
|
// // is the same as
|
||||||
|
// multierr.Combine(err1, err2, err3)
|
||||||
|
//
|
||||||
|
// The returned error formats into a readable multi-line error message if
|
||||||
|
// formatted with %+v.
|
||||||
|
//
|
||||||
|
// fmt.Sprintf("%+v", multierr.Combine(err1, err2))
|
||||||
|
func Combine(errors ...error) error {
|
||||||
|
return fromSlice(errors)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append appends the given errors together. Either value may be nil.
|
||||||
|
//
|
||||||
|
// This function is a specialization of Combine for the common case where
|
||||||
|
// there are only two errors.
|
||||||
|
//
|
||||||
|
// err = multierr.Append(reader.Close(), writer.Close())
|
||||||
|
//
|
||||||
|
// The following pattern may also be used to record failure of deferred
|
||||||
|
// operations without losing information about the original error.
|
||||||
|
//
|
||||||
|
// func doSomething(..) (err error) {
|
||||||
|
// f := acquireResource()
|
||||||
|
// defer func() {
|
||||||
|
// err = multierr.Append(err, f.Close())
|
||||||
|
// }()
|
||||||
|
func Append(left error, right error) error {
|
||||||
|
switch {
|
||||||
|
case left == nil:
|
||||||
|
return right
|
||||||
|
case right == nil:
|
||||||
|
return left
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := right.(*multiError); !ok {
|
||||||
|
if l, ok := left.(*multiError); ok && !l.copyNeeded.Swap(true) {
|
||||||
|
// Common case where the error on the left is constantly being
|
||||||
|
// appended to.
|
||||||
|
errs := append(l.errors, right)
|
||||||
|
return &multiError{errors: errs}
|
||||||
|
} else if !ok {
|
||||||
|
// Both errors are single errors.
|
||||||
|
return &multiError{errors: []error{left, right}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Either right or both, left and right, are multiErrors. Rely on usual
|
||||||
|
// expensive logic.
|
||||||
|
errors := [2]error{left, right}
|
||||||
|
return fromSlice(errors[0:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppendInto appends an error into the destination of an error pointer and
|
||||||
|
// returns whether the error being appended was non-nil.
|
||||||
|
//
|
||||||
|
// var err error
|
||||||
|
// multierr.AppendInto(&err, r.Close())
|
||||||
|
// multierr.AppendInto(&err, w.Close())
|
||||||
|
//
|
||||||
|
// The above is equivalent to,
|
||||||
|
//
|
||||||
|
// err := multierr.Append(r.Close(), w.Close())
|
||||||
|
//
|
||||||
|
// As AppendInto reports whether the provided error was non-nil, it may be
|
||||||
|
// used to build a multierr error in a loop more ergonomically. For example:
|
||||||
|
//
|
||||||
|
// var err error
|
||||||
|
// for line := range lines {
|
||||||
|
// var item Item
|
||||||
|
// if multierr.AppendInto(&err, parse(line, &item)) {
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// items = append(items, item)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Compare this with a verison that relies solely on Append:
|
||||||
|
//
|
||||||
|
// var err error
|
||||||
|
// for line := range lines {
|
||||||
|
// var item Item
|
||||||
|
// if parseErr := parse(line, &item); parseErr != nil {
|
||||||
|
// err = multierr.Append(err, parseErr)
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// items = append(items, item)
|
||||||
|
// }
|
||||||
|
func AppendInto(into *error, err error) (errored bool) {
|
||||||
|
if into == nil {
|
||||||
|
// We panic if 'into' is nil. This is not documented above
|
||||||
|
// because suggesting that the pointer must be non-nil may
|
||||||
|
// confuse users into thinking that the error that it points
|
||||||
|
// to must be non-nil.
|
||||||
|
panic("misuse of multierr.AppendInto: into pointer must not be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
*into = Append(*into, err)
|
||||||
|
return true
|
||||||
|
}
|
8
vendor/go.uber.org/multierr/glide.yaml
generated
vendored
Normal file
8
vendor/go.uber.org/multierr/glide.yaml
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package: go.uber.org/multierr
|
||||||
|
import:
|
||||||
|
- package: go.uber.org/atomic
|
||||||
|
version: ^1
|
||||||
|
testImport:
|
||||||
|
- package: github.com/stretchr/testify
|
||||||
|
subpackages:
|
||||||
|
- assert
|
52
vendor/go.uber.org/multierr/go113.go
generated
vendored
Normal file
52
vendor/go.uber.org/multierr/go113.go
generated
vendored
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
// Copyright (c) 2019 Uber Technologies, Inc.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// +build go1.13
|
||||||
|
|
||||||
|
package multierr
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
// As attempts to find the first error in the error list that matches the type
|
||||||
|
// of the value that target points to.
|
||||||
|
//
|
||||||
|
// This function allows errors.As to traverse the values stored on the
|
||||||
|
// multierr error.
|
||||||
|
func (merr *multiError) As(target interface{}) bool {
|
||||||
|
for _, err := range merr.Errors() {
|
||||||
|
if errors.As(err, target) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is attempts to match the provided error against errors in the error list.
|
||||||
|
//
|
||||||
|
// This function allows errors.Is to traverse the values stored on the
|
||||||
|
// multierr error.
|
||||||
|
func (merr *multiError) Is(target error) bool {
|
||||||
|
for _, err := range merr.Errors() {
|
||||||
|
if errors.Is(err, target) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
17
vendor/go.uber.org/zap/.codecov.yml
generated
vendored
Normal file
17
vendor/go.uber.org/zap/.codecov.yml
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
coverage:
|
||||||
|
range: 80..100
|
||||||
|
round: down
|
||||||
|
precision: 2
|
||||||
|
|
||||||
|
status:
|
||||||
|
project: # measuring the overall project coverage
|
||||||
|
default: # context, you can create multiple ones with custom titles
|
||||||
|
enabled: yes # must be yes|true to enable this status
|
||||||
|
target: 95% # specify the target coverage for each commit status
|
||||||
|
# option: "auto" (must increase from parent commit or pull request base)
|
||||||
|
# option: "X%" a static target percentage to hit
|
||||||
|
if_not_found: success # if parent is not found report status as success, error, or failure
|
||||||
|
if_ci_failed: error # if ci fails report status as success, error, or failure
|
||||||
|
ignore:
|
||||||
|
- internal/readme/readme.go
|
||||||
|
|
32
vendor/go.uber.org/zap/.gitignore
generated
vendored
Normal file
32
vendor/go.uber.org/zap/.gitignore
generated
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Folders
|
||||||
|
_obj
|
||||||
|
_test
|
||||||
|
vendor
|
||||||
|
|
||||||
|
# Architecture specific extensions/prefixes
|
||||||
|
*.[568vq]
|
||||||
|
[568vq].out
|
||||||
|
|
||||||
|
*.cgo1.go
|
||||||
|
*.cgo2.c
|
||||||
|
_cgo_defun.c
|
||||||
|
_cgo_gotypes.go
|
||||||
|
_cgo_export.*
|
||||||
|
|
||||||
|
_testmain.go
|
||||||
|
|
||||||
|
*.exe
|
||||||
|
*.test
|
||||||
|
*.prof
|
||||||
|
*.pprof
|
||||||
|
*.out
|
||||||
|
*.log
|
||||||
|
|
||||||
|
/bin
|
||||||
|
cover.out
|
||||||
|
cover.html
|
109
vendor/go.uber.org/zap/.readme.tmpl
generated
vendored
Normal file
109
vendor/go.uber.org/zap/.readme.tmpl
generated
vendored
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
# :zap: zap [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov]
|
||||||
|
|
||||||
|
Blazing fast, structured, leveled logging in Go.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
`go get -u go.uber.org/zap`
|
||||||
|
|
||||||
|
Note that zap only supports the two most recent minor versions of Go.
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
In contexts where performance is nice, but not critical, use the
|
||||||
|
`SugaredLogger`. It's 4-10x faster than other structured logging
|
||||||
|
packages and includes both structured and `printf`-style APIs.
|
||||||
|
|
||||||
|
```go
|
||||||
|
logger, _ := zap.NewProduction()
|
||||||
|
defer logger.Sync() // flushes buffer, if any
|
||||||
|
sugar := logger.Sugar()
|
||||||
|
sugar.Infow("failed to fetch URL",
|
||||||
|
// Structured context as loosely typed key-value pairs.
|
||||||
|
"url", url,
|
||||||
|
"attempt", 3,
|
||||||
|
"backoff", time.Second,
|
||||||
|
)
|
||||||
|
sugar.Infof("Failed to fetch URL: %s", url)
|
||||||
|
```
|
||||||
|
|
||||||
|
When performance and type safety are critical, use the `Logger`. It's even
|
||||||
|
faster than the `SugaredLogger` and allocates far less, but it only supports
|
||||||
|
structured logging.
|
||||||
|
|
||||||
|
```go
|
||||||
|
logger, _ := zap.NewProduction()
|
||||||
|
defer logger.Sync()
|
||||||
|
logger.Info("failed to fetch URL",
|
||||||
|
// Structured context as strongly typed Field values.
|
||||||
|
zap.String("url", url),
|
||||||
|
zap.Int("attempt", 3),
|
||||||
|
zap.Duration("backoff", time.Second),
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
See the [documentation][doc] and [FAQ](FAQ.md) for more details.
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
|
||||||
|
For applications that log in the hot path, reflection-based serialization and
|
||||||
|
string formatting are prohibitively expensive — they're CPU-intensive
|
||||||
|
and make many small allocations. Put differently, using `encoding/json` and
|
||||||
|
`fmt.Fprintf` to log tons of `interface{}`s makes your application slow.
|
||||||
|
|
||||||
|
Zap takes a different approach. It includes a reflection-free, zero-allocation
|
||||||
|
JSON encoder, and the base `Logger` strives to avoid serialization overhead
|
||||||
|
and allocations wherever possible. By building the high-level `SugaredLogger`
|
||||||
|
on that foundation, zap lets users *choose* when they need to count every
|
||||||
|
allocation and when they'd prefer a more familiar, loosely typed API.
|
||||||
|
|
||||||
|
As measured by its own [benchmarking suite][], not only is zap more performant
|
||||||
|
than comparable structured logging packages — it's also faster than the
|
||||||
|
standard library. Like all benchmarks, take these with a grain of salt.<sup
|
||||||
|
id="anchor-versions">[1](#footnote-versions)</sup>
|
||||||
|
|
||||||
|
Log a message and 10 fields:
|
||||||
|
|
||||||
|
{{.BenchmarkAddingFields}}
|
||||||
|
|
||||||
|
Log a message with a logger that already has 10 fields of context:
|
||||||
|
|
||||||
|
{{.BenchmarkAccumulatedContext}}
|
||||||
|
|
||||||
|
Log a static string, without any context or `printf`-style templating:
|
||||||
|
|
||||||
|
{{.BenchmarkWithoutFields}}
|
||||||
|
|
||||||
|
## Development Status: Stable
|
||||||
|
|
||||||
|
All APIs are finalized, and no breaking changes will be made in the 1.x series
|
||||||
|
of releases. Users of semver-aware dependency management systems should pin
|
||||||
|
zap to `^1`.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
We encourage and support an active, healthy community of contributors —
|
||||||
|
including you! Details are in the [contribution guide](CONTRIBUTING.md) and
|
||||||
|
the [code of conduct](CODE_OF_CONDUCT.md). The zap maintainers keep an eye on
|
||||||
|
issues and pull requests, but you can also report any negative conduct to
|
||||||
|
oss-conduct@uber.com. That email list is a private, safe space; even the zap
|
||||||
|
maintainers don't have access, so don't hesitate to hold us to a high
|
||||||
|
standard.
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
Released under the [MIT License](LICENSE.txt).
|
||||||
|
|
||||||
|
<sup id="footnote-versions">1</sup> In particular, keep in mind that we may be
|
||||||
|
benchmarking against slightly older versions of other packages. Versions are
|
||||||
|
pinned in zap's [glide.lock][] file. [↩](#anchor-versions)
|
||||||
|
|
||||||
|
[doc-img]: https://godoc.org/go.uber.org/zap?status.svg
|
||||||
|
[doc]: https://godoc.org/go.uber.org/zap
|
||||||
|
[ci-img]: https://travis-ci.com/uber-go/zap.svg?branch=master
|
||||||
|
[ci]: https://travis-ci.com/uber-go/zap
|
||||||
|
[cov-img]: https://codecov.io/gh/uber-go/zap/branch/master/graph/badge.svg
|
||||||
|
[cov]: https://codecov.io/gh/uber-go/zap
|
||||||
|
[benchmarking suite]: https://github.com/uber-go/zap/tree/master/benchmarks
|
||||||
|
[glide.lock]: https://github.com/uber-go/zap/blob/master/glide.lock
|
||||||
|
|
516
vendor/go.uber.org/zap/CHANGELOG.md
generated
vendored
Normal file
516
vendor/go.uber.org/zap/CHANGELOG.md
generated
vendored
Normal file
@ -0,0 +1,516 @@
|
|||||||
|
# Changelog
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## 1.19.1 (8 Sep 2021)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* [#1001][]: JSON: Fix complex number encoding with negative imaginary part. Thanks to @hemantjadon.
|
||||||
|
* [#1003][]: JSON: Fix inaccurate precision when encoding float32.
|
||||||
|
|
||||||
|
[#1001]: https://github.com/uber-go/zap/pull/1001
|
||||||
|
[#1003]: https://github.com/uber-go/zap/pull/1003
|
||||||
|
|
||||||
|
## 1.19.0 (9 Aug 2021)
|
||||||
|
|
||||||
|
Enhancements:
|
||||||
|
* [#975][]: Avoid panicking in Sampler core if the level is out of bounds.
|
||||||
|
* [#984][]: Reduce the size of BufferedWriteSyncer by aligning the fields
|
||||||
|
better.
|
||||||
|
|
||||||
|
[#975]: https://github.com/uber-go/zap/pull/975
|
||||||
|
[#984]: https://github.com/uber-go/zap/pull/984
|
||||||
|
|
||||||
|
Thanks to @lancoLiu and @thockin for their contributions to this release.
|
||||||
|
|
||||||
|
## 1.18.1 (28 Jun 2021)
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
* [#974][]: Fix nil dereference in logger constructed by `zap.NewNop`.
|
||||||
|
|
||||||
|
[#974]: https://github.com/uber-go/zap/pull/974
|
||||||
|
|
||||||
|
## 1.18.0 (28 Jun 2021)
|
||||||
|
|
||||||
|
Enhancements:
|
||||||
|
* [#961][]: Add `zapcore.BufferedWriteSyncer`, a new `WriteSyncer` that buffers
|
||||||
|
messages in-memory and flushes them periodically.
|
||||||
|
* [#971][]: Add `zapio.Writer` to use a Zap logger as an `io.Writer`.
|
||||||
|
* [#897][]: Add `zap.WithClock` option to control the source of time via the
|
||||||
|
new `zapcore.Clock` interface.
|
||||||
|
* [#949][]: Avoid panicking in `zap.SugaredLogger` when arguments of `*w`
|
||||||
|
methods don't match expectations.
|
||||||
|
* [#943][]: Add support for filtering by level or arbitrary matcher function to
|
||||||
|
`zaptest/observer`.
|
||||||
|
* [#691][]: Comply with `io.StringWriter` and `io.ByteWriter` in Zap's
|
||||||
|
`buffer.Buffer`.
|
||||||
|
|
||||||
|
Thanks to @atrn0, @ernado, @heyanfu, @hnlq715, @zchee
|
||||||
|
for their contributions to this release.
|
||||||
|
|
||||||
|
[#691]: https://github.com/uber-go/zap/pull/691
|
||||||
|
[#897]: https://github.com/uber-go/zap/pull/897
|
||||||
|
[#943]: https://github.com/uber-go/zap/pull/943
|
||||||
|
[#949]: https://github.com/uber-go/zap/pull/949
|
||||||
|
[#961]: https://github.com/uber-go/zap/pull/961
|
||||||
|
[#971]: https://github.com/uber-go/zap/pull/971
|
||||||
|
|
||||||
|
## 1.17.0 (25 May 2021)
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
* [#867][]: Encode `<nil>` for nil `error` instead of a panic.
|
||||||
|
* [#931][], [#936][]: Update minimum version constraints to address
|
||||||
|
vulnerabilities in dependencies.
|
||||||
|
|
||||||
|
Enhancements:
|
||||||
|
* [#865][]: Improve alignment of fields of the Logger struct, reducing its
|
||||||
|
size from 96 to 80 bytes.
|
||||||
|
* [#881][]: Support `grpclog.LoggerV2` in zapgrpc.
|
||||||
|
* [#903][]: Support URL-encoded POST requests to the AtomicLevel HTTP handler
|
||||||
|
with the `application/x-www-form-urlencoded` content type.
|
||||||
|
* [#912][]: Support multi-field encoding with `zap.Inline`.
|
||||||
|
* [#913][]: Speed up SugaredLogger for calls with a single string.
|
||||||
|
* [#928][]: Add support for filtering by field name to `zaptest/observer`.
|
||||||
|
|
||||||
|
Thanks to @ash2k, @FMLS, @jimmystewpot, @Oncilla, @tsoslow, @tylitianrui, @withshubh, and @wziww for their contributions to this release.
|
||||||
|
|
||||||
|
## 1.16.0 (1 Sep 2020)
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
* [#828][]: Fix missing newline in IncreaseLevel error messages.
|
||||||
|
* [#835][]: Fix panic in JSON encoder when encoding times or durations
|
||||||
|
without specifying a time or duration encoder.
|
||||||
|
* [#843][]: Honor CallerSkip when taking stack traces.
|
||||||
|
* [#862][]: Fix the default file permissions to use `0666` and rely on the umask instead.
|
||||||
|
* [#854][]: Encode `<nil>` for nil `Stringer` instead of a panic error log.
|
||||||
|
|
||||||
|
Enhancements:
|
||||||
|
* [#629][]: Added `zapcore.TimeEncoderOfLayout` to easily create time encoders
|
||||||
|
for custom layouts.
|
||||||
|
* [#697][]: Added support for a configurable delimiter in the console encoder.
|
||||||
|
* [#852][]: Optimize console encoder by pooling the underlying JSON encoder.
|
||||||
|
* [#844][]: Add ability to include the calling function as part of logs.
|
||||||
|
* [#843][]: Add `StackSkip` for including truncated stacks as a field.
|
||||||
|
* [#861][]: Add options to customize Fatal behaviour for better testability.
|
||||||
|
|
||||||
|
Thanks to @SteelPhase, @tmshn, @lixingwang, @wyxloading, @moul, @segevfiner, @andy-retailnext and @jcorbin for their contributions to this release.
|
||||||
|
|
||||||
|
## 1.15.0 (23 Apr 2020)
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
* [#804][]: Fix handling of `Time` values out of `UnixNano` range.
|
||||||
|
* [#812][]: Fix `IncreaseLevel` being reset after a call to `With`.
|
||||||
|
|
||||||
|
Enhancements:
|
||||||
|
* [#806][]: Add `WithCaller` option to supersede the `AddCaller` option. This
|
||||||
|
allows disabling annotation of log entries with caller information if
|
||||||
|
previously enabled with `AddCaller`.
|
||||||
|
* [#813][]: Deprecate `NewSampler` constructor in favor of
|
||||||
|
`NewSamplerWithOptions` which supports a `SamplerHook` option. This option
|
||||||
|
adds support for monitoring sampling decisions through a hook.
|
||||||
|
|
||||||
|
Thanks to @danielbprice for their contributions to this release.
|
||||||
|
|
||||||
|
## 1.14.1 (14 Mar 2020)
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
* [#791][]: Fix panic on attempting to build a logger with an invalid Config.
|
||||||
|
* [#795][]: Vendoring Zap with `go mod vendor` no longer includes Zap's
|
||||||
|
development-time dependencies.
|
||||||
|
* [#799][]: Fix issue introduced in 1.14.0 that caused invalid JSON output to
|
||||||
|
be generated for arrays of `time.Time` objects when using string-based time
|
||||||
|
formats.
|
||||||
|
|
||||||
|
Thanks to @YashishDua for their contributions to this release.
|
||||||
|
|
||||||
|
## 1.14.0 (20 Feb 2020)
|
||||||
|
|
||||||
|
Enhancements:
|
||||||
|
* [#771][]: Optimize calls for disabled log levels.
|
||||||
|
* [#773][]: Add millisecond duration encoder.
|
||||||
|
* [#775][]: Add option to increase the level of a logger.
|
||||||
|
* [#786][]: Optimize time formatters using `Time.AppendFormat` where possible.
|
||||||
|
|
||||||
|
Thanks to @caibirdme for their contributions to this release.
|
||||||
|
|
||||||
|
## 1.13.0 (13 Nov 2019)
|
||||||
|
|
||||||
|
Enhancements:
|
||||||
|
* [#758][]: Add `Intp`, `Stringp`, and other similar `*p` field constructors
|
||||||
|
to log pointers to primitives with support for `nil` values.
|
||||||
|
|
||||||
|
Thanks to @jbizzle for their contributions to this release.
|
||||||
|
|
||||||
|
## 1.12.0 (29 Oct 2019)
|
||||||
|
|
||||||
|
Enhancements:
|
||||||
|
* [#751][]: Migrate to Go modules.
|
||||||
|
|
||||||
|
## 1.11.0 (21 Oct 2019)
|
||||||
|
|
||||||
|
Enhancements:
|
||||||
|
* [#725][]: Add `zapcore.OmitKey` to omit keys in an `EncoderConfig`.
|
||||||
|
* [#736][]: Add `RFC3339` and `RFC3339Nano` time encoders.
|
||||||
|
|
||||||
|
Thanks to @juicemia, @uhthomas for their contributions to this release.
|
||||||
|
|
||||||
|
## 1.10.0 (29 Apr 2019)
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
* [#657][]: Fix `MapObjectEncoder.AppendByteString` not adding value as a
|
||||||
|
string.
|
||||||
|
* [#706][]: Fix incorrect call depth to determine caller in Go 1.12.
|
||||||
|
|
||||||
|
Enhancements:
|
||||||
|
* [#610][]: Add `zaptest.WrapOptions` to wrap `zap.Option` for creating test
|
||||||
|
loggers.
|
||||||
|
* [#675][]: Don't panic when encoding a String field.
|
||||||
|
* [#704][]: Disable HTML escaping for JSON objects encoded using the
|
||||||
|
reflect-based encoder.
|
||||||
|
|
||||||
|
Thanks to @iaroslav-ciupin, @lelenanam, @joa, @NWilson for their contributions
|
||||||
|
to this release.
|
||||||
|
|
||||||
|
## v1.9.1 (06 Aug 2018)
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
|
||||||
|
* [#614][]: MapObjectEncoder should not ignore empty slices.
|
||||||
|
|
||||||
|
## v1.9.0 (19 Jul 2018)
|
||||||
|
|
||||||
|
Enhancements:
|
||||||
|
* [#602][]: Reduce number of allocations when logging with reflection.
|
||||||
|
* [#572][], [#606][]: Expose a registry for third-party logging sinks.
|
||||||
|
|
||||||
|
Thanks to @nfarah86, @AlekSi, @JeanMertz, @philippgille, @etsangsplk, and
|
||||||
|
@dimroc for their contributions to this release.
|
||||||
|
|
||||||
|
## v1.8.0 (13 Apr 2018)
|
||||||
|
|
||||||
|
Enhancements:
|
||||||
|
* [#508][]: Make log level configurable when redirecting the standard
|
||||||
|
library's logger.
|
||||||
|
* [#518][]: Add a logger that writes to a `*testing.TB`.
|
||||||
|
* [#577][]: Add a top-level alias for `zapcore.Field` to clean up GoDoc.
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
* [#574][]: Add a missing import comment to `go.uber.org/zap/buffer`.
|
||||||
|
|
||||||
|
Thanks to @DiSiqueira and @djui for their contributions to this release.
|
||||||
|
|
||||||
|
## v1.7.1 (25 Sep 2017)
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
* [#504][]: Store strings when using AddByteString with the map encoder.
|
||||||
|
|
||||||
|
## v1.7.0 (21 Sep 2017)
|
||||||
|
|
||||||
|
Enhancements:
|
||||||
|
|
||||||
|
* [#487][]: Add `NewStdLogAt`, which extends `NewStdLog` by allowing the user
|
||||||
|
to specify the level of the logged messages.
|
||||||
|
|
||||||
|
## v1.6.0 (30 Aug 2017)
|
||||||
|
|
||||||
|
Enhancements:
|
||||||
|
|
||||||
|
* [#491][]: Omit zap stack frames from stacktraces.
|
||||||
|
* [#490][]: Add a `ContextMap` method to observer logs for simpler
|
||||||
|
field validation in tests.
|
||||||
|
|
||||||
|
## v1.5.0 (22 Jul 2017)
|
||||||
|
|
||||||
|
Enhancements:
|
||||||
|
|
||||||
|
* [#460][] and [#470][]: Support errors produced by `go.uber.org/multierr`.
|
||||||
|
* [#465][]: Support user-supplied encoders for logger names.
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
|
||||||
|
* [#477][]: Fix a bug that incorrectly truncated deep stacktraces.
|
||||||
|
|
||||||
|
Thanks to @richard-tunein and @pavius for their contributions to this release.
|
||||||
|
|
||||||
|
## v1.4.1 (08 Jun 2017)
|
||||||
|
|
||||||
|
This release fixes two bugs.
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
|
||||||
|
* [#435][]: Support a variety of case conventions when unmarshaling levels.
|
||||||
|
* [#444][]: Fix a panic in the observer.
|
||||||
|
|
||||||
|
## v1.4.0 (12 May 2017)
|
||||||
|
|
||||||
|
This release adds a few small features and is fully backward-compatible.
|
||||||
|
|
||||||
|
Enhancements:
|
||||||
|
|
||||||
|
* [#424][]: Add a `LineEnding` field to `EncoderConfig`, allowing users to
|
||||||
|
override the Unix-style default.
|
||||||
|
* [#425][]: Preserve time zones when logging times.
|
||||||
|
* [#431][]: Make `zap.AtomicLevel` implement `fmt.Stringer`, which makes a
|
||||||
|
variety of operations a bit simpler.
|
||||||
|
|
||||||
|
## v1.3.0 (25 Apr 2017)
|
||||||
|
|
||||||
|
This release adds an enhancement to zap's testing helpers as well as the
|
||||||
|
ability to marshal an AtomicLevel. It is fully backward-compatible.
|
||||||
|
|
||||||
|
Enhancements:
|
||||||
|
|
||||||
|
* [#415][]: Add a substring-filtering helper to zap's observer. This is
|
||||||
|
particularly useful when testing the `SugaredLogger`.
|
||||||
|
* [#416][]: Make `AtomicLevel` implement `encoding.TextMarshaler`.
|
||||||
|
|
||||||
|
## v1.2.0 (13 Apr 2017)
|
||||||
|
|
||||||
|
This release adds a gRPC compatibility wrapper. It is fully backward-compatible.
|
||||||
|
|
||||||
|
Enhancements:
|
||||||
|
|
||||||
|
* [#402][]: Add a `zapgrpc` package that wraps zap's Logger and implements
|
||||||
|
`grpclog.Logger`.
|
||||||
|
|
||||||
|
## v1.1.0 (31 Mar 2017)
|
||||||
|
|
||||||
|
This release fixes two bugs and adds some enhancements to zap's testing helpers.
|
||||||
|
It is fully backward-compatible.
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
|
||||||
|
* [#385][]: Fix caller path trimming on Windows.
|
||||||
|
* [#396][]: Fix a panic when attempting to use non-existent directories with
|
||||||
|
zap's configuration struct.
|
||||||
|
|
||||||
|
Enhancements:
|
||||||
|
|
||||||
|
* [#386][]: Add filtering helpers to zaptest's observing logger.
|
||||||
|
|
||||||
|
Thanks to @moitias for contributing to this release.
|
||||||
|
|
||||||
|
## v1.0.0 (14 Mar 2017)
|
||||||
|
|
||||||
|
This is zap's first stable release. All exported APIs are now final, and no
|
||||||
|
further breaking changes will be made in the 1.x release series. Anyone using a
|
||||||
|
semver-aware dependency manager should now pin to `^1`.
|
||||||
|
|
||||||
|
Breaking changes:
|
||||||
|
|
||||||
|
* [#366][]: Add byte-oriented APIs to encoders to log UTF-8 encoded text without
|
||||||
|
casting from `[]byte` to `string`.
|
||||||
|
* [#364][]: To support buffering outputs, add `Sync` methods to `zapcore.Core`,
|
||||||
|
`zap.Logger`, and `zap.SugaredLogger`.
|
||||||
|
* [#371][]: Rename the `testutils` package to `zaptest`, which is less likely to
|
||||||
|
clash with other testing helpers.
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
|
||||||
|
* [#362][]: Make the ISO8601 time formatters fixed-width, which is friendlier
|
||||||
|
for tab-separated console output.
|
||||||
|
* [#369][]: Remove the automatic locks in `zapcore.NewCore`, which allows zap to
|
||||||
|
work with concurrency-safe `WriteSyncer` implementations.
|
||||||
|
* [#347][]: Stop reporting errors when trying to `fsync` standard out on Linux
|
||||||
|
systems.
|
||||||
|
* [#373][]: Report the correct caller from zap's standard library
|
||||||
|
interoperability wrappers.
|
||||||
|
|
||||||
|
Enhancements:
|
||||||
|
|
||||||
|
* [#348][]: Add a registry allowing third-party encodings to work with zap's
|
||||||
|
built-in `Config`.
|
||||||
|
* [#327][]: Make the representation of logger callers configurable (like times,
|
||||||
|
levels, and durations).
|
||||||
|
* [#376][]: Allow third-party encoders to use their own buffer pools, which
|
||||||
|
removes the last performance advantage that zap's encoders have over plugins.
|
||||||
|
* [#346][]: Add `CombineWriteSyncers`, a convenience function to tee multiple
|
||||||
|
`WriteSyncer`s and lock the result.
|
||||||
|
* [#365][]: Make zap's stacktraces compatible with mid-stack inlining (coming in
|
||||||
|
Go 1.9).
|
||||||
|
* [#372][]: Export zap's observing logger as `zaptest/observer`. This makes it
|
||||||
|
easier for particularly punctilious users to unit test their application's
|
||||||
|
logging.
|
||||||
|
|
||||||
|
Thanks to @suyash, @htrendev, @flisky, @Ulexus, and @skipor for their
|
||||||
|
contributions to this release.
|
||||||
|
|
||||||
|
## v1.0.0-rc.3 (7 Mar 2017)
|
||||||
|
|
||||||
|
This is the third release candidate for zap's stable release. There are no
|
||||||
|
breaking changes.
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
|
||||||
|
* [#339][]: Byte slices passed to `zap.Any` are now correctly treated as binary blobs
|
||||||
|
rather than `[]uint8`.
|
||||||
|
|
||||||
|
Enhancements:
|
||||||
|
|
||||||
|
* [#307][]: Users can opt into colored output for log levels.
|
||||||
|
* [#353][]: In addition to hijacking the output of the standard library's
|
||||||
|
package-global logging functions, users can now construct a zap-backed
|
||||||
|
`log.Logger` instance.
|
||||||
|
* [#311][]: Frames from common runtime functions and some of zap's internal
|
||||||
|
machinery are now omitted from stacktraces.
|
||||||
|
|
||||||
|
Thanks to @ansel1 and @suyash for their contributions to this release.
|
||||||
|
|
||||||
|
## v1.0.0-rc.2 (21 Feb 2017)
|
||||||
|
|
||||||
|
This is the second release candidate for zap's stable release. It includes two
|
||||||
|
breaking changes.
|
||||||
|
|
||||||
|
Breaking changes:
|
||||||
|
|
||||||
|
* [#316][]: Zap's global loggers are now fully concurrency-safe
|
||||||
|
(previously, users had to ensure that `ReplaceGlobals` was called before the
|
||||||
|
loggers were in use). However, they must now be accessed via the `L()` and
|
||||||
|
`S()` functions. Users can update their projects with
|
||||||
|
|
||||||
|
```
|
||||||
|
gofmt -r "zap.L -> zap.L()" -w .
|
||||||
|
gofmt -r "zap.S -> zap.S()" -w .
|
||||||
|
```
|
||||||
|
* [#309][] and [#317][]: RC1 was mistakenly shipped with invalid
|
||||||
|
JSON and YAML struct tags on all config structs. This release fixes the tags
|
||||||
|
and adds static analysis to prevent similar bugs in the future.
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
|
||||||
|
* [#321][]: Redirecting the standard library's `log` output now
|
||||||
|
correctly reports the logger's caller.
|
||||||
|
|
||||||
|
Enhancements:
|
||||||
|
|
||||||
|
* [#325][] and [#333][]: Zap now transparently supports non-standard, rich
|
||||||
|
errors like those produced by `github.com/pkg/errors`.
|
||||||
|
* [#326][]: Though `New(nil)` continues to return a no-op logger, `NewNop()` is
|
||||||
|
now preferred. Users can update their projects with `gofmt -r 'zap.New(nil) ->
|
||||||
|
zap.NewNop()' -w .`.
|
||||||
|
* [#300][]: Incorrectly importing zap as `github.com/uber-go/zap` now returns a
|
||||||
|
more informative error.
|
||||||
|
|
||||||
|
Thanks to @skipor and @chapsuk for their contributions to this release.
|
||||||
|
|
||||||
|
## v1.0.0-rc.1 (14 Feb 2017)
|
||||||
|
|
||||||
|
This is the first release candidate for zap's stable release. There are multiple
|
||||||
|
breaking changes and improvements from the pre-release version. Most notably:
|
||||||
|
|
||||||
|
* **Zap's import path is now "go.uber.org/zap"** — all users will
|
||||||
|
need to update their code.
|
||||||
|
* User-facing types and functions remain in the `zap` package. Code relevant
|
||||||
|
largely to extension authors is now in the `zapcore` package.
|
||||||
|
* The `zapcore.Core` type makes it easy for third-party packages to use zap's
|
||||||
|
internals but provide a different user-facing API.
|
||||||
|
* `Logger` is now a concrete type instead of an interface.
|
||||||
|
* A less verbose (though slower) logging API is included by default.
|
||||||
|
* Package-global loggers `L` and `S` are included.
|
||||||
|
* A human-friendly console encoder is included.
|
||||||
|
* A declarative config struct allows common logger configurations to be managed
|
||||||
|
as configuration instead of code.
|
||||||
|
* Sampling is more accurate, and doesn't depend on the standard library's shared
|
||||||
|
timer heap.
|
||||||
|
|
||||||
|
## v0.1.0-beta.1 (6 Feb 2017)
|
||||||
|
|
||||||
|
This is a minor version, tagged to allow users to pin to the pre-1.0 APIs and
|
||||||
|
upgrade at their leisure. Since this is the first tagged release, there are no
|
||||||
|
backward compatibility concerns and all functionality is new.
|
||||||
|
|
||||||
|
Early zap adopters should pin to the 0.1.x minor version until they're ready to
|
||||||
|
upgrade to the upcoming stable release.
|
||||||
|
|
||||||
|
[#316]: https://github.com/uber-go/zap/pull/316
|
||||||
|
[#309]: https://github.com/uber-go/zap/pull/309
|
||||||
|
[#317]: https://github.com/uber-go/zap/pull/317
|
||||||
|
[#321]: https://github.com/uber-go/zap/pull/321
|
||||||
|
[#325]: https://github.com/uber-go/zap/pull/325
|
||||||
|
[#333]: https://github.com/uber-go/zap/pull/333
|
||||||
|
[#326]: https://github.com/uber-go/zap/pull/326
|
||||||
|
[#300]: https://github.com/uber-go/zap/pull/300
|
||||||
|
[#339]: https://github.com/uber-go/zap/pull/339
|
||||||
|
[#307]: https://github.com/uber-go/zap/pull/307
|
||||||
|
[#353]: https://github.com/uber-go/zap/pull/353
|
||||||
|
[#311]: https://github.com/uber-go/zap/pull/311
|
||||||
|
[#366]: https://github.com/uber-go/zap/pull/366
|
||||||
|
[#364]: https://github.com/uber-go/zap/pull/364
|
||||||
|
[#371]: https://github.com/uber-go/zap/pull/371
|
||||||
|
[#362]: https://github.com/uber-go/zap/pull/362
|
||||||
|
[#369]: https://github.com/uber-go/zap/pull/369
|
||||||
|
[#347]: https://github.com/uber-go/zap/pull/347
|
||||||
|
[#373]: https://github.com/uber-go/zap/pull/373
|
||||||
|
[#348]: https://github.com/uber-go/zap/pull/348
|
||||||
|
[#327]: https://github.com/uber-go/zap/pull/327
|
||||||
|
[#376]: https://github.com/uber-go/zap/pull/376
|
||||||
|
[#346]: https://github.com/uber-go/zap/pull/346
|
||||||
|
[#365]: https://github.com/uber-go/zap/pull/365
|
||||||
|
[#372]: https://github.com/uber-go/zap/pull/372
|
||||||
|
[#385]: https://github.com/uber-go/zap/pull/385
|
||||||
|
[#396]: https://github.com/uber-go/zap/pull/396
|
||||||
|
[#386]: https://github.com/uber-go/zap/pull/386
|
||||||
|
[#402]: https://github.com/uber-go/zap/pull/402
|
||||||
|
[#415]: https://github.com/uber-go/zap/pull/415
|
||||||
|
[#416]: https://github.com/uber-go/zap/pull/416
|
||||||
|
[#424]: https://github.com/uber-go/zap/pull/424
|
||||||
|
[#425]: https://github.com/uber-go/zap/pull/425
|
||||||
|
[#431]: https://github.com/uber-go/zap/pull/431
|
||||||
|
[#435]: https://github.com/uber-go/zap/pull/435
|
||||||
|
[#444]: https://github.com/uber-go/zap/pull/444
|
||||||
|
[#477]: https://github.com/uber-go/zap/pull/477
|
||||||
|
[#465]: https://github.com/uber-go/zap/pull/465
|
||||||
|
[#460]: https://github.com/uber-go/zap/pull/460
|
||||||
|
[#470]: https://github.com/uber-go/zap/pull/470
|
||||||
|
[#487]: https://github.com/uber-go/zap/pull/487
|
||||||
|
[#490]: https://github.com/uber-go/zap/pull/490
|
||||||
|
[#491]: https://github.com/uber-go/zap/pull/491
|
||||||
|
[#504]: https://github.com/uber-go/zap/pull/504
|
||||||
|
[#508]: https://github.com/uber-go/zap/pull/508
|
||||||
|
[#518]: https://github.com/uber-go/zap/pull/518
|
||||||
|
[#577]: https://github.com/uber-go/zap/pull/577
|
||||||
|
[#574]: https://github.com/uber-go/zap/pull/574
|
||||||
|
[#602]: https://github.com/uber-go/zap/pull/602
|
||||||
|
[#572]: https://github.com/uber-go/zap/pull/572
|
||||||
|
[#606]: https://github.com/uber-go/zap/pull/606
|
||||||
|
[#614]: https://github.com/uber-go/zap/pull/614
|
||||||
|
[#657]: https://github.com/uber-go/zap/pull/657
|
||||||
|
[#706]: https://github.com/uber-go/zap/pull/706
|
||||||
|
[#610]: https://github.com/uber-go/zap/pull/610
|
||||||
|
[#675]: https://github.com/uber-go/zap/pull/675
|
||||||
|
[#704]: https://github.com/uber-go/zap/pull/704
|
||||||
|
[#725]: https://github.com/uber-go/zap/pull/725
|
||||||
|
[#736]: https://github.com/uber-go/zap/pull/736
|
||||||
|
[#751]: https://github.com/uber-go/zap/pull/751
|
||||||
|
[#758]: https://github.com/uber-go/zap/pull/758
|
||||||
|
[#771]: https://github.com/uber-go/zap/pull/771
|
||||||
|
[#773]: https://github.com/uber-go/zap/pull/773
|
||||||
|
[#775]: https://github.com/uber-go/zap/pull/775
|
||||||
|
[#786]: https://github.com/uber-go/zap/pull/786
|
||||||
|
[#791]: https://github.com/uber-go/zap/pull/791
|
||||||
|
[#795]: https://github.com/uber-go/zap/pull/795
|
||||||
|
[#799]: https://github.com/uber-go/zap/pull/799
|
||||||
|
[#804]: https://github.com/uber-go/zap/pull/804
|
||||||
|
[#812]: https://github.com/uber-go/zap/pull/812
|
||||||
|
[#806]: https://github.com/uber-go/zap/pull/806
|
||||||
|
[#813]: https://github.com/uber-go/zap/pull/813
|
||||||
|
[#629]: https://github.com/uber-go/zap/pull/629
|
||||||
|
[#697]: https://github.com/uber-go/zap/pull/697
|
||||||
|
[#828]: https://github.com/uber-go/zap/pull/828
|
||||||
|
[#835]: https://github.com/uber-go/zap/pull/835
|
||||||
|
[#843]: https://github.com/uber-go/zap/pull/843
|
||||||
|
[#844]: https://github.com/uber-go/zap/pull/844
|
||||||
|
[#852]: https://github.com/uber-go/zap/pull/852
|
||||||
|
[#854]: https://github.com/uber-go/zap/pull/854
|
||||||
|
[#861]: https://github.com/uber-go/zap/pull/861
|
||||||
|
[#862]: https://github.com/uber-go/zap/pull/862
|
||||||
|
[#865]: https://github.com/uber-go/zap/pull/865
|
||||||
|
[#867]: https://github.com/uber-go/zap/pull/867
|
||||||
|
[#881]: https://github.com/uber-go/zap/pull/881
|
||||||
|
[#903]: https://github.com/uber-go/zap/pull/903
|
||||||
|
[#912]: https://github.com/uber-go/zap/pull/912
|
||||||
|
[#913]: https://github.com/uber-go/zap/pull/913
|
||||||
|
[#928]: https://github.com/uber-go/zap/pull/928
|
||||||
|
[#931]: https://github.com/uber-go/zap/pull/931
|
||||||
|
[#936]: https://github.com/uber-go/zap/pull/936
|
75
vendor/go.uber.org/zap/CODE_OF_CONDUCT.md
generated
vendored
Normal file
75
vendor/go.uber.org/zap/CODE_OF_CONDUCT.md
generated
vendored
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
# Contributor Covenant Code of Conduct
|
||||||
|
|
||||||
|
## Our Pledge
|
||||||
|
|
||||||
|
In the interest of fostering an open and welcoming environment, we as
|
||||||
|
contributors and maintainers pledge to making participation in our project and
|
||||||
|
our community a harassment-free experience for everyone, regardless of age,
|
||||||
|
body size, disability, ethnicity, gender identity and expression, level of
|
||||||
|
experience, nationality, personal appearance, race, religion, or sexual
|
||||||
|
identity and orientation.
|
||||||
|
|
||||||
|
## Our Standards
|
||||||
|
|
||||||
|
Examples of behavior that contributes to creating a positive environment
|
||||||
|
include:
|
||||||
|
|
||||||
|
* Using welcoming and inclusive language
|
||||||
|
* Being respectful of differing viewpoints and experiences
|
||||||
|
* Gracefully accepting constructive criticism
|
||||||
|
* Focusing on what is best for the community
|
||||||
|
* Showing empathy towards other community members
|
||||||
|
|
||||||
|
Examples of unacceptable behavior by participants include:
|
||||||
|
|
||||||
|
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||||
|
advances
|
||||||
|
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||||
|
* Public or private harassment
|
||||||
|
* Publishing others' private information, such as a physical or electronic
|
||||||
|
address, without explicit permission
|
||||||
|
* Other conduct which could reasonably be considered inappropriate in a
|
||||||
|
professional setting
|
||||||
|
|
||||||
|
## Our Responsibilities
|
||||||
|
|
||||||
|
Project maintainers are responsible for clarifying the standards of acceptable
|
||||||
|
behavior and are expected to take appropriate and fair corrective action in
|
||||||
|
response to any instances of unacceptable behavior.
|
||||||
|
|
||||||
|
Project maintainers have the right and responsibility to remove, edit, or
|
||||||
|
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||||
|
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||||
|
permanently any contributor for other behaviors that they deem inappropriate,
|
||||||
|
threatening, offensive, or harmful.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This Code of Conduct applies both within project spaces and in public spaces
|
||||||
|
when an individual is representing the project or its community. Examples of
|
||||||
|
representing a project or community include using an official project e-mail
|
||||||
|
address, posting via an official social media account, or acting as an
|
||||||
|
appointed representative at an online or offline event. Representation of a
|
||||||
|
project may be further defined and clarified by project maintainers.
|
||||||
|
|
||||||
|
## Enforcement
|
||||||
|
|
||||||
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||||
|
reported by contacting the project team at oss-conduct@uber.com. The project
|
||||||
|
team will review and investigate all complaints, and will respond in a way
|
||||||
|
that it deems appropriate to the circumstances. The project team is obligated
|
||||||
|
to maintain confidentiality with regard to the reporter of an incident.
|
||||||
|
Further details of specific enforcement policies may be posted separately.
|
||||||
|
|
||||||
|
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||||
|
faith may face temporary or permanent repercussions as determined by other
|
||||||
|
members of the project's leadership.
|
||||||
|
|
||||||
|
## Attribution
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||||
|
version 1.4, available at
|
||||||
|
[http://contributor-covenant.org/version/1/4][version].
|
||||||
|
|
||||||
|
[homepage]: http://contributor-covenant.org
|
||||||
|
[version]: http://contributor-covenant.org/version/1/4/
|
75
vendor/go.uber.org/zap/CONTRIBUTING.md
generated
vendored
Normal file
75
vendor/go.uber.org/zap/CONTRIBUTING.md
generated
vendored
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
# Contributing
|
||||||
|
|
||||||
|
We'd love your help making zap the very best structured logging library in Go!
|
||||||
|
|
||||||
|
If you'd like to add new exported APIs, please [open an issue][open-issue]
|
||||||
|
describing your proposal — discussing API changes ahead of time makes
|
||||||
|
pull request review much smoother. In your issue, pull request, and any other
|
||||||
|
communications, please remember to treat your fellow contributors with
|
||||||
|
respect! We take our [code of conduct](CODE_OF_CONDUCT.md) seriously.
|
||||||
|
|
||||||
|
Note that you'll need to sign [Uber's Contributor License Agreement][cla]
|
||||||
|
before we can accept any of your contributions. If necessary, a bot will remind
|
||||||
|
you to accept the CLA when you open your pull request.
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
[Fork][fork], then clone the repository:
|
||||||
|
|
||||||
|
```
|
||||||
|
mkdir -p $GOPATH/src/go.uber.org
|
||||||
|
cd $GOPATH/src/go.uber.org
|
||||||
|
git clone git@github.com:your_github_username/zap.git
|
||||||
|
cd zap
|
||||||
|
git remote add upstream https://github.com/uber-go/zap.git
|
||||||
|
git fetch upstream
|
||||||
|
```
|
||||||
|
|
||||||
|
Make sure that the tests and the linters pass:
|
||||||
|
|
||||||
|
```
|
||||||
|
make test
|
||||||
|
make lint
|
||||||
|
```
|
||||||
|
|
||||||
|
If you're not using the minor version of Go specified in the Makefile's
|
||||||
|
`LINTABLE_MINOR_VERSIONS` variable, `make lint` doesn't do anything. This is
|
||||||
|
fine, but it means that you'll only discover lint failures after you open your
|
||||||
|
pull request.
|
||||||
|
|
||||||
|
## Making Changes
|
||||||
|
|
||||||
|
Start by creating a new branch for your changes:
|
||||||
|
|
||||||
|
```
|
||||||
|
cd $GOPATH/src/go.uber.org/zap
|
||||||
|
git checkout master
|
||||||
|
git fetch upstream
|
||||||
|
git rebase upstream/master
|
||||||
|
git checkout -b cool_new_feature
|
||||||
|
```
|
||||||
|
|
||||||
|
Make your changes, then ensure that `make lint` and `make test` still pass. If
|
||||||
|
you're satisfied with your changes, push them to your fork.
|
||||||
|
|
||||||
|
```
|
||||||
|
git push origin cool_new_feature
|
||||||
|
```
|
||||||
|
|
||||||
|
Then use the GitHub UI to open a pull request.
|
||||||
|
|
||||||
|
At this point, you're waiting on us to review your changes. We *try* to respond
|
||||||
|
to issues and pull requests within a few business days, and we may suggest some
|
||||||
|
improvements or alternatives. Once your changes are approved, one of the
|
||||||
|
project maintainers will merge them.
|
||||||
|
|
||||||
|
We're much more likely to approve your changes if you:
|
||||||
|
|
||||||
|
* Add tests for new functionality.
|
||||||
|
* Write a [good commit message][commit-message].
|
||||||
|
* Maintain backward compatibility.
|
||||||
|
|
||||||
|
[fork]: https://github.com/uber-go/zap/fork
|
||||||
|
[open-issue]: https://github.com/uber-go/zap/issues/new
|
||||||
|
[cla]: https://cla-assistant.io/uber-go/zap
|
||||||
|
[commit-message]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
|
164
vendor/go.uber.org/zap/FAQ.md
generated
vendored
Normal file
164
vendor/go.uber.org/zap/FAQ.md
generated
vendored
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
# Frequently Asked Questions
|
||||||
|
|
||||||
|
## Design
|
||||||
|
|
||||||
|
### Why spend so much effort on logger performance?
|
||||||
|
|
||||||
|
Of course, most applications won't notice the impact of a slow logger: they
|
||||||
|
already take tens or hundreds of milliseconds for each operation, so an extra
|
||||||
|
millisecond doesn't matter.
|
||||||
|
|
||||||
|
On the other hand, why *not* make structured logging fast? The `SugaredLogger`
|
||||||
|
isn't any harder to use than other logging packages, and the `Logger` makes
|
||||||
|
structured logging possible in performance-sensitive contexts. Across a fleet
|
||||||
|
of Go microservices, making each application even slightly more efficient adds
|
||||||
|
up quickly.
|
||||||
|
|
||||||
|
### Why aren't `Logger` and `SugaredLogger` interfaces?
|
||||||
|
|
||||||
|
Unlike the familiar `io.Writer` and `http.Handler`, `Logger` and
|
||||||
|
`SugaredLogger` interfaces would include *many* methods. As [Rob Pike points
|
||||||
|
out][go-proverbs], "The bigger the interface, the weaker the abstraction."
|
||||||
|
Interfaces are also rigid — *any* change requires releasing a new major
|
||||||
|
version, since it breaks all third-party implementations.
|
||||||
|
|
||||||
|
Making the `Logger` and `SugaredLogger` concrete types doesn't sacrifice much
|
||||||
|
abstraction, and it lets us add methods without introducing breaking changes.
|
||||||
|
Your applications should define and depend upon an interface that includes
|
||||||
|
just the methods you use.
|
||||||
|
|
||||||
|
### Why are some of my logs missing?
|
||||||
|
|
||||||
|
Logs are dropped intentionally by zap when sampling is enabled. The production
|
||||||
|
configuration (as returned by `NewProductionConfig()` enables sampling which will
|
||||||
|
cause repeated logs within a second to be sampled. See more details on why sampling
|
||||||
|
is enabled in [Why sample application logs](https://github.com/uber-go/zap/blob/master/FAQ.md#why-sample-application-logs).
|
||||||
|
|
||||||
|
### Why sample application logs?
|
||||||
|
|
||||||
|
Applications often experience runs of errors, either because of a bug or
|
||||||
|
because of a misbehaving user. Logging errors is usually a good idea, but it
|
||||||
|
can easily make this bad situation worse: not only is your application coping
|
||||||
|
with a flood of errors, it's also spending extra CPU cycles and I/O logging
|
||||||
|
those errors. Since writes are typically serialized, logging limits throughput
|
||||||
|
when you need it most.
|
||||||
|
|
||||||
|
Sampling fixes this problem by dropping repetitive log entries. Under normal
|
||||||
|
conditions, your application writes out every entry. When similar entries are
|
||||||
|
logged hundreds or thousands of times each second, though, zap begins dropping
|
||||||
|
duplicates to preserve throughput.
|
||||||
|
|
||||||
|
### Why do the structured logging APIs take a message in addition to fields?
|
||||||
|
|
||||||
|
Subjectively, we find it helpful to accompany structured context with a brief
|
||||||
|
description. This isn't critical during development, but it makes debugging
|
||||||
|
and operating unfamiliar systems much easier.
|
||||||
|
|
||||||
|
More concretely, zap's sampling algorithm uses the message to identify
|
||||||
|
duplicate entries. In our experience, this is a practical middle ground
|
||||||
|
between random sampling (which often drops the exact entry that you need while
|
||||||
|
debugging) and hashing the complete entry (which is prohibitively expensive).
|
||||||
|
|
||||||
|
### Why include package-global loggers?
|
||||||
|
|
||||||
|
Since so many other logging packages include a global logger, many
|
||||||
|
applications aren't designed to accept loggers as explicit parameters.
|
||||||
|
Changing function signatures is often a breaking change, so zap includes
|
||||||
|
global loggers to simplify migration.
|
||||||
|
|
||||||
|
Avoid them where possible.
|
||||||
|
|
||||||
|
### Why include dedicated Panic and Fatal log levels?
|
||||||
|
|
||||||
|
In general, application code should handle errors gracefully instead of using
|
||||||
|
`panic` or `os.Exit`. However, every rule has exceptions, and it's common to
|
||||||
|
crash when an error is truly unrecoverable. To avoid losing any information
|
||||||
|
— especially the reason for the crash — the logger must flush any
|
||||||
|
buffered entries before the process exits.
|
||||||
|
|
||||||
|
Zap makes this easy by offering `Panic` and `Fatal` logging methods that
|
||||||
|
automatically flush before exiting. Of course, this doesn't guarantee that
|
||||||
|
logs will never be lost, but it eliminates a common error.
|
||||||
|
|
||||||
|
See the discussion in uber-go/zap#207 for more details.
|
||||||
|
|
||||||
|
### What's `DPanic`?
|
||||||
|
|
||||||
|
`DPanic` stands for "panic in development." In development, it logs at
|
||||||
|
`PanicLevel`; otherwise, it logs at `ErrorLevel`. `DPanic` makes it easier to
|
||||||
|
catch errors that are theoretically possible, but shouldn't actually happen,
|
||||||
|
*without* crashing in production.
|
||||||
|
|
||||||
|
If you've ever written code like this, you need `DPanic`:
|
||||||
|
|
||||||
|
```go
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("shouldn't ever get here: %v", err))
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
### What does the error `expects import "go.uber.org/zap"` mean?
|
||||||
|
|
||||||
|
Either zap was installed incorrectly or you're referencing the wrong package
|
||||||
|
name in your code.
|
||||||
|
|
||||||
|
Zap's source code happens to be hosted on GitHub, but the [import
|
||||||
|
path][import-path] is `go.uber.org/zap`. This gives us, the project
|
||||||
|
maintainers, the freedom to move the source code if necessary. However, it
|
||||||
|
means that you need to take a little care when installing and using the
|
||||||
|
package.
|
||||||
|
|
||||||
|
If you follow two simple rules, everything should work: install zap with `go
|
||||||
|
get -u go.uber.org/zap`, and always import it in your code with `import
|
||||||
|
"go.uber.org/zap"`. Your code shouldn't contain *any* references to
|
||||||
|
`github.com/uber-go/zap`.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Does zap support log rotation?
|
||||||
|
|
||||||
|
Zap doesn't natively support rotating log files, since we prefer to leave this
|
||||||
|
to an external program like `logrotate`.
|
||||||
|
|
||||||
|
However, it's easy to integrate a log rotation package like
|
||||||
|
[`gopkg.in/natefinch/lumberjack.v2`][lumberjack] as a `zapcore.WriteSyncer`.
|
||||||
|
|
||||||
|
```go
|
||||||
|
// lumberjack.Logger is already safe for concurrent use, so we don't need to
|
||||||
|
// lock it.
|
||||||
|
w := zapcore.AddSync(&lumberjack.Logger{
|
||||||
|
Filename: "/var/log/myapp/foo.log",
|
||||||
|
MaxSize: 500, // megabytes
|
||||||
|
MaxBackups: 3,
|
||||||
|
MaxAge: 28, // days
|
||||||
|
})
|
||||||
|
core := zapcore.NewCore(
|
||||||
|
zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()),
|
||||||
|
w,
|
||||||
|
zap.InfoLevel,
|
||||||
|
)
|
||||||
|
logger := zap.New(core)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Extensions
|
||||||
|
|
||||||
|
We'd love to support every logging need within zap itself, but we're only
|
||||||
|
familiar with a handful of log ingestion systems, flag-parsing packages, and
|
||||||
|
the like. Rather than merging code that we can't effectively debug and
|
||||||
|
support, we'd rather grow an ecosystem of zap extensions.
|
||||||
|
|
||||||
|
We're aware of the following extensions, but haven't used them ourselves:
|
||||||
|
|
||||||
|
| Package | Integration |
|
||||||
|
| --- | --- |
|
||||||
|
| `github.com/tchap/zapext` | Sentry, syslog |
|
||||||
|
| `github.com/fgrosse/zaptest` | Ginkgo |
|
||||||
|
| `github.com/blendle/zapdriver` | Stackdriver |
|
||||||
|
| `github.com/moul/zapgorm` | Gorm |
|
||||||
|
| `github.com/moul/zapfilter` | Advanced filtering rules |
|
||||||
|
|
||||||
|
[go-proverbs]: https://go-proverbs.github.io/
|
||||||
|
[import-path]: https://golang.org/cmd/go/#hdr-Remote_import_paths
|
||||||
|
[lumberjack]: https://godoc.org/gopkg.in/natefinch/lumberjack.v2
|
19
vendor/go.uber.org/zap/LICENSE.txt
generated
vendored
Normal file
19
vendor/go.uber.org/zap/LICENSE.txt
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
Copyright (c) 2016-2017 Uber Technologies, Inc.
|
||||||
|
|
||||||
|
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.
|
73
vendor/go.uber.org/zap/Makefile
generated
vendored
Normal file
73
vendor/go.uber.org/zap/Makefile
generated
vendored
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
export GOBIN ?= $(shell pwd)/bin
|
||||||
|
|
||||||
|
GOLINT = $(GOBIN)/golint
|
||||||
|
STATICCHECK = $(GOBIN)/staticcheck
|
||||||
|
BENCH_FLAGS ?= -cpuprofile=cpu.pprof -memprofile=mem.pprof -benchmem
|
||||||
|
|
||||||
|
# Directories containing independent Go modules.
|
||||||
|
#
|
||||||
|
# We track coverage only for the main module.
|
||||||
|
MODULE_DIRS = . ./benchmarks ./zapgrpc/internal/test
|
||||||
|
|
||||||
|
# Many Go tools take file globs or directories as arguments instead of packages.
|
||||||
|
GO_FILES := $(shell \
|
||||||
|
find . '(' -path '*/.*' -o -path './vendor' ')' -prune \
|
||||||
|
-o -name '*.go' -print | cut -b3-)
|
||||||
|
|
||||||
|
.PHONY: all
|
||||||
|
all: lint test
|
||||||
|
|
||||||
|
.PHONY: lint
|
||||||
|
lint: $(GOLINT) $(STATICCHECK)
|
||||||
|
@rm -rf lint.log
|
||||||
|
@echo "Checking formatting..."
|
||||||
|
@gofmt -d -s $(GO_FILES) 2>&1 | tee lint.log
|
||||||
|
@echo "Checking vet..."
|
||||||
|
@$(foreach dir,$(MODULE_DIRS),(cd $(dir) && go vet ./... 2>&1) &&) true | tee -a lint.log
|
||||||
|
@echo "Checking lint..."
|
||||||
|
@$(foreach dir,$(MODULE_DIRS),(cd $(dir) && $(GOLINT) ./... 2>&1) &&) true | tee -a lint.log
|
||||||
|
@echo "Checking staticcheck..."
|
||||||
|
@$(foreach dir,$(MODULE_DIRS),(cd $(dir) && $(STATICCHECK) ./... 2>&1) &&) true | tee -a lint.log
|
||||||
|
@echo "Checking for unresolved FIXMEs..."
|
||||||
|
@git grep -i fixme | grep -v -e Makefile | tee -a lint.log
|
||||||
|
@echo "Checking for license headers..."
|
||||||
|
@./checklicense.sh | tee -a lint.log
|
||||||
|
@[ ! -s lint.log ]
|
||||||
|
@echo "Checking 'go mod tidy'..."
|
||||||
|
@make tidy
|
||||||
|
@if ! git diff --quiet; then \
|
||||||
|
echo "'go mod tidy' resulted in changes or working tree is dirty:"; \
|
||||||
|
git --no-pager diff; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
$(GOLINT):
|
||||||
|
cd tools && go install golang.org/x/lint/golint
|
||||||
|
|
||||||
|
$(STATICCHECK):
|
||||||
|
cd tools && go install honnef.co/go/tools/cmd/staticcheck
|
||||||
|
|
||||||
|
.PHONY: test
|
||||||
|
test:
|
||||||
|
@$(foreach dir,$(MODULE_DIRS),(cd $(dir) && go test -race ./...) &&) true
|
||||||
|
|
||||||
|
.PHONY: cover
|
||||||
|
cover:
|
||||||
|
go test -race -coverprofile=cover.out -coverpkg=./... ./...
|
||||||
|
go tool cover -html=cover.out -o cover.html
|
||||||
|
|
||||||
|
.PHONY: bench
|
||||||
|
BENCH ?= .
|
||||||
|
bench:
|
||||||
|
@$(foreach dir,$(MODULE_DIRS), ( \
|
||||||
|
cd $(dir) && \
|
||||||
|
go list ./... | xargs -n1 go test -bench=$(BENCH) -run="^$$" $(BENCH_FLAGS) \
|
||||||
|
) &&) true
|
||||||
|
|
||||||
|
.PHONY: updatereadme
|
||||||
|
updatereadme:
|
||||||
|
rm -f README.md
|
||||||
|
cat .readme.tmpl | go run internal/readme/readme.go > README.md
|
||||||
|
|
||||||
|
.PHONY: tidy
|
||||||
|
tidy:
|
||||||
|
@$(foreach dir,$(MODULE_DIRS),(cd $(dir) && go mod tidy) &&) true
|
134
vendor/go.uber.org/zap/README.md
generated
vendored
Normal file
134
vendor/go.uber.org/zap/README.md
generated
vendored
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
# :zap: zap [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov]
|
||||||
|
|
||||||
|
Blazing fast, structured, leveled logging in Go.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
`go get -u go.uber.org/zap`
|
||||||
|
|
||||||
|
Note that zap only supports the two most recent minor versions of Go.
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
In contexts where performance is nice, but not critical, use the
|
||||||
|
`SugaredLogger`. It's 4-10x faster than other structured logging
|
||||||
|
packages and includes both structured and `printf`-style APIs.
|
||||||
|
|
||||||
|
```go
|
||||||
|
logger, _ := zap.NewProduction()
|
||||||
|
defer logger.Sync() // flushes buffer, if any
|
||||||
|
sugar := logger.Sugar()
|
||||||
|
sugar.Infow("failed to fetch URL",
|
||||||
|
// Structured context as loosely typed key-value pairs.
|
||||||
|
"url", url,
|
||||||
|
"attempt", 3,
|
||||||
|
"backoff", time.Second,
|
||||||
|
)
|
||||||
|
sugar.Infof("Failed to fetch URL: %s", url)
|
||||||
|
```
|
||||||
|
|
||||||
|
When performance and type safety are critical, use the `Logger`. It's even
|
||||||
|
faster than the `SugaredLogger` and allocates far less, but it only supports
|
||||||
|
structured logging.
|
||||||
|
|
||||||
|
```go
|
||||||
|
logger, _ := zap.NewProduction()
|
||||||
|
defer logger.Sync()
|
||||||
|
logger.Info("failed to fetch URL",
|
||||||
|
// Structured context as strongly typed Field values.
|
||||||
|
zap.String("url", url),
|
||||||
|
zap.Int("attempt", 3),
|
||||||
|
zap.Duration("backoff", time.Second),
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
See the [documentation][doc] and [FAQ](FAQ.md) for more details.
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
|
||||||
|
For applications that log in the hot path, reflection-based serialization and
|
||||||
|
string formatting are prohibitively expensive — they're CPU-intensive
|
||||||
|
and make many small allocations. Put differently, using `encoding/json` and
|
||||||
|
`fmt.Fprintf` to log tons of `interface{}`s makes your application slow.
|
||||||
|
|
||||||
|
Zap takes a different approach. It includes a reflection-free, zero-allocation
|
||||||
|
JSON encoder, and the base `Logger` strives to avoid serialization overhead
|
||||||
|
and allocations wherever possible. By building the high-level `SugaredLogger`
|
||||||
|
on that foundation, zap lets users *choose* when they need to count every
|
||||||
|
allocation and when they'd prefer a more familiar, loosely typed API.
|
||||||
|
|
||||||
|
As measured by its own [benchmarking suite][], not only is zap more performant
|
||||||
|
than comparable structured logging packages — it's also faster than the
|
||||||
|
standard library. Like all benchmarks, take these with a grain of salt.<sup
|
||||||
|
id="anchor-versions">[1](#footnote-versions)</sup>
|
||||||
|
|
||||||
|
Log a message and 10 fields:
|
||||||
|
|
||||||
|
| Package | Time | Time % to zap | Objects Allocated |
|
||||||
|
| :------ | :--: | :-----------: | :---------------: |
|
||||||
|
| :zap: zap | 862 ns/op | +0% | 5 allocs/op
|
||||||
|
| :zap: zap (sugared) | 1250 ns/op | +45% | 11 allocs/op
|
||||||
|
| zerolog | 4021 ns/op | +366% | 76 allocs/op
|
||||||
|
| go-kit | 4542 ns/op | +427% | 105 allocs/op
|
||||||
|
| apex/log | 26785 ns/op | +3007% | 115 allocs/op
|
||||||
|
| logrus | 29501 ns/op | +3322% | 125 allocs/op
|
||||||
|
| log15 | 29906 ns/op | +3369% | 122 allocs/op
|
||||||
|
|
||||||
|
Log a message with a logger that already has 10 fields of context:
|
||||||
|
|
||||||
|
| Package | Time | Time % to zap | Objects Allocated |
|
||||||
|
| :------ | :--: | :-----------: | :---------------: |
|
||||||
|
| :zap: zap | 126 ns/op | +0% | 0 allocs/op
|
||||||
|
| :zap: zap (sugared) | 187 ns/op | +48% | 2 allocs/op
|
||||||
|
| zerolog | 88 ns/op | -30% | 0 allocs/op
|
||||||
|
| go-kit | 5087 ns/op | +3937% | 103 allocs/op
|
||||||
|
| log15 | 18548 ns/op | +14621% | 73 allocs/op
|
||||||
|
| apex/log | 26012 ns/op | +20544% | 104 allocs/op
|
||||||
|
| logrus | 27236 ns/op | +21516% | 113 allocs/op
|
||||||
|
|
||||||
|
Log a static string, without any context or `printf`-style templating:
|
||||||
|
|
||||||
|
| Package | Time | Time % to zap | Objects Allocated |
|
||||||
|
| :------ | :--: | :-----------: | :---------------: |
|
||||||
|
| :zap: zap | 118 ns/op | +0% | 0 allocs/op
|
||||||
|
| :zap: zap (sugared) | 191 ns/op | +62% | 2 allocs/op
|
||||||
|
| zerolog | 93 ns/op | -21% | 0 allocs/op
|
||||||
|
| go-kit | 280 ns/op | +137% | 11 allocs/op
|
||||||
|
| standard library | 499 ns/op | +323% | 2 allocs/op
|
||||||
|
| apex/log | 1990 ns/op | +1586% | 10 allocs/op
|
||||||
|
| logrus | 3129 ns/op | +2552% | 24 allocs/op
|
||||||
|
| log15 | 3887 ns/op | +3194% | 23 allocs/op
|
||||||
|
|
||||||
|
## Development Status: Stable
|
||||||
|
|
||||||
|
All APIs are finalized, and no breaking changes will be made in the 1.x series
|
||||||
|
of releases. Users of semver-aware dependency management systems should pin
|
||||||
|
zap to `^1`.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
We encourage and support an active, healthy community of contributors —
|
||||||
|
including you! Details are in the [contribution guide](CONTRIBUTING.md) and
|
||||||
|
the [code of conduct](CODE_OF_CONDUCT.md). The zap maintainers keep an eye on
|
||||||
|
issues and pull requests, but you can also report any negative conduct to
|
||||||
|
oss-conduct@uber.com. That email list is a private, safe space; even the zap
|
||||||
|
maintainers don't have access, so don't hesitate to hold us to a high
|
||||||
|
standard.
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
Released under the [MIT License](LICENSE.txt).
|
||||||
|
|
||||||
|
<sup id="footnote-versions">1</sup> In particular, keep in mind that we may be
|
||||||
|
benchmarking against slightly older versions of other packages. Versions are
|
||||||
|
pinned in the [benchmarks/go.mod][] file. [↩](#anchor-versions)
|
||||||
|
|
||||||
|
[doc-img]: https://pkg.go.dev/badge/go.uber.org/zap
|
||||||
|
[doc]: https://pkg.go.dev/go.uber.org/zap
|
||||||
|
[ci-img]: https://github.com/uber-go/zap/actions/workflows/go.yml/badge.svg
|
||||||
|
[ci]: https://github.com/uber-go/zap/actions/workflows/go.yml
|
||||||
|
[cov-img]: https://codecov.io/gh/uber-go/zap/branch/master/graph/badge.svg
|
||||||
|
[cov]: https://codecov.io/gh/uber-go/zap
|
||||||
|
[benchmarking suite]: https://github.com/uber-go/zap/tree/master/benchmarks
|
||||||
|
[benchmarks/go.mod]: https://github.com/uber-go/zap/blob/master/benchmarks/go.mod
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user