master
李光春 2 years ago
parent d641e26624
commit 2f7385f8f6

3
.gitignore vendored

@ -5,4 +5,5 @@
.vscode .vscode
*.log *.log
gomod.sh gomod.sh
*_test.go *_test.go
/vendor/

@ -1,95 +1,49 @@
package dingtalk package dingtalk
import ( import (
"go.dtapp.net/dorm"
"go.dtapp.net/golog" "go.dtapp.net/golog"
"go.dtapp.net/gorequest" "go.dtapp.net/gorequest"
) )
// client *dorm.GormClient
type gormClientFun func() *dorm.GormClient
// client *dorm.MongoClient
// databaseName string
type mongoClientFun func() (*dorm.MongoClient, string)
// ClientConfig 实例配置 // ClientConfig 实例配置
type ClientConfig struct { type ClientConfig struct {
Secret string Secret string
AccessToken string AccessToken string
GormClientFun gormClientFun // 日志配置 ApiGormClientFun golog.ApiClientFun // 日志配置
MongoClientFun mongoClientFun // 日志配置 Debug bool // 日志开关
Debug bool // 日志开关 ZapLog *golog.ZapLog // 日志服务
ZapLog *golog.ZapLog // 日志服务
CurrentIp string // 当前ip
} }
// Client 实例 // Client 实例
type Client struct { type Client struct {
requestClient *gorequest.App // 请求服务 requestClient *gorequest.App // 请求服务
zapLog *golog.ZapLog // 日志服务 zapLog *golog.ZapLog // 日志服务
currentIp string // 当前ip
config struct { config struct {
secret string secret string
accessToken string accessToken string
} }
log struct { log struct {
gorm bool // 日志开关 status bool // 状态
gormClient *dorm.GormClient // 日志数据库 client *golog.ApiClient // 日志服务
logGormClient *golog.ApiClient // 日志服务
mongo bool // 日志开关
mongoClient *dorm.MongoClient // 日志数据库
logMongoClient *golog.ApiClient // 日志服务
} }
} }
// NewClient 创建实例化 // NewClient 创建实例化
func NewClient(config *ClientConfig) (*Client, error) { func NewClient(config *ClientConfig) (*Client, error) {
var err error
c := &Client{} c := &Client{}
c.zapLog = config.ZapLog c.zapLog = config.ZapLog
c.currentIp = config.CurrentIp
c.config.secret = config.Secret c.config.secret = config.Secret
c.config.accessToken = config.AccessToken c.config.accessToken = config.AccessToken
c.requestClient = gorequest.NewHttp() c.requestClient = gorequest.NewHttp()
gormClient := config.GormClientFun() apiGormClient := config.ApiGormClientFun()
if gormClient != nil && gormClient.Db != nil { if apiGormClient != nil {
c.log.logGormClient, err = golog.NewApiGormClient(&golog.ApiGormClientConfig{ c.log.client = apiGormClient
GormClientFun: func() (*dorm.GormClient, string) { c.log.status = true
return gormClient, logTable
},
Debug: config.Debug,
ZapLog: c.zapLog,
CurrentIp: c.currentIp,
})
if err != nil {
return nil, err
}
c.log.gorm = true
c.log.gormClient = gormClient
}
mongoClient, databaseName := config.MongoClientFun()
if mongoClient != nil && mongoClient.Db != nil {
c.log.logMongoClient, err = golog.NewApiMongoClient(&golog.ApiMongoClientConfig{
MongoClientFun: func() (*dorm.MongoClient, string, string) {
return mongoClient, databaseName, logTable
},
Debug: config.Debug,
ZapLog: c.zapLog,
CurrentIp: c.currentIp,
})
if err != nil {
return nil, err
}
c.log.mongo = true
c.log.mongoClient = mongoClient
} }
return c, nil return c, nil

@ -1,7 +1,10 @@
package dingtalk package dingtalk
const ( const (
apiUrl = "https://oapi.dingtalk.com" apiUrl = "https://oapi.dingtalk.com"
)
const (
logTable = "dingtalk" logTable = "dingtalk"
Version = "1.0.10" Version = "1.0.11"
) )

@ -3,8 +3,8 @@ module go.dtapp.net/dingtalk
go 1.19 go 1.19
require ( require (
go.dtapp.net/dorm v1.0.33 go.dtapp.net/dorm v1.0.38
go.dtapp.net/golog v1.0.73 go.dtapp.net/golog v1.0.87
go.dtapp.net/gorequest v1.0.31 go.dtapp.net/gorequest v1.0.31
) )
@ -17,7 +17,7 @@ require (
github.com/gin-gonic/gin v1.8.1 // indirect github.com/gin-gonic/gin v1.8.1 // indirect
github.com/go-playground/locales v0.14.0 // indirect github.com/go-playground/locales v0.14.0 // indirect
github.com/go-playground/universal-translator v0.18.0 // indirect github.com/go-playground/universal-translator v0.18.0 // indirect
github.com/go-playground/validator/v10 v10.11.0 // indirect github.com/go-playground/validator/v10 v10.11.1 // indirect
github.com/go-redis/redis/v9 v9.0.0-beta.2 // indirect github.com/go-redis/redis/v9 v9.0.0-beta.2 // indirect
github.com/go-sql-driver/mysql v1.6.0 // indirect github.com/go-sql-driver/mysql v1.6.0 // indirect
github.com/goccy/go-json v0.9.11 // indirect github.com/goccy/go-json v0.9.11 // indirect
@ -34,7 +34,7 @@ require (
github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect github.com/jinzhu/now v1.1.5 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.15.9 // indirect github.com/klauspost/compress v1.15.10 // indirect
github.com/leodido/go-urn v1.2.1 // indirect github.com/leodido/go-urn v1.2.1 // indirect
github.com/lib/pq v1.10.7 // indirect github.com/lib/pq v1.10.7 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect github.com/mattn/go-isatty v0.0.16 // indirect
@ -42,6 +42,8 @@ require (
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/montanaflynn/stats v0.6.6 // indirect github.com/montanaflynn/stats v0.6.6 // indirect
github.com/natefinch/lumberjack v2.0.0+incompatible // indirect github.com/natefinch/lumberjack v2.0.0+incompatible // indirect
github.com/oschwald/geoip2-golang v1.8.0 // indirect
github.com/oschwald/maxminddb-golang v1.10.0 // indirect
github.com/pelletier/go-toml/v2 v2.0.5 // indirect github.com/pelletier/go-toml/v2 v2.0.5 // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/saracen/go7z v0.0.0-20191010121135-9c09b6bd7fda // indirect github.com/saracen/go7z v0.0.0-20191010121135-9c09b6bd7fda // indirect
@ -64,7 +66,7 @@ require (
github.com/xdg-go/scram v1.1.1 // indirect github.com/xdg-go/scram v1.1.1 // indirect
github.com/xdg-go/stringprep v1.0.3 // indirect github.com/xdg-go/stringprep v1.0.3 // indirect
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect
go.dtapp.net/goip v1.0.30 // indirect go.dtapp.net/goip v1.0.36 // indirect
go.dtapp.net/gorandom v1.0.1 // indirect go.dtapp.net/gorandom v1.0.1 // indirect
go.dtapp.net/gostring v1.0.10 // indirect go.dtapp.net/gostring v1.0.10 // indirect
go.dtapp.net/gotime v1.0.5 // indirect go.dtapp.net/gotime v1.0.5 // indirect
@ -74,17 +76,17 @@ require (
go.uber.org/atomic v1.10.0 // indirect go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.8.0 // indirect go.uber.org/multierr v1.8.0 // indirect
go.uber.org/zap v1.23.0 // indirect go.uber.org/zap v1.23.0 // indirect
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 // indirect golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0 // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
golang.org/x/net v0.0.0-20220909164309-bea034e7d591 // indirect golang.org/x/net v0.0.0-20220920152717-4a395b0a80a1 // indirect
golang.org/x/sync v0.0.0-20220907140024-f12130a52804 // indirect golang.org/x/sync v0.0.0-20220907140024-f12130a52804 // indirect
golang.org/x/sys v0.0.0-20220913175220-63ea55921009 // indirect golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 // indirect
golang.org/x/text v0.3.7 // indirect golang.org/x/text v0.3.7 // indirect
google.golang.org/protobuf v1.28.1 // indirect google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect
gorm.io/driver/mysql v1.3.6 // indirect gorm.io/driver/mysql v1.3.6 // indirect
gorm.io/driver/postgres v1.3.9 // indirect gorm.io/driver/postgres v1.3.10 // indirect
gorm.io/gorm v1.23.8 // indirect gorm.io/gorm v1.23.9 // indirect
mellium.im/sasl v0.3.0 // indirect mellium.im/sasl v0.3.0 // indirect
modernc.org/sqlite v1.18.1 // indirect modernc.org/sqlite v1.18.1 // indirect
xorm.io/builder v0.3.12 // indirect xorm.io/builder v0.3.12 // indirect

@ -95,8 +95,8 @@ github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
github.com/go-playground/validator/v10 v10.11.0 h1:0W+xRM511GY47Yy3bZUbJVitCNg2BOGlCyvTqsp/xIw= github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ=
github.com/go-playground/validator/v10 v10.11.0/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
github.com/go-redis/redis/v9 v9.0.0-beta.2 h1:ZSr84TsnQyKMAg8gnV+oawuQezeJR11/09THcWCQzr4= github.com/go-redis/redis/v9 v9.0.0-beta.2 h1:ZSr84TsnQyKMAg8gnV+oawuQezeJR11/09THcWCQzr4=
github.com/go-redis/redis/v9 v9.0.0-beta.2/go.mod h1:Bldcd/M/bm9HbnNPi/LUtYBSD8ttcZYBMupwMXhdU0o= github.com/go-redis/redis/v9 v9.0.0-beta.2/go.mod h1:Bldcd/M/bm9HbnNPi/LUtYBSD8ttcZYBMupwMXhdU0o=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
@ -193,7 +193,6 @@ github.com/jackc/pgconn v1.8.1/go.mod h1:JV6m6b6jhjdmzchES0drzCcYcAHS1OPD5xu3OZ/
github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=
github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
github.com/jackc/pgconn v1.11.0/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= github.com/jackc/pgconn v1.11.0/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
github.com/jackc/pgconn v1.12.1/go.mod h1:ZkhRC59Llhrq3oSfrikvwQ5NaxYExr6twkdkMLaKono=
github.com/jackc/pgconn v1.13.0 h1:3L1XMNV2Zvca/8BYhzcRFS70Lr0WlDg16Di6SFGAbys= github.com/jackc/pgconn v1.13.0 h1:3L1XMNV2Zvca/8BYhzcRFS70Lr0WlDg16Di6SFGAbys=
github.com/jackc/pgconn v1.13.0/go.mod h1:AnowpAqO4CMIIJNZl2VJp+KrkAZciAkhEl0W0JIobpI= github.com/jackc/pgconn v1.13.0/go.mod h1:AnowpAqO4CMIIJNZl2VJp+KrkAZciAkhEl0W0JIobpI=
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
@ -213,7 +212,6 @@ github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwX
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.2.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgproto3/v2 v2.2.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.3.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.3.1 h1:nwj7qwf0S+Q7ISFfBndqeLwSwxs+4DPsbRFjECT1Y4Y= github.com/jackc/pgproto3/v2 v2.3.1 h1:nwj7qwf0S+Q7ISFfBndqeLwSwxs+4DPsbRFjECT1Y4Y=
github.com/jackc/pgproto3/v2 v2.3.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgproto3/v2 v2.3.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
@ -229,7 +227,6 @@ github.com/jackc/pgtype v1.7.0/go.mod h1:ZnHF+rMePVqDKaOfJVI4Q8IVvAQMryDlDkZnKOI
github.com/jackc/pgtype v1.8.0/go.mod h1:PqDKcEBtllAtk/2p6z6SHdXW5UB+MhE75tUol2OKexE= github.com/jackc/pgtype v1.8.0/go.mod h1:PqDKcEBtllAtk/2p6z6SHdXW5UB+MhE75tUol2OKexE=
github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=
github.com/jackc/pgtype v1.10.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= github.com/jackc/pgtype v1.10.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
github.com/jackc/pgtype v1.11.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
github.com/jackc/pgtype v1.12.0 h1:Dlq8Qvcch7kiehm8wPGIW0W3KsCCHJnRacKW0UM8n5w= github.com/jackc/pgtype v1.12.0 h1:Dlq8Qvcch7kiehm8wPGIW0W3KsCCHJnRacKW0UM8n5w=
github.com/jackc/pgtype v1.12.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= github.com/jackc/pgtype v1.12.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
@ -242,7 +239,6 @@ github.com/jackc/pgx/v4 v4.11.0/go.mod h1:i62xJgdrtVDsnL3U8ekyrQXEwGNTRoG7/8r+CI
github.com/jackc/pgx/v4 v4.12.0/go.mod h1:fE547h6VulLPA3kySjfnSG/e2D861g/50JlVUa/ub60= github.com/jackc/pgx/v4 v4.12.0/go.mod h1:fE547h6VulLPA3kySjfnSG/e2D861g/50JlVUa/ub60=
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
github.com/jackc/pgx/v4 v4.15.0/go.mod h1:D/zyOyXiaM1TmVWnOM18p0xdDtdakRBa0RsVGI3U3bw= github.com/jackc/pgx/v4 v4.15.0/go.mod h1:D/zyOyXiaM1TmVWnOM18p0xdDtdakRBa0RsVGI3U3bw=
github.com/jackc/pgx/v4 v4.16.1/go.mod h1:SIhx0D5hoADaiXZVyv+3gSm3LCIIINTVO0PficsvWGQ=
github.com/jackc/pgx/v4 v4.17.2 h1:0Ut0rpeKwvIVbMQ1KbMBU4h6wxehBI535LK6Flheh8E= github.com/jackc/pgx/v4 v4.17.2 h1:0Ut0rpeKwvIVbMQ1KbMBU4h6wxehBI535LK6Flheh8E=
github.com/jackc/pgx/v4 v4.17.2/go.mod h1:lcxIZN44yMIrWI78a5CpucdD14hX0SBDbNRvjDBItsw= github.com/jackc/pgx/v4 v4.17.2/go.mod h1:lcxIZN44yMIrWI78a5CpucdD14hX0SBDbNRvjDBItsw=
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
@ -271,8 +267,8 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:C
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
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/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY= github.com/klauspost/compress v1.15.10 h1:Ai8UzuomSCDw90e1qNMtb15msBXsNpH6gzkkENQNcJo=
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/klauspost/compress v1.15.10/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
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/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
@ -362,6 +358,10 @@ github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxS
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/oschwald/geoip2-golang v1.8.0 h1:KfjYB8ojCEn/QLqsDU0AzrJ3R5Qa9vFlx3z6SLNcKTs=
github.com/oschwald/geoip2-golang v1.8.0/go.mod h1:R7bRvYjOeaoenAp9sKRS8GX5bJWcZ0laWO5+DauEktw=
github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg=
github.com/oschwald/maxminddb-golang v1.10.0/go.mod h1:Y2ELenReaLAZ0b400URyGwvYxHV1dLIxBuyOsyYjHK0=
github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
@ -495,12 +495,12 @@ github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
go.dtapp.net/dorm v1.0.33 h1:QRAVEQ6Uf3WENSOrXytzzH+PjH90JySowd3jbB9PQjw= go.dtapp.net/dorm v1.0.38 h1:9OgWY5bnar6D0Xdho62xn7RluXJNe8i7Kz/IeSUObF4=
go.dtapp.net/dorm v1.0.33/go.mod h1:4WNSzrUGs7YIudq1cRZQNkHOlPAbG6thI3mXX1tQcYY= go.dtapp.net/dorm v1.0.38/go.mod h1:z9ksZ4Y0HHH0odjEiG57d90/ZUBM51qXEWJC8fS+dEM=
go.dtapp.net/goip v1.0.30 h1:/wP2ewSNWLzG2Oh2VsTfQCv/2rw1KKi9XerD4rQaMLM= go.dtapp.net/goip v1.0.36 h1:+wexFCMnP3f+6jPYXjBLMyjnP+DfQrslWvXifndxkdc=
go.dtapp.net/goip v1.0.30/go.mod h1:9l8e/slVanziGXfvrUwOMx6028EV/lzN5vVpixmtUYY= go.dtapp.net/goip v1.0.36/go.mod h1:9/Oo1HVM4EVUsvAebdV6CaBAK4S6qQMQWT3LcJfH6jM=
go.dtapp.net/golog v1.0.73 h1:1j7EU1iIM8b0UTMScxqUHZsgYFjKRy9SG/ArlpdcnfE= go.dtapp.net/golog v1.0.87 h1:mueYl2NEf28rt9z0AgI7SvdLaqc88VdQVnjgQKon0ps=
go.dtapp.net/golog v1.0.73/go.mod h1:I1WfgHWcEikqxjhMdoyH+/VVi/9KmnZy11NnqpsugqY= go.dtapp.net/golog v1.0.87/go.mod h1:SeAB/PPgwh+bVoG2TbDvWJtbPxvKImZCih1Un3SY9fA=
go.dtapp.net/gorandom v1.0.1 h1:IWfMClh1ECPvyUjlqD7MwLq4mZdUusD1qAwAdsvEJBs= go.dtapp.net/gorandom v1.0.1 h1:IWfMClh1ECPvyUjlqD7MwLq4mZdUusD1qAwAdsvEJBs=
go.dtapp.net/gorandom v1.0.1/go.mod h1:ZPdgalKpvFV/ATQqR0k4ns/F/IpITAZpx6WkWirr5Y8= go.dtapp.net/gorandom v1.0.1/go.mod h1:ZPdgalKpvFV/ATQqR0k4ns/F/IpITAZpx6WkWirr5Y8=
go.dtapp.net/gorequest v1.0.31 h1:r/OoU5Y00TbJjkQtpvwjsb/pllqO0UQQjFRY1veZYZc= go.dtapp.net/gorequest v1.0.31 h1:r/OoU5Y00TbJjkQtpvwjsb/pllqO0UQQjFRY1veZYZc=
@ -556,13 +556,12 @@ golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWP
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220307211146-efcb8507fb70/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220307211146-efcb8507fb70/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM= golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0 h1:a5Yg6ylndHHYJqIPrdq0AhvR6KTvDTAvgBtaidhEevY=
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20181106170214-d68db9428509/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20181106170214-d68db9428509/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
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=
@ -594,8 +593,8 @@ golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220909164309-bea034e7d591 h1:D0B/7al0LLrVC8aWF4+oxpv/m8bc7ViFfVS8/gXGdqI= golang.org/x/net v0.0.0-20220920152717-4a395b0a80a1 h1:KPlMURVqlGj7IS5s1RR3RyiiiKAgGMrh3O4A0tpOQOg=
golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20220920152717-4a395b0a80a1/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
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/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
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=
@ -641,8 +640,8 @@ golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220913175220-63ea55921009 h1:PuvuRMeLWqsf/ZdT1UUZz0syhioyv1mzuFZsXs4fvhw= golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 h1:h+EGohizhe9XlX18rfpa8k8RAc5XyaeamM+0VHRd4lc=
golang.org/x/sys v0.0.0-20220913175220-63ea55921009/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -727,11 +726,12 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/mysql v1.3.6 h1:BhX1Y/RyALb+T9bZ3t07wLnPZBukt+IRkMn8UZSNbGM= gorm.io/driver/mysql v1.3.6 h1:BhX1Y/RyALb+T9bZ3t07wLnPZBukt+IRkMn8UZSNbGM=
gorm.io/driver/mysql v1.3.6/go.mod h1:sSIebwZAVPiT+27jK9HIwvsqOGKx3YMPmrA3mBJR10c= gorm.io/driver/mysql v1.3.6/go.mod h1:sSIebwZAVPiT+27jK9HIwvsqOGKx3YMPmrA3mBJR10c=
gorm.io/driver/postgres v1.3.9 h1:lWGiVt5CijhQAg0PWB7Od1RNcBw/jS4d2cAScBcSDXg= gorm.io/driver/postgres v1.3.10 h1:Fsd+pQpFMGlGxxVMUPJhNo8gG8B1lKtk8QQ4/VZZAJw=
gorm.io/driver/postgres v1.3.9/go.mod h1:qw/FeqjxmYqW5dBcYNBsnhQULIApQdk7YuuDPktVi1U= gorm.io/driver/postgres v1.3.10/go.mod h1:whNfh5WhhHs96honoLjBAMwJGYEuA3m1hvgUbNXhPCw=
gorm.io/gorm v1.23.7/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= gorm.io/gorm v1.23.7/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
gorm.io/gorm v1.23.8 h1:h8sGJ+biDgBA1AD1Ha9gFCx7h8npU7AsLdlkX0n2TpE=
gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
gorm.io/gorm v1.23.9 h1:NSHG021i+MCznokeXR3udGaNyFyBQJW8MbjrJMVCfGw=
gorm.io/gorm v1.23.9/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
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=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

@ -28,12 +28,9 @@ func (c *Client) request(ctx context.Context, url string, params map[string]inte
return gorequest.Response{}, err return gorequest.Response{}, err
} }
// 日志 // 记录日志
if c.log.gorm { if c.log.status {
go c.log.logGormClient.GormMiddleware(ctx, request, Version) go c.log.client.Middleware(ctx, request, Version)
}
if c.log.mongo {
go c.log.logMongoClient.MongoMiddleware(ctx, request, Version)
} }
return request, err return request, err

@ -1,7 +1,7 @@
Package validator Package validator
================= =================
<img align="right" src="https://raw.githubusercontent.com/go-playground/validator/v9/logo.png">[![Join the chat at https://gitter.im/go-playground/validator](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/go-playground/validator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) <img align="right" src="https://raw.githubusercontent.com/go-playground/validator/v9/logo.png">[![Join the chat at https://gitter.im/go-playground/validator](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/go-playground/validator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
![Project status](https://img.shields.io/badge/version-10.11.0-green.svg) ![Project status](https://img.shields.io/badge/version-10.11.1-green.svg)
[![Build Status](https://travis-ci.org/go-playground/validator.svg?branch=master)](https://travis-ci.org/go-playground/validator) [![Build Status](https://travis-ci.org/go-playground/validator.svg?branch=master)](https://travis-ci.org/go-playground/validator)
[![Coverage Status](https://coveralls.io/repos/go-playground/validator/badge.svg?branch=master&service=github)](https://coveralls.io/github/go-playground/validator?branch=master) [![Coverage Status](https://coveralls.io/repos/go-playground/validator/badge.svg?branch=master&service=github)](https://coveralls.io/github/go-playground/validator?branch=master)
[![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/validator)](https://goreportcard.com/report/github.com/go-playground/validator) [![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/validator)](https://goreportcard.com/report/github.com/go-playground/validator)

@ -1484,10 +1484,15 @@ func isAlphaUnicode(fl FieldLevel) bool {
return alphaUnicodeRegex.MatchString(fl.Field().String()) return alphaUnicodeRegex.MatchString(fl.Field().String())
} }
// isBoolean is the validation function for validating if the current field's value can be safely converted to a boolean. // isBoolean is the validation function for validating if the current field's value is a valid boolean value or can be safely converted to a boolean value.
func isBoolean(fl FieldLevel) bool { func isBoolean(fl FieldLevel) bool {
_, err := strconv.ParseBool(fl.Field().String()) switch fl.Field().Kind() {
return err == nil case reflect.Bool:
return true
default:
_, err := strconv.ParseBool(fl.Field().String())
return err == nil
}
} }
// isDefault is the opposite of required aka hasValue // isDefault is the opposite of required aka hasValue

@ -17,6 +17,12 @@ This package provides various compression algorithms.
# changelog # changelog
* July 21, 2022 (v1.15.9)
* zstd: Fix decoder crash on amd64 (no BMI) on invalid input https://github.com/klauspost/compress/pull/645
* zstd: Disable decoder extended memory copies (amd64) due to possible crashes https://github.com/klauspost/compress/pull/644
* zstd: Allow single segments up to "max decoded size" by @klauspost in https://github.com/klauspost/compress/pull/643
* July 13, 2022 (v1.15.8) * July 13, 2022 (v1.15.8)
* gzip: fix stack exhaustion bug in Reader.Read https://github.com/klauspost/compress/pull/641 * gzip: fix stack exhaustion bug in Reader.Read https://github.com/klauspost/compress/pull/641

@ -763,17 +763,20 @@ func (d *Decoder) decompress4X8bit(dst, src []byte) ([]byte, error) {
d.bufs.Put(buf) d.bufs.Put(buf)
return nil, errors.New("corruption detected: stream overrun 1") return nil, errors.New("corruption detected: stream overrun 1")
} }
copy(out, buf[0][:])
copy(out[dstEvery:], buf[1][:])
copy(out[dstEvery*2:], buf[2][:])
copy(out[dstEvery*3:], buf[3][:])
out = out[bufoff:]
decoded += bufoff * 4
// There must at least be 3 buffers left. // There must at least be 3 buffers left.
if len(out) < dstEvery*3 { if len(out)-bufoff < dstEvery*3 {
d.bufs.Put(buf) d.bufs.Put(buf)
return nil, errors.New("corruption detected: stream overrun 2") return nil, errors.New("corruption detected: stream overrun 2")
} }
//copy(out, buf[0][:])
//copy(out[dstEvery:], buf[1][:])
//copy(out[dstEvery*2:], buf[2][:])
*(*[bufoff]byte)(out) = buf[0]
*(*[bufoff]byte)(out[dstEvery:]) = buf[1]
*(*[bufoff]byte)(out[dstEvery*2:]) = buf[2]
*(*[bufoff]byte)(out[dstEvery*3:]) = buf[3]
out = out[bufoff:]
decoded += bufoff * 4
} }
} }
if off > 0 { if off > 0 {
@ -997,17 +1000,22 @@ func (d *Decoder) decompress4X8bitExactly(dst, src []byte) ([]byte, error) {
d.bufs.Put(buf) d.bufs.Put(buf)
return nil, errors.New("corruption detected: stream overrun 1") return nil, errors.New("corruption detected: stream overrun 1")
} }
copy(out, buf[0][:])
copy(out[dstEvery:], buf[1][:])
copy(out[dstEvery*2:], buf[2][:])
copy(out[dstEvery*3:], buf[3][:])
out = out[bufoff:]
decoded += bufoff * 4
// There must at least be 3 buffers left. // There must at least be 3 buffers left.
if len(out) < dstEvery*3 { if len(out)-bufoff < dstEvery*3 {
d.bufs.Put(buf) d.bufs.Put(buf)
return nil, errors.New("corruption detected: stream overrun 2") return nil, errors.New("corruption detected: stream overrun 2")
} }
//copy(out, buf[0][:])
//copy(out[dstEvery:], buf[1][:])
//copy(out[dstEvery*2:], buf[2][:])
// copy(out[dstEvery*3:], buf[3][:])
*(*[bufoff]byte)(out) = buf[0]
*(*[bufoff]byte)(out[dstEvery:]) = buf[1]
*(*[bufoff]byte)(out[dstEvery*2:]) = buf[2]
*(*[bufoff]byte)(out[dstEvery*3:]) = buf[3]
out = out[bufoff:]
decoded += bufoff * 4
} }
} }
if off > 0 { if off > 0 {

@ -14,12 +14,14 @@ import (
// decompress4x_main_loop_x86 is an x86 assembler implementation // decompress4x_main_loop_x86 is an x86 assembler implementation
// of Decompress4X when tablelog > 8. // of Decompress4X when tablelog > 8.
//
//go:noescape //go:noescape
func decompress4x_main_loop_amd64(ctx *decompress4xContext) func decompress4x_main_loop_amd64(ctx *decompress4xContext)
// decompress4x_8b_loop_x86 is an x86 assembler implementation // decompress4x_8b_loop_x86 is an x86 assembler implementation
// of Decompress4X when tablelog <= 8 which decodes 4 entries // of Decompress4X when tablelog <= 8 which decodes 4 entries
// per loop. // per loop.
//
//go:noescape //go:noescape
func decompress4x_8b_main_loop_amd64(ctx *decompress4xContext) func decompress4x_8b_main_loop_amd64(ctx *decompress4xContext)
@ -145,11 +147,13 @@ func (d *Decoder) Decompress4X(dst, src []byte) ([]byte, error) {
// decompress4x_main_loop_x86 is an x86 assembler implementation // decompress4x_main_loop_x86 is an x86 assembler implementation
// of Decompress1X when tablelog > 8. // of Decompress1X when tablelog > 8.
//
//go:noescape //go:noescape
func decompress1x_main_loop_amd64(ctx *decompress1xContext) func decompress1x_main_loop_amd64(ctx *decompress1xContext)
// decompress4x_main_loop_x86 is an x86 with BMI2 assembler implementation // decompress4x_main_loop_x86 is an x86 with BMI2 assembler implementation
// of Decompress1X when tablelog > 8. // of Decompress1X when tablelog > 8.
//
//go:noescape //go:noescape
func decompress1x_main_loop_bmi2(ctx *decompress1xContext) func decompress1x_main_loop_bmi2(ctx *decompress1xContext)

@ -1,7 +1,6 @@
// Code generated by command: go run gen.go -out ../decompress_amd64.s -pkg=huff0. DO NOT EDIT. // Code generated by command: go run gen.go -out ../decompress_amd64.s -pkg=huff0. DO NOT EDIT.
//go:build amd64 && !appengine && !noasm && gc //go:build amd64 && !appengine && !noasm && gc
// +build amd64,!appengine,!noasm,gc
// func decompress4x_main_loop_amd64(ctx *decompress4xContext) // func decompress4x_main_loop_amd64(ctx *decompress4xContext)
TEXT ·decompress4x_main_loop_amd64(SB), $0-8 TEXT ·decompress4x_main_loop_amd64(SB), $0-8

@ -122,17 +122,21 @@ func (d *Decoder) Decompress4X(dst, src []byte) ([]byte, error) {
d.bufs.Put(buf) d.bufs.Put(buf)
return nil, errors.New("corruption detected: stream overrun 1") return nil, errors.New("corruption detected: stream overrun 1")
} }
copy(out, buf[0][:])
copy(out[dstEvery:], buf[1][:])
copy(out[dstEvery*2:], buf[2][:])
copy(out[dstEvery*3:], buf[3][:])
out = out[bufoff:]
decoded += bufoff * 4
// There must at least be 3 buffers left. // There must at least be 3 buffers left.
if len(out) < dstEvery*3 { if len(out)-bufoff < dstEvery*3 {
d.bufs.Put(buf) d.bufs.Put(buf)
return nil, errors.New("corruption detected: stream overrun 2") return nil, errors.New("corruption detected: stream overrun 2")
} }
//copy(out, buf[0][:])
//copy(out[dstEvery:], buf[1][:])
//copy(out[dstEvery*2:], buf[2][:])
//copy(out[dstEvery*3:], buf[3][:])
*(*[bufoff]byte)(out) = buf[0]
*(*[bufoff]byte)(out[dstEvery:]) = buf[1]
*(*[bufoff]byte)(out[dstEvery*2:]) = buf[2]
*(*[bufoff]byte)(out[dstEvery*3:]) = buf[3]
out = out[bufoff:]
decoded += bufoff * 4
} }
} }
if off > 0 { if off > 0 {

@ -18,6 +18,7 @@ func load64(b []byte, i int) uint64 {
// emitLiteral writes a literal chunk and returns the number of bytes written. // emitLiteral writes a literal chunk and returns the number of bytes written.
// //
// It assumes that: // It assumes that:
//
// dst is long enough to hold the encoded bytes // dst is long enough to hold the encoded bytes
// 1 <= len(lit) && len(lit) <= 65536 // 1 <= len(lit) && len(lit) <= 65536
func emitLiteral(dst, lit []byte) int { func emitLiteral(dst, lit []byte) int {
@ -42,6 +43,7 @@ func emitLiteral(dst, lit []byte) int {
// emitCopy writes a copy chunk and returns the number of bytes written. // emitCopy writes a copy chunk and returns the number of bytes written.
// //
// It assumes that: // It assumes that:
//
// dst is long enough to hold the encoded bytes // dst is long enough to hold the encoded bytes
// 1 <= offset && offset <= 65535 // 1 <= offset && offset <= 65535
// 4 <= length && length <= 65535 // 4 <= length && length <= 65535
@ -89,6 +91,7 @@ func emitCopy(dst []byte, offset, length int) int {
// src[i:i+k-j] and src[j:k] have the same contents. // src[i:i+k-j] and src[j:k] have the same contents.
// //
// It assumes that: // It assumes that:
//
// 0 <= i && i < j && j <= len(src) // 0 <= i && i < j && j <= len(src)
func extendMatch(src []byte, i, j int) int { func extendMatch(src []byte, i, j int) int {
for ; j < len(src) && src[i] == src[j]; i, j = i+1, j+1 { for ; j < len(src) && src[i] == src[j]; i, j = i+1, j+1 {
@ -105,8 +108,9 @@ func hash(u, shift uint32) uint32 {
// been written. // been written.
// //
// It also assumes that: // It also assumes that:
//
// len(dst) >= MaxEncodedLen(len(src)) && // len(dst) >= MaxEncodedLen(len(src)) &&
// minNonLiteralBlockSize <= len(src) && len(src) <= maxBlockSize // minNonLiteralBlockSize <= len(src) && len(src) <= maxBlockSize
func encodeBlock(dst, src []byte) (d int) { func encodeBlock(dst, src []byte) (d int) {
// Initialize the hash table. Its size ranges from 1<<8 to 1<<14 inclusive. // Initialize the hash table. Its size ranges from 1<<8 to 1<<14 inclusive.
// The table element type is uint16, as s < sLimit and sLimit < len(src) // The table element type is uint16, as s < sLimit and sLimit < len(src)

@ -12,6 +12,8 @@ The `zstd` package is provided as open source software using a Go standard licen
Currently the package is heavily optimized for 64 bit processors and will be significantly slower on 32 bit processors. Currently the package is heavily optimized for 64 bit processors and will be significantly slower on 32 bit processors.
For seekable zstd streams, see [this excellent package](https://github.com/SaveTheRbtz/zstd-seekable-format-go).
## Installation ## Installation
Install using `go get -u github.com/klauspost/compress`. The package is located in `github.com/klauspost/compress/zstd`. Install using `go get -u github.com/klauspost/compress`. The package is located in `github.com/klauspost/compress/zstd`.

@ -10,7 +10,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"sync" "sync"
@ -651,7 +650,7 @@ func (b *blockDec) prepareSequences(in []byte, hist *history) (err error) {
fatalErr(binary.Write(&buf, binary.LittleEndian, hist.decoders.matchLengths.fse)) fatalErr(binary.Write(&buf, binary.LittleEndian, hist.decoders.matchLengths.fse))
fatalErr(binary.Write(&buf, binary.LittleEndian, hist.decoders.offsets.fse)) fatalErr(binary.Write(&buf, binary.LittleEndian, hist.decoders.offsets.fse))
buf.Write(in) buf.Write(in)
ioutil.WriteFile(filepath.Join("testdata", "seqs", fn), buf.Bytes(), os.ModePerm) os.WriteFile(filepath.Join("testdata", "seqs", fn), buf.Bytes(), os.ModePerm)
} }
return nil return nil

@ -7,7 +7,6 @@ package zstd
import ( import (
"fmt" "fmt"
"io" "io"
"io/ioutil"
) )
type byteBuffer interface { type byteBuffer interface {
@ -124,7 +123,7 @@ func (r *readerWrapper) readByte() (byte, error) {
} }
func (r *readerWrapper) skipN(n int64) error { func (r *readerWrapper) skipN(n int64) error {
n2, err := io.CopyN(ioutil.Discard, r.r, n) n2, err := io.CopyN(io.Discard, r.r, n)
if n2 != n { if n2 != n {
err = io.ErrUnexpectedEOF err = io.ErrUnexpectedEOF
} }

@ -312,6 +312,7 @@ func (d *Decoder) DecodeAll(input, dst []byte) ([]byte, error) {
// Grab a block decoder and frame decoder. // Grab a block decoder and frame decoder.
block := <-d.decoders block := <-d.decoders
frame := block.localFrame frame := block.localFrame
initialSize := len(dst)
defer func() { defer func() {
if debugDecoder { if debugDecoder {
printf("re-adding decoder: %p", block) printf("re-adding decoder: %p", block)
@ -354,7 +355,16 @@ func (d *Decoder) DecodeAll(input, dst []byte) ([]byte, error) {
return dst, ErrWindowSizeExceeded return dst, ErrWindowSizeExceeded
} }
if frame.FrameContentSize != fcsUnknown { if frame.FrameContentSize != fcsUnknown {
if frame.FrameContentSize > d.o.maxDecodedSize-uint64(len(dst)) { if frame.FrameContentSize > d.o.maxDecodedSize-uint64(len(dst)-initialSize) {
if debugDecoder {
println("decoder size exceeded; fcs:", frame.FrameContentSize, "> mcs:", d.o.maxDecodedSize-uint64(len(dst)-initialSize), "len:", len(dst))
}
return dst, ErrDecoderSizeExceeded
}
if d.o.limitToCap && frame.FrameContentSize > uint64(cap(dst)-len(dst)) {
if debugDecoder {
println("decoder size exceeded; fcs:", frame.FrameContentSize, "> (cap-len)", cap(dst)-len(dst))
}
return dst, ErrDecoderSizeExceeded return dst, ErrDecoderSizeExceeded
} }
if cap(dst)-len(dst) < int(frame.FrameContentSize) { if cap(dst)-len(dst) < int(frame.FrameContentSize) {
@ -364,7 +374,7 @@ func (d *Decoder) DecodeAll(input, dst []byte) ([]byte, error) {
} }
} }
if cap(dst) == 0 { if cap(dst) == 0 && !d.o.limitToCap {
// Allocate len(input) * 2 by default if nothing is provided // Allocate len(input) * 2 by default if nothing is provided
// and we didn't get frame content size. // and we didn't get frame content size.
size := len(input) * 2 size := len(input) * 2
@ -382,6 +392,9 @@ func (d *Decoder) DecodeAll(input, dst []byte) ([]byte, error) {
if err != nil { if err != nil {
return dst, err return dst, err
} }
if uint64(len(dst)-initialSize) > d.o.maxDecodedSize {
return dst, ErrDecoderSizeExceeded
}
if len(frame.bBuf) == 0 { if len(frame.bBuf) == 0 {
if debugDecoder { if debugDecoder {
println("frame dbuf empty") println("frame dbuf empty")
@ -852,6 +865,10 @@ decodeStream:
} }
} }
if err == nil && d.frame.WindowSize > d.o.maxWindowSize { if err == nil && d.frame.WindowSize > d.o.maxWindowSize {
if debugDecoder {
println("decoder size exceeded, fws:", d.frame.WindowSize, "> mws:", d.o.maxWindowSize)
}
err = ErrDecoderSizeExceeded err = ErrDecoderSizeExceeded
} }
if err != nil { if err != nil {

@ -20,6 +20,7 @@ type decoderOptions struct {
maxWindowSize uint64 maxWindowSize uint64
dicts []dict dicts []dict
ignoreChecksum bool ignoreChecksum bool
limitToCap bool
} }
func (o *decoderOptions) setDefault() { func (o *decoderOptions) setDefault() {
@ -114,6 +115,17 @@ func WithDecoderMaxWindow(size uint64) DOption {
} }
} }
// WithDecodeAllCapLimit will limit DecodeAll to decoding cap(dst)-len(dst) bytes,
// or any size set in WithDecoderMaxMemory.
// This can be used to limit decoding to a specific maximum output size.
// Disabled by default.
func WithDecodeAllCapLimit(b bool) DOption {
return func(o *decoderOptions) error {
o.limitToCap = b
return nil
}
}
// IgnoreChecksum allows to forcibly ignore checksum checking. // IgnoreChecksum allows to forcibly ignore checksum checking.
func IgnoreChecksum(b bool) DOption { func IgnoreChecksum(b bool) DOption {
return func(o *decoderOptions) error { return func(o *decoderOptions) error {

@ -416,15 +416,23 @@ encodeLoop:
// Try to find a better match by searching for a long match at the end of the current best match // Try to find a better match by searching for a long match at the end of the current best match
if s+matched < sLimit { if s+matched < sLimit {
// Allow some bytes at the beginning to mismatch.
// Sweet spot is around 3 bytes, but depends on input.
// The skipped bytes are tested in Extend backwards,
// and still picked up as part of the match if they do.
const skipBeginning = 3
nextHashL := hashLen(load6432(src, s+matched), betterLongTableBits, betterLongLen) nextHashL := hashLen(load6432(src, s+matched), betterLongTableBits, betterLongLen)
cv := load3232(src, s) s2 := s + skipBeginning
cv := load3232(src, s2)
candidateL := e.longTable[nextHashL] candidateL := e.longTable[nextHashL]
coffsetL := candidateL.offset - e.cur - matched coffsetL := candidateL.offset - e.cur - matched + skipBeginning
if coffsetL >= 0 && coffsetL < s && s-coffsetL < e.maxMatchOff && cv == load3232(src, coffsetL) { if coffsetL >= 0 && coffsetL < s2 && s2-coffsetL < e.maxMatchOff && cv == load3232(src, coffsetL) {
// Found a long match, at least 4 bytes. // Found a long match, at least 4 bytes.
matchedNext := e.matchlen(s+4, coffsetL+4, src) + 4 matchedNext := e.matchlen(s2+4, coffsetL+4, src) + 4
if matchedNext > matched { if matchedNext > matched {
t = coffsetL t = coffsetL
s = s2
matched = matchedNext matched = matchedNext
if debugMatches { if debugMatches {
println("long match at end-of-match") println("long match at end-of-match")
@ -434,12 +442,13 @@ encodeLoop:
// Check prev long... // Check prev long...
if true { if true {
coffsetL = candidateL.prev - e.cur - matched coffsetL = candidateL.prev - e.cur - matched + skipBeginning
if coffsetL >= 0 && coffsetL < s && s-coffsetL < e.maxMatchOff && cv == load3232(src, coffsetL) { if coffsetL >= 0 && coffsetL < s2 && s2-coffsetL < e.maxMatchOff && cv == load3232(src, coffsetL) {
// Found a long match, at least 4 bytes. // Found a long match, at least 4 bytes.
matchedNext := e.matchlen(s+4, coffsetL+4, src) + 4 matchedNext := e.matchlen(s2+4, coffsetL+4, src) + 4
if matchedNext > matched { if matchedNext > matched {
t = coffsetL t = coffsetL
s = s2
matched = matchedNext matched = matchedNext
if debugMatches { if debugMatches {
println("prev long match at end-of-match") println("prev long match at end-of-match")

@ -1103,7 +1103,8 @@ func (e *doubleFastEncoderDict) Reset(d *dict, singleBlock bool) {
} }
if allDirty || dirtyShardCnt > dLongTableShardCnt/2 { if allDirty || dirtyShardCnt > dLongTableShardCnt/2 {
copy(e.longTable[:], e.dictLongTable) //copy(e.longTable[:], e.dictLongTable)
e.longTable = *(*[dFastLongTableSize]tableEntry)(e.dictLongTable)
for i := range e.longTableShardDirty { for i := range e.longTableShardDirty {
e.longTableShardDirty[i] = false e.longTableShardDirty[i] = false
} }
@ -1114,7 +1115,9 @@ func (e *doubleFastEncoderDict) Reset(d *dict, singleBlock bool) {
continue continue
} }
copy(e.longTable[i*dLongTableShardSize:(i+1)*dLongTableShardSize], e.dictLongTable[i*dLongTableShardSize:(i+1)*dLongTableShardSize]) // copy(e.longTable[i*dLongTableShardSize:(i+1)*dLongTableShardSize], e.dictLongTable[i*dLongTableShardSize:(i+1)*dLongTableShardSize])
*(*[dLongTableShardSize]tableEntry)(e.longTable[i*dLongTableShardSize:]) = *(*[dLongTableShardSize]tableEntry)(e.dictLongTable[i*dLongTableShardSize:])
e.longTableShardDirty[i] = false e.longTableShardDirty[i] = false
} }
} }

@ -871,7 +871,8 @@ func (e *fastEncoderDict) Reset(d *dict, singleBlock bool) {
const shardCnt = tableShardCnt const shardCnt = tableShardCnt
const shardSize = tableShardSize const shardSize = tableShardSize
if e.allDirty || dirtyShardCnt > shardCnt*4/6 { if e.allDirty || dirtyShardCnt > shardCnt*4/6 {
copy(e.table[:], e.dictTable) //copy(e.table[:], e.dictTable)
e.table = *(*[tableSize]tableEntry)(e.dictTable)
for i := range e.tableShardDirty { for i := range e.tableShardDirty {
e.tableShardDirty[i] = false e.tableShardDirty[i] = false
} }
@ -883,7 +884,8 @@ func (e *fastEncoderDict) Reset(d *dict, singleBlock bool) {
continue continue
} }
copy(e.table[i*shardSize:(i+1)*shardSize], e.dictTable[i*shardSize:(i+1)*shardSize]) //copy(e.table[i*shardSize:(i+1)*shardSize], e.dictTable[i*shardSize:(i+1)*shardSize])
*(*[shardSize]tableEntry)(e.table[i*shardSize:]) = *(*[shardSize]tableEntry)(e.dictTable[i*shardSize:])
e.tableShardDirty[i] = false e.tableShardDirty[i] = false
} }
e.allDirty = false e.allDirty = false

@ -353,12 +353,23 @@ func (d *frameDec) runDecoder(dst []byte, dec *blockDec) ([]byte, error) {
// Store input length, so we only check new data. // Store input length, so we only check new data.
crcStart := len(dst) crcStart := len(dst)
d.history.decoders.maxSyncLen = 0 d.history.decoders.maxSyncLen = 0
if d.o.limitToCap {
d.history.decoders.maxSyncLen = uint64(cap(dst) - len(dst))
}
if d.FrameContentSize != fcsUnknown { if d.FrameContentSize != fcsUnknown {
d.history.decoders.maxSyncLen = d.FrameContentSize + uint64(len(dst)) if !d.o.limitToCap || d.FrameContentSize+uint64(len(dst)) < d.history.decoders.maxSyncLen {
d.history.decoders.maxSyncLen = d.FrameContentSize + uint64(len(dst))
}
if d.history.decoders.maxSyncLen > d.o.maxDecodedSize { if d.history.decoders.maxSyncLen > d.o.maxDecodedSize {
if debugDecoder {
println("maxSyncLen:", d.history.decoders.maxSyncLen, "> maxDecodedSize:", d.o.maxDecodedSize)
}
return dst, ErrDecoderSizeExceeded return dst, ErrDecoderSizeExceeded
} }
if uint64(cap(dst)) < d.history.decoders.maxSyncLen { if debugDecoder {
println("maxSyncLen:", d.history.decoders.maxSyncLen)
}
if !d.o.limitToCap && uint64(cap(dst)-len(dst)) < d.history.decoders.maxSyncLen {
// Alloc for output // Alloc for output
dst2 := make([]byte, len(dst), d.history.decoders.maxSyncLen+compressedBlockOverAlloc) dst2 := make([]byte, len(dst), d.history.decoders.maxSyncLen+compressedBlockOverAlloc)
copy(dst2, dst) copy(dst2, dst)
@ -378,7 +389,13 @@ func (d *frameDec) runDecoder(dst []byte, dec *blockDec) ([]byte, error) {
if err != nil { if err != nil {
break break
} }
if uint64(len(d.history.b)) > d.o.maxDecodedSize { if uint64(len(d.history.b)-crcStart) > d.o.maxDecodedSize {
println("runDecoder: maxDecodedSize exceeded", uint64(len(d.history.b)-crcStart), ">", d.o.maxDecodedSize)
err = ErrDecoderSizeExceeded
break
}
if d.o.limitToCap && len(d.history.b) > cap(dst) {
println("runDecoder: cap exceeded", uint64(len(d.history.b)), ">", cap(dst))
err = ErrDecoderSizeExceeded err = ErrDecoderSizeExceeded
break break
} }

@ -1,7 +1,6 @@
// Code generated by command: go run gen_fse.go -out ../fse_decoder_amd64.s -pkg=zstd. DO NOT EDIT. // Code generated by command: go run gen_fse.go -out ../fse_decoder_amd64.s -pkg=zstd. DO NOT EDIT.
//go:build !appengine && !noasm && gc && !noasm //go:build !appengine && !noasm && gc && !noasm
// +build !appengine,!noasm,gc,!noasm
// func buildDtable_asm(s *fseDecoder, ctx *buildDtableAsmContext) int // func buildDtable_asm(s *fseDecoder, ctx *buildDtableAsmContext) int
TEXT ·buildDtable_asm(SB), $0-24 TEXT ·buildDtable_asm(SB), $0-24

@ -32,18 +32,22 @@ type decodeSyncAsmContext struct {
// sequenceDecs_decodeSync_amd64 implements the main loop of sequenceDecs.decodeSync in x86 asm. // sequenceDecs_decodeSync_amd64 implements the main loop of sequenceDecs.decodeSync in x86 asm.
// //
// Please refer to seqdec_generic.go for the reference implementation. // Please refer to seqdec_generic.go for the reference implementation.
//
//go:noescape //go:noescape
func sequenceDecs_decodeSync_amd64(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int func sequenceDecs_decodeSync_amd64(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int
// sequenceDecs_decodeSync_bmi2 implements the main loop of sequenceDecs.decodeSync in x86 asm with BMI2 extensions. // sequenceDecs_decodeSync_bmi2 implements the main loop of sequenceDecs.decodeSync in x86 asm with BMI2 extensions.
//
//go:noescape //go:noescape
func sequenceDecs_decodeSync_bmi2(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int func sequenceDecs_decodeSync_bmi2(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int
// sequenceDecs_decodeSync_safe_amd64 does the same as above, but does not write more than output buffer. // sequenceDecs_decodeSync_safe_amd64 does the same as above, but does not write more than output buffer.
//
//go:noescape //go:noescape
func sequenceDecs_decodeSync_safe_amd64(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int func sequenceDecs_decodeSync_safe_amd64(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int
// sequenceDecs_decodeSync_safe_bmi2 does the same as above, but does not write more than output buffer. // sequenceDecs_decodeSync_safe_bmi2 does the same as above, but does not write more than output buffer.
//
//go:noescape //go:noescape
func sequenceDecs_decodeSync_safe_bmi2(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int func sequenceDecs_decodeSync_safe_bmi2(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int
@ -201,20 +205,24 @@ const errorNotEnoughSpace = 5
// sequenceDecs_decode implements the main loop of sequenceDecs in x86 asm. // sequenceDecs_decode implements the main loop of sequenceDecs in x86 asm.
// //
// Please refer to seqdec_generic.go for the reference implementation. // Please refer to seqdec_generic.go for the reference implementation.
//
//go:noescape //go:noescape
func sequenceDecs_decode_amd64(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int func sequenceDecs_decode_amd64(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int
// sequenceDecs_decode implements the main loop of sequenceDecs in x86 asm. // sequenceDecs_decode implements the main loop of sequenceDecs in x86 asm.
// //
// Please refer to seqdec_generic.go for the reference implementation. // Please refer to seqdec_generic.go for the reference implementation.
//
//go:noescape //go:noescape
func sequenceDecs_decode_56_amd64(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int func sequenceDecs_decode_56_amd64(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int
// sequenceDecs_decode implements the main loop of sequenceDecs in x86 asm with BMI2 extensions. // sequenceDecs_decode implements the main loop of sequenceDecs in x86 asm with BMI2 extensions.
//
//go:noescape //go:noescape
func sequenceDecs_decode_bmi2(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int func sequenceDecs_decode_bmi2(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int
// sequenceDecs_decode implements the main loop of sequenceDecs in x86 asm with BMI2 extensions. // sequenceDecs_decode implements the main loop of sequenceDecs in x86 asm with BMI2 extensions.
//
//go:noescape //go:noescape
func sequenceDecs_decode_56_bmi2(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int func sequenceDecs_decode_56_bmi2(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int
@ -308,10 +316,12 @@ type executeAsmContext struct {
// Returns false if a match offset is too big. // Returns false if a match offset is too big.
// //
// Please refer to seqdec_generic.go for the reference implementation. // Please refer to seqdec_generic.go for the reference implementation.
//
//go:noescape //go:noescape
func sequenceDecs_executeSimple_amd64(ctx *executeAsmContext) bool func sequenceDecs_executeSimple_amd64(ctx *executeAsmContext) bool
// Same as above, but with safe memcopies // Same as above, but with safe memcopies
//
//go:noescape //go:noescape
func sequenceDecs_executeSimple_safe_amd64(ctx *executeAsmContext) bool func sequenceDecs_executeSimple_safe_amd64(ctx *executeAsmContext) bool

@ -1,7 +1,6 @@
// Code generated by command: go run gen.go -out ../seqdec_amd64.s -pkg=zstd. DO NOT EDIT. // Code generated by command: go run gen.go -out ../seqdec_amd64.s -pkg=zstd. DO NOT EDIT.
//go:build !appengine && !noasm && gc && !noasm //go:build !appengine && !noasm && gc && !noasm
// +build !appengine,!noasm,gc,!noasm
// func sequenceDecs_decode_amd64(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int // func sequenceDecs_decode_amd64(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int
// Requires: CMOV // Requires: CMOV

@ -1,3 +1,3 @@
package dorm package dorm
const Version = "1.0.33" const Version = "1.0.38"

7
vendor/go.dtapp.net/dorm/gorm.go generated vendored

@ -9,6 +9,13 @@ import (
"time" "time"
) )
// GormClientFun *GormClient 驱动
type GormClientFun func() *GormClient
// GormClientTableFun *GormClient 驱动
// string 表名
type GormClientTableFun func() (*GormClient, string)
type ConfigGormClient struct { type ConfigGormClient struct {
Dns string // 地址 Dns string // 地址
LogStatus bool // 日志 - 状态 LogStatus bool // 日志 - 状态

@ -160,7 +160,7 @@ func NewGormPostgresqlClient(config *ConfigGormClient) (*GormClient, error) {
// 设置了连接可复用的最大时间 // 设置了连接可复用的最大时间
if c.config.ConnSetConnMaxLifetime == 0 { if c.config.ConnSetConnMaxLifetime == 0 {
sqlDB.SetConnMaxLifetime(time.Second * 600) sqlDB.SetConnMaxLifetime(time.Hour)
} else { } else {
sqlDB.SetConnMaxLifetime(time.Duration(c.config.ConnSetConnMaxLifetime)) sqlDB.SetConnMaxLifetime(time.Duration(c.config.ConnSetConnMaxLifetime))
} }

35
vendor/go.dtapp.net/dorm/mongo.go generated vendored

@ -4,11 +4,19 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options" "go.mongodb.org/mongo-driver/mongo/options"
) )
// MongoClientFun *MongoClient 驱动
// string 库名
type MongoClientFun func() (*MongoClient, string)
// MongoClientCollectionFun *MongoClient 驱动
// string 库名
// string 集合
type MongoClientCollectionFun func() (*MongoClient, string, string)
type ConfigMongoClient struct { type ConfigMongoClient struct {
Dns string // 地址 Dns string // 地址
Opts *options.ClientOptions Opts *options.ClientOptions
@ -16,36 +24,31 @@ type ConfigMongoClient struct {
} }
type MongoClient struct { type MongoClient struct {
Db *mongo.Client // 驱动 Db *mongo.Client // 驱动
config *ConfigMongoClient // 配置 config *ConfigMongoClient // 配置
databaseName string // 库名
collectionName string // 表名
//filterArr []queryFilter // 查询条件数组
filter bson.D // 查询条件
} }
func NewMongoClient(config *ConfigMongoClient) (*MongoClient, error) { func NewMongoClient(config *ConfigMongoClient) (*MongoClient, error) {
var ctx = context.Background()
var err error var err error
c := &MongoClient{config: config} c := &MongoClient{config: config}
c.databaseName = c.config.DatabaseName
// 连接到MongoDB // 连接到MongoDB
if c.config.Dns != "" { if c.config.Dns != "" {
c.Db, err = mongo.Connect(context.Background(), options.Client().ApplyURI(c.config.Dns)) c.Db, err = mongo.Connect(ctx, options.Client().ApplyURI(c.config.Dns))
if err != nil { if err != nil {
return nil, errors.New(fmt.Sprintf("连接失败:%v", err)) return nil, errors.New(fmt.Sprintf("连接失败:%v", err))
} }
} else { } else {
c.Db, err = mongo.Connect(context.Background(), c.config.Opts) c.Db, err = mongo.Connect(ctx, c.config.Opts)
if err != nil { if err != nil {
return nil, errors.New(fmt.Sprintf("连接失败:%v", err)) return nil, errors.New(fmt.Sprintf("连接失败:%v", err))
} }
} }
// 检查连接 // 检查连接
err = c.Db.Ping(context.TODO(), nil) err = c.Db.Ping(ctx, nil)
if err != nil { if err != nil {
return nil, errors.New(fmt.Sprintf("检查连接失败:%v", err)) return nil, errors.New(fmt.Sprintf("检查连接失败:%v", err))
} }
@ -54,10 +57,6 @@ func NewMongoClient(config *ConfigMongoClient) (*MongoClient, error) {
} }
// Close 关闭 // Close 关闭
func (c *MongoClient) Close() error { func (c *MongoClient) Close(ctx context.Context) error {
err := c.Db.Disconnect(context.TODO()) return c.Db.Disconnect(ctx)
if err != nil {
return errors.New(fmt.Sprintf("关闭失败:%v", err))
}
return nil
} }

@ -1 +0,0 @@
package dorm

@ -1,185 +0,0 @@
package dorm
import (
"context"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
// InsertOne 插入一个文档
func (c *MongoClient) InsertOne(document interface{}) (result *mongo.InsertOneResult, err error) {
collection := c.Db.Database(c.getDatabaseName()).Collection(c.collectionName)
result, err = collection.InsertOne(context.TODO(), document)
return
}
// InsertMany 插入多个文档
func (c *MongoClient) InsertMany(documents []interface{}) (result *mongo.InsertManyResult, err error) {
collection := c.Db.Database(c.getDatabaseName()).Collection(c.collectionName)
result, err = collection.InsertMany(context.TODO(), documents)
return
}
// Delete 删除文档
func (c *MongoClient) Delete(filter interface{}) (err error) {
collection := c.Db.Database(c.getDatabaseName()).Collection(c.collectionName)
_, err = collection.DeleteOne(context.TODO(), filter)
return
}
// DeleteId 删除文档
func (c *MongoClient) DeleteId(id interface{}) (err error) {
collection := c.Db.Database(c.getDatabaseName()).Collection(c.collectionName)
_, err = collection.DeleteOne(context.TODO(), bson.M{"_id": id})
return
}
// DeleteMany 删除多个文档
func (c *MongoClient) DeleteMany(filter interface{}) (result *mongo.DeleteResult, err error) {
collection := c.Db.Database(c.getDatabaseName()).Collection(c.collectionName)
result, err = collection.DeleteMany(context.TODO(), filter)
return
}
// UpdateOne 更新单个文档
// 修改字段的值($set)
// 字段增加值 inc($inc)
// 从数组中增加一个元素 push($push)
// 从数组中删除一个元素 pull($pull)
func (c *MongoClient) UpdateOne(filter interface{}, update interface{}) (err error) {
collection := c.Db.Database(c.getDatabaseName()).Collection(c.collectionName)
_, err = collection.UpdateOne(context.TODO(), filter, update)
return
}
// UpdateId 更新单个文档
// 修改字段的值($set)
// 字段增加值 inc($inc)
// 从数组中增加一个元素 push($push)
// 从数组中删除一个元素 pull($pull)
func (c *MongoClient) UpdateId(id interface{}, update interface{}) (err error) {
collection := c.Db.Database(c.getDatabaseName()).Collection(c.collectionName)
_, err = collection.UpdateOne(context.TODO(), bson.M{"_id": id}, update)
return
}
// UpdateMany 更新多个文档
// 修改字段的值($set)
// 字段增加值 inc($inc)
// 从数组中增加一个元素 push($push)
// 从数组中删除一个元素 pull($pull)
func (c *MongoClient) UpdateMany(filter interface{}, update interface{}) (result *mongo.UpdateResult, err error) {
collection := c.Db.Database(c.getDatabaseName()).Collection(c.collectionName)
result, err = collection.UpdateMany(context.TODO(), filter, update)
return
}
type FindResultI interface {
Many(result interface{}) error
}
// Find 查询
func (c *MongoClient) Find(filter interface{}) (*mongo.Cursor, error) {
collection := c.Db.Database(c.getDatabaseName()).Collection(c.collectionName)
return collection.Find(context.TODO(), filter)
}
type FindOneResultI interface {
One(result interface{}) error
}
// FindOne 查询单个文档
func (c *MongoClient) FindOne(filter interface{}) *mongo.SingleResult {
collection := c.Db.Database(c.getDatabaseName()).Collection(c.collectionName)
return collection.FindOne(context.TODO(), filter)
}
type FindManyResultI interface {
Many(result interface{}) error
}
// FindMany 查询多个文档
func (c *MongoClient) FindMany(filter interface{}) (*mongo.Cursor, error) {
collection := c.Db.Database(c.getDatabaseName()).Collection(c.collectionName)
return collection.Find(context.TODO(), filter)
}
// FindManyByFilters 多条件查询
func (c *MongoClient) FindManyByFilters(filter interface{}) (result *mongo.Cursor, err error) {
collection, err := c.Db.Database(c.getDatabaseName()).Collection(c.collectionName).Clone()
result, err = collection.Find(context.TODO(), bson.M{"$and": filter})
return result, err
}
// FindManyByFiltersSort 多条件查询支持排序
func (c *MongoClient) FindManyByFiltersSort(filter interface{}, Sort interface{}) (result *mongo.Cursor, err error) {
collection, err := c.Db.Database(c.getDatabaseName()).Collection(c.collectionName).Clone()
findOptions := options.Find()
findOptions.SetSort(Sort)
result, err = collection.Find(context.TODO(), filter, findOptions)
return result, err
}
// FindCollection 查询集合文档
func (c *MongoClient) FindCollection(Limit int64) (result *mongo.Cursor, err error) {
collection := c.Db.Database(c.getDatabaseName()).Collection(c.collectionName)
findOptions := options.Find()
findOptions.SetLimit(Limit)
result, err = collection.Find(context.TODO(), bson.D{{}}, findOptions)
return result, err
}
// FindCollectionSort 查询集合文档支持排序
func (c *MongoClient) FindCollectionSort(Sort interface{}, Limit int64) (result *mongo.Cursor, err error) {
collection := c.Db.Database(c.getDatabaseName()).Collection(c.collectionName)
findOptions := options.Find()
findOptions.SetSort(Sort)
findOptions.SetLimit(Limit)
result, err = collection.Find(context.TODO(), bson.D{{}}, findOptions)
return result, err
}
// FindManyCollectionSort 查询集合文档支持排序支持条件
func (c *MongoClient) FindManyCollectionSort(filter interface{}, Sort interface{}) (result *mongo.Cursor, err error) {
collection := c.Db.Database(c.getDatabaseName()).Collection(c.collectionName)
findOptions := options.Find()
findOptions.SetSort(Sort)
result, err = collection.Find(context.TODO(), filter, findOptions)
return result, err
}
// CollectionCount 查询集合里有多少数据
func (c *MongoClient) CollectionCount() (name string, size int64) {
collection := c.Db.Database(c.getDatabaseName()).Collection(c.collectionName)
name = collection.Name()
size, _ = collection.EstimatedDocumentCount(context.TODO())
return name, size
}
// CollectionDocuments 按选项查询集合
// Skip 跳过
// Limit 读取数量
// sort 1 -1 . 1 为升序 -1 为降序
func (c *MongoClient) CollectionDocuments(Skip, Limit int64, sort int, key string, value interface{}) (result *mongo.Cursor, err error) {
collection := c.Db.Database(c.getDatabaseName()).Collection(c.collectionName)
SORT := bson.D{{"_id", sort}}
filter := bson.D{{key, value}}
findOptions := options.Find().SetSort(SORT).SetLimit(Limit).SetSkip(Skip)
result, err = collection.Find(context.TODO(), filter, findOptions)
return result, err
}
// AggregateByFiltersSort 统计分析
func (c *MongoClient) AggregateByFiltersSort(pipeline interface{}) (result *mongo.Cursor, err error) {
collection := c.Db.Database(c.getDatabaseName()).Collection(c.collectionName)
result, err = collection.Aggregate(context.TODO(), pipeline)
return result, err
}
// CountDocumentsByFilters 统计数量
func (c *MongoClient) CountDocumentsByFilters(filter interface{}) (count int64, err error) {
collection := c.Db.Database(c.getDatabaseName()).Collection(c.collectionName)
count, err = collection.CountDocuments(context.TODO(), filter)
return count, err
}

@ -1,15 +0,0 @@
package dorm
import (
"context"
"go.mongodb.org/mongo-driver/mongo"
)
type FindResult struct {
cursor *mongo.Cursor
err error
}
func (f *FindResult) Many(result interface{}) error {
return f.cursor.All(context.TODO(), result)
}

@ -1,15 +0,0 @@
package dorm
import (
"context"
"go.mongodb.org/mongo-driver/mongo"
)
type FindManyResult struct {
cursor *mongo.Cursor
err error
}
func (f *FindManyResult) Many(result interface{}) error {
return f.cursor.All(context.TODO(), result)
}

@ -1,11 +0,0 @@
package dorm
import "go.mongodb.org/mongo-driver/mongo"
type FindOneResult struct {
singleResult *mongo.SingleResult
}
func (f *FindOneResult) One(result interface{}) error {
return f.singleResult.Decode(result)
}

@ -6,13 +6,3 @@ import "go.mongodb.org/mongo-driver/mongo"
func (c *MongoClient) GetDb() *mongo.Client { func (c *MongoClient) GetDb() *mongo.Client {
return c.Db return c.Db
} }
// 获取库名
func (c *MongoClient) getDatabaseName() string {
return c.databaseName
}
// 获取表名
func (c *MongoClient) getCollectionName() string {
return c.collectionName
}

@ -1 +0,0 @@
package dorm

@ -1,28 +0,0 @@
package dorm
import "reflect"
// Database 设置库名
func (c *MongoClient) Database(databaseName string) *MongoClient {
c.databaseName = databaseName
return c
}
// Collection 设置表名
func (c *MongoClient) Collection(collectionName string) *MongoClient {
c.collectionName = collectionName
return c
}
// Model 传入模型自动获取库名和表名
func (c *MongoClient) Model(value interface{}) *MongoClient {
// https://studygolang.com/articles/896
val := reflect.ValueOf(value)
if methodValue := val.MethodByName("Database"); methodValue.IsValid() {
c.databaseName = methodValue.Call(nil)[0].String()
}
if methodValue := val.MethodByName("TableName"); methodValue.IsValid() {
c.collectionName = methodValue.Call(nil)[0].String()
}
return c
}

@ -7,44 +7,79 @@ import (
"time" "time"
) )
// BsonTime 类型 // BsonTime 时间类型
type BsonTime time.Time type BsonTime time.Time
// Value 时间类型
func (t BsonTime) Value() string {
return gotime.SetCurrent(time.Time(t)).Bson()
}
// MarshalJSON 实现json序列化 // MarshalJSON 实现json序列化
func (t BsonTime) MarshalJSON() ([]byte, error) { func (bt BsonTime) MarshalJSON() ([]byte, error) {
//log.Println("MarshalJSON")
b := make([]byte, 0) b := make([]byte, 0)
b = append(b, gotime.SetCurrent(time.Time(t)).Bson()...)
b = append(b, gotime.SetCurrent(time.Time(bt)).Bson()...)
return b, nil return b, nil
} }
// UnmarshalJSON 实现json反序列化 // UnmarshalJSON 实现json反序列化
func (t *BsonTime) UnmarshalJSON(data []byte) (err error) { func (bt *BsonTime) UnmarshalJSON(data []byte) (err error) {
//log.Println("UnmarshalJSON")
t1 := gotime.SetCurrentParse(string(data)) if string(data) == "null" {
*t = BsonTime(t1.Time) return nil
return }
bsonTime := gotime.SetCurrentParse(string(data))
*bt = BsonTime(bsonTime.Time)
return nil
}
func (bt BsonTime) Time() time.Time {
return gotime.SetCurrent(time.Time(bt)).Time
}
func (bt BsonTime) Format() string {
return gotime.SetCurrent(time.Time(bt)).Format()
}
func (bt BsonTime) TimePro() gotime.Pro {
return gotime.SetCurrent(time.Time(bt))
}
// NewBsonTimeCurrent 创建当前时间
func NewBsonTimeCurrent() BsonTime {
return BsonTime(gotime.Current().Time)
}
// NewBsonTimeFromTime 创建某个时间
func NewBsonTimeFromTime(t time.Time) BsonTime {
return BsonTime(t)
}
// NewBsonTimeFromString 创建某个时间 字符串
func NewBsonTimeFromString(t string) BsonTime {
return BsonTime(gotime.SetCurrentParse(t).Time)
}
// Value 时间类型
func (bt BsonTime) Value() string {
return gotime.SetCurrent(time.Time(bt)).Bson()
} }
// MarshalBSONValue 实现bson序列化 // MarshalBSONValue 实现bson序列化
func (t BsonTime) MarshalBSONValue() (bsontype.Type, []byte, error) { func (bt BsonTime) MarshalBSONValue() (bsontype.Type, []byte, error) {
//log.Println("MarshalBSONValue") //log.Println("MarshalBSONValue")
targetTime := gotime.SetCurrent(time.Time(t)).Bson() targetTime := gotime.SetCurrent(time.Time(bt)).Bson()
return bson.MarshalValue(targetTime) return bson.MarshalValue(targetTime)
} }
// UnmarshalBSONValue 实现bson反序列化 // UnmarshalBSONValue 实现bson反序列化
func (t *BsonTime) UnmarshalBSONValue(t2 bsontype.Type, data []byte) error { func (bt *BsonTime) UnmarshalBSONValue(t2 bsontype.Type, data []byte) error {
//log.Println("UnmarshalBSONValue") //log.Println("UnmarshalBSONValue")
t1 := gotime.SetCurrentParse(string(data)) t1 := gotime.SetCurrentParse(string(data))
//if string(data) == "" { //if string(data) == "" {
// return errors.New(fmt.Sprintf("%s, %s, %s", "读取数据失败:", t2, data)) // return errors.New(fmt.Sprintf("%s, %s, %s", "读取数据失败:", t2, data))
//} //}
*t = BsonTime(t1.Time) *bt = BsonTime(t1.Time)
return nil return nil
} }

@ -1,48 +0,0 @@
package dorm
import (
"context"
"go.mongodb.org/mongo-driver/mongo"
)
type MongoTransaction struct {
startSession mongo.Session
Session mongo.SessionContext
db *mongo.Client // 驱动
databaseName string // 库名
collectionName string // 表名
}
// Begin 开始事务,会同时创建开始会话需要在退出时关闭会话
func (c *MongoClient) Begin() (ms MongoTransaction, err error) {
ms.db = c.Db
// 开始会话
ms.startSession, err = ms.db.StartSession()
if err != nil {
panic(err)
}
// 会话上下文
ms.Session = mongo.NewSessionContext(context.Background(), ms.startSession)
// 会话开启事务
err = ms.startSession.StartTransaction()
return ms, err
}
// Close 关闭会话
func (ms *MongoTransaction) Close() {
ms.startSession.EndSession(context.TODO())
}
// Rollback 回滚事务
func (ms *MongoTransaction) Rollback() error {
return ms.startSession.AbortTransaction(context.Background())
}
// Commit 提交事务
func (ms *MongoTransaction) Commit() error {
return ms.startSession.CommitTransaction(context.Background())
}

@ -1,174 +0,0 @@
package dorm
import (
"context"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
// InsertOne 插入一个文档
func (ms *MongoTransaction) InsertOne(document interface{}) (result *mongo.InsertOneResult, err error) {
collection := ms.db.Database(ms.getDatabaseName()).Collection(ms.collectionName)
result, err = collection.InsertOne(ms.Session, document)
return
}
// InsertMany 插入多个文档
func (ms *MongoTransaction) InsertMany(documents []interface{}) (result *mongo.InsertManyResult, err error) {
collection := ms.db.Database(ms.getDatabaseName()).Collection(ms.collectionName)
result, err = collection.InsertMany(ms.Session, documents)
return
}
// Delete 删除文档
func (ms *MongoTransaction) Delete(filter interface{}) (err error) {
collection := ms.db.Database(ms.getDatabaseName()).Collection(ms.collectionName)
_, err = collection.DeleteOne(ms.Session, filter)
return
}
// DeleteId 删除文档
func (ms *MongoTransaction) DeleteId(id interface{}) (err error) {
collection := ms.db.Database(ms.getDatabaseName()).Collection(ms.collectionName)
_, err = collection.DeleteOne(ms.Session, bson.M{"_id": id})
return
}
// DeleteMany 删除多个文档
func (ms *MongoTransaction) DeleteMany(key string, value interface{}) (result *mongo.DeleteResult, err error) {
collection := ms.db.Database(ms.getDatabaseName()).Collection(ms.collectionName)
filter := bson.D{{key, value}}
result, err = collection.DeleteMany(ms.Session, filter)
return
}
// UpdateOne 更新单个文档
// 修改字段的值($set)
// 字段增加值 inc($inc)
// 从数组中增加一个元素 push($push)
// 从数组中删除一个元素 pull($pull)
func (ms *MongoTransaction) UpdateOne(filter interface{}, update interface{}) (err error) {
collection := ms.db.Database(ms.getDatabaseName()).Collection(ms.collectionName)
_, err = collection.UpdateOne(ms.Session, filter, update)
return
}
// UpdateId 更新单个文档
// 修改字段的值($set)
// 字段增加值 inc($inc)
// 从数组中增加一个元素 push($push)
// 从数组中删除一个元素 pull($pull)
func (ms *MongoTransaction) UpdateId(id interface{}, update interface{}) (err error) {
collection := ms.db.Database(ms.getDatabaseName()).Collection(ms.collectionName)
_, err = collection.UpdateOne(context.TODO(), bson.M{"_id": id}, update)
return
}
// UpdateMany 更新多个文档
// 修改字段的值($set)
// 字段增加值 inc($inc)
// 从数组中增加一个元素 push($push)
// 从数组中删除一个元素 pull($pull)
func (ms *MongoTransaction) UpdateMany(filter interface{}, update interface{}) (result *mongo.UpdateResult, err error) {
collection := ms.db.Database(ms.getDatabaseName()).Collection(ms.collectionName)
result, err = collection.UpdateMany(ms.Session, filter, update)
return
}
// Find 查询
func (ms *MongoTransaction) Find(filter interface{}) (*mongo.Cursor, error) {
collection := ms.db.Database(ms.getDatabaseName()).Collection(ms.collectionName)
return collection.Find(ms.Session, filter)
}
// FindOne 查询单个文档
func (ms *MongoTransaction) FindOne(filter interface{}) *mongo.SingleResult {
collection := ms.db.Database(ms.getDatabaseName()).Collection(ms.collectionName)
return collection.FindOne(ms.Session, filter)
}
// FindMany 查询多个文档
func (ms *MongoTransaction) FindMany(filter interface{}) (*mongo.Cursor, error) {
collection := ms.db.Database(ms.getDatabaseName()).Collection(ms.collectionName)
return collection.Find(ms.Session, filter)
}
// FindManyByFilters 多条件查询
func (ms *MongoTransaction) FindManyByFilters(filter interface{}) (result *mongo.Cursor, err error) {
collection, err := ms.db.Database(ms.getDatabaseName()).Collection(ms.collectionName).Clone()
result, err = collection.Find(ms.Session, bson.M{"$and": filter})
return result, err
}
// FindManyByFiltersSort 多条件查询支持排序
func (ms *MongoTransaction) FindManyByFiltersSort(filter interface{}, Sort interface{}) (result *mongo.Cursor, err error) {
collection, err := ms.db.Database(ms.getDatabaseName()).Collection(ms.collectionName).Clone()
findOptions := options.Find()
findOptions.SetSort(Sort)
result, err = collection.Find(ms.Session, filter, findOptions)
return result, err
}
// FindCollection 查询集合文档
func (ms *MongoTransaction) FindCollection(Limit int64) (result *mongo.Cursor, err error) {
collection := ms.db.Database(ms.getDatabaseName()).Collection(ms.collectionName)
findOptions := options.Find()
findOptions.SetLimit(Limit)
result, err = collection.Find(ms.Session, bson.D{{}}, findOptions)
return result, err
}
// FindCollectionSort 查询集合文档支持排序
func (ms *MongoTransaction) FindCollectionSort(Sort interface{}, Limit int64) (result *mongo.Cursor, err error) {
collection := ms.db.Database(ms.getDatabaseName()).Collection(ms.collectionName)
findOptions := options.Find()
findOptions.SetSort(Sort)
findOptions.SetLimit(Limit)
result, err = collection.Find(ms.Session, bson.D{{}}, findOptions)
return result, err
}
// FindManyCollectionSort 查询集合文档支持排序支持条件
func (ms *MongoTransaction) FindManyCollectionSort(filter interface{}, Sort interface{}) (result *mongo.Cursor, err error) {
collection := ms.db.Database(ms.getDatabaseName()).Collection(ms.collectionName)
findOptions := options.Find()
findOptions.SetSort(Sort)
result, err = collection.Find(ms.Session, filter, findOptions)
return result, err
}
// CollectionCount 查询集合里有多少数据
func (ms *MongoTransaction) CollectionCount(ctx context.Context) (name string, size int64) {
collection := ms.db.Database(ms.getDatabaseName()).Collection(ms.collectionName)
name = collection.Name()
size, _ = collection.EstimatedDocumentCount(ctx)
return name, size
}
// CollectionDocuments 按选项查询集合
// Skip 跳过
// Limit 读取数量
// sort 1 -1 . 1 为升序 -1 为降序
func (ms *MongoTransaction) CollectionDocuments(Skip, Limit int64, sort int, key string, value interface{}) (result *mongo.Cursor, err error) {
collection := ms.db.Database(ms.getDatabaseName()).Collection(ms.collectionName)
SORT := bson.D{{"_id", sort}}
filter := bson.D{{key, value}}
findOptions := options.Find().SetSort(SORT).SetLimit(Limit).SetSkip(Skip)
result, err = collection.Find(ms.Session, filter, findOptions)
return result, err
}
// AggregateByFiltersSort 统计分析
func (ms *MongoTransaction) AggregateByFiltersSort(pipeline interface{}) (result *mongo.Cursor, err error) {
collection := ms.db.Database(ms.getDatabaseName()).Collection(ms.collectionName)
result, err = collection.Aggregate(ms.Session, pipeline)
return result, err
}
// CountDocumentsByFilters 统计数量
func (ms *MongoTransaction) CountDocumentsByFilters(filter interface{}) (count int64, err error) {
collection := ms.db.Database(ms.getDatabaseName()).Collection(ms.collectionName)
count, err = collection.CountDocuments(ms.Session, filter)
return count, err
}

@ -1,11 +0,0 @@
package dorm
// 获取库名
func (ms *MongoTransaction) getDatabaseName() string {
return ms.databaseName
}
// 获取表名
func (ms *MongoTransaction) getCollectionName() string {
return ms.collectionName
}

@ -1,28 +0,0 @@
package dorm
import "reflect"
// Database 设置库名
func (ms *MongoTransaction) Database(databaseName string) *MongoTransaction {
ms.databaseName = databaseName
return ms
}
// Collection 设置表名
func (ms *MongoTransaction) Collection(collectionName string) *MongoTransaction {
ms.collectionName = collectionName
return ms
}
// Model 传入模型自动获取库名和表名
func (ms *MongoTransaction) Model(value interface{}) *MongoTransaction {
// https://studygolang.com/articles/896
val := reflect.ValueOf(value)
if methodValue := val.MethodByName("Database"); methodValue.IsValid() {
ms.databaseName = methodValue.Call(nil)[0].String()
}
if methodValue := val.MethodByName("TableName"); methodValue.IsValid() {
ms.collectionName = methodValue.Call(nil)[0].String()
}
return ms
}

@ -8,6 +8,9 @@ import (
"time" "time"
) )
// RedisClientFun *RedisClient 驱动
type RedisClientFun func() *RedisClient
type ConfigRedisClient struct { type ConfigRedisClient struct {
Addr string // 地址 Addr string // 地址
Password string // 密码 Password string // 密码

@ -6,3 +6,6 @@
*.log *.log
*_test.go *_test.go
gomod.sh gomod.sh
*.zip
*.tar.gz
/vendor/

@ -1,53 +1,53 @@
package goip package goip
import ( import (
"net"
"strconv" "strconv"
) )
var (
ipv4 = "IPV4"
ipv6 = "IPV6"
)
type AnalyseResult struct { type AnalyseResult struct {
IP string `json:"ip,omitempty"` // 输入的ip地址 Ip string `json:"ip"` // ip
Country string `json:"country,omitempty"` // 国家或地区 Continent string `json:"continent"` // 大陆
Province string `json:"province,omitempty"` // 省份 Country string `json:"country"` // 国家
City string `json:"city,omitempty"` // 城市 Province string `json:"province"` // 省份
Area string `json:"area,omitempty"` // 区域 City string `json:"city"` // 城市
Isp string `json:"isp,omitempty"` // 运营商 Isp string `json:"isp"` // 运营商
LocationTimeZone string `json:"location_time_zone"` // 位置时区
LocationLatitude float64 `json:"location_latitude"` // 位置纬度
LocationLongitude float64 `json:"location_longitude"` // 位置经度
} }
func (c *Client) Analyse(item string) AnalyseResult { func (c *Client) Analyse(item string) AnalyseResult {
isIp := c.isIpv4OrIpv6(item) isIp := c.isIpv4OrIpv6(item)
ipByte := net.ParseIP(item)
switch isIp { switch isIp {
case ipv4: case ipv4:
info := c.V4db.Find(item) ip2regionV2Info, _ := c.QueryIp2RegionV2(ipByte)
search, err := c.V4Region.MemorySearch(item) geoIpInfo, _ := c.QueryGeoIp(ipByte)
if err != nil { return AnalyseResult{
return AnalyseResult{ Ip: ipByte.String(),
IP: info.IP, Continent: geoIpInfo.Continent.Name,
Country: info.Country, Country: geoIpInfo.Country.Name,
Area: info.Area, Province: ip2regionV2Info.Province,
} City: ip2regionV2Info.City,
} else { Isp: ip2regionV2Info.Operator,
return AnalyseResult{ LocationTimeZone: geoIpInfo.Location.TimeZone,
IP: search.IP, LocationLatitude: geoIpInfo.Location.Latitude,
Country: search.Country, LocationLongitude: geoIpInfo.Location.Longitude,
Province: search.Province,
City: search.City,
Isp: info.Area,
}
} }
case ipv6: case ipv6:
info := c.V6db.Find(item) geoIpInfo, _ := c.QueryGeoIp(ipByte)
ipv6Info, _ := c.QueryIpv6wry(ipByte)
return AnalyseResult{ return AnalyseResult{
IP: info.IP, Ip: ipByte.String(),
Country: info.Country, Continent: geoIpInfo.Continent.Name,
Province: info.Province, Country: geoIpInfo.Country.Name,
City: info.City, Province: ipv6Info.Province,
Area: info.Area, City: ipv6Info.City,
Isp: info.Isp, Isp: ipv6Info.Isp,
LocationTimeZone: geoIpInfo.Location.TimeZone,
LocationLatitude: geoIpInfo.Location.Latitude,
LocationLongitude: geoIpInfo.Location.Longitude,
} }
default: default:
return AnalyseResult{} return AnalyseResult{}

@ -1,3 +1,3 @@
package goip package goip
const Version = "1.0.30" const Version = "1.0.36"

63
vendor/go.dtapp.net/goip/goip.go generated vendored

@ -1,63 +0,0 @@
package goip
import (
"go.dtapp.net/goip/ip2region"
v4 "go.dtapp.net/goip/v4"
v6 "go.dtapp.net/goip/v6"
"log"
"strings"
)
type Client struct {
V4Region ip2region.Ip2Region // IPV4
V4db v4.Pointer // IPV4
V6db v6.Pointer // IPV6
}
// NewIp 实例化
func NewIp() *Client {
app := &Client{}
v4Num := app.V4db.InitIPV4Data()
log.Printf("IPV4 库加载完成 共加载:%d 条 IP 记录\n", v4Num)
v6Num := app.V6db.InitIPV4Data()
log.Printf("IPV6 库加载完成 共加载:%d 条 IP 记录\n", v6Num)
return app
}
func (c *Client) Ipv4(ip string) (res v4.Result, resInfo ip2region.IpInfo) {
res = c.V4db.Find(ip)
resInfo, _ = c.V4Region.MemorySearch(ip)
return res, resInfo
}
func (c *Client) Ipv6(ip string) (res v6.Result) {
res = c.V6db.Find(ip)
return res
}
func (c *Client) isIpv4OrIpv6(ip string) string {
if len(ip) < 7 {
return ""
}
arrIpv4 := strings.Split(ip, ".")
if len(arrIpv4) == 4 {
//. 判断IPv4
for _, val := range arrIpv4 {
if !c.CheckIpv4(val) {
return ""
}
}
return ipv4
}
arrIpv6 := strings.Split(ip, ":")
if len(arrIpv6) == 8 {
// 判断Ipv6
for _, val := range arrIpv6 {
if !c.CheckIpv6(val) {
return "Neither"
}
}
return ipv6
}
return ""
}

43
vendor/go.dtapp.net/goip/ip.go generated vendored

@ -4,11 +4,13 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"go.dtapp.net/gorequest" "go.dtapp.net/gorequest"
"log"
"net" "net"
) )
// GetInsideIp 内网ip // GetInsideIp 内网ip
func GetInsideIp(ctx context.Context) string { func GetInsideIp(ctx context.Context) string {
conn, err := net.Dial("udp", "8.8.8.8:80") conn, err := net.Dial("udp", "8.8.8.8:80")
if err != nil { if err != nil {
panic(err) panic(err)
@ -47,30 +49,35 @@ func Ips(ctx context.Context) (map[string]string, error) {
return ips, nil return ips, nil
} }
var respGetOutsideIp struct {
Data struct {
Ip string `json:"ip"`
} `json:"data"`
}
// GetOutsideIp 外网ip // GetOutsideIp 外网ip
func GetOutsideIp(ctx context.Context) (ip string) { func GetOutsideIp(ctx context.Context) string {
ip = "0.0.0.0"
get := gorequest.NewHttp() // 返回结果
get.SetUri("https://api.dtapp.net/ip") type respGetOutsideIp struct {
response, err := get.Get(ctx) Data struct {
Ip string `json:"ip,omitempty"`
} `json:"data"`
}
// 请求
getHttp := gorequest.NewHttp()
getHttp.SetUri("https://api.dtapp.net/ip")
response, err := getHttp.Get(ctx)
if err != nil { if err != nil {
return log.Printf("[GetOutsideIp]getHttp.Get%s\n", err)
return "0.0.0.0"
} }
err = json.Unmarshal(response.ResponseBody, &respGetOutsideIp) // 解析
var responseJson respGetOutsideIp
err = json.Unmarshal(response.ResponseBody, &responseJson)
if err != nil { if err != nil {
return log.Printf("[GetOutsideIp]json.Unmarshal%s\n", err)
return "0.0.0.0"
} }
if respGetOutsideIp.Data.Ip == "" { if responseJson.Data.Ip == "" {
return responseJson.Data.Ip = "0.0.0.0"
} }
ip = respGetOutsideIp.Data.Ip return responseJson.Data.Ip
return respGetOutsideIp.Data.Ip
} }
// GetMacAddr 获取Mac地址 // GetMacAddr 获取Mac地址

@ -2,16 +2,22 @@ package ip2region
import ( import (
"io/ioutil" "io/ioutil"
"log"
"net/http" "net/http"
) )
func getOnline() ([]byte, error) { func OnlineDownload() {
resp, err := http.Get("https://ghproxy.com/?q=https://github.com/lionsoul2014/ip2region/blob/master/data/ip2region.db?raw=true") resp, err := http.Get("https://ghproxy.com/?q=https://github.com/lionsoul2014/ip2region/blob/master/v1.0/data/ip2region.db?raw=true")
if err != nil { if err != nil {
return nil, err panic(err)
} }
defer resp.Body.Close() defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body) body, err := ioutil.ReadAll(resp.Body)
return body, err
err = ioutil.WriteFile("./ip2region.db", body, 0644)
if err != nil {
panic(err)
}
log.Printf("已下载最新 ip2region 数据库 %s ", "./ip2region.db")
} }

Binary file not shown.

@ -1,183 +0,0 @@
package ip2region
import (
_ "embed"
"errors"
"go.dtapp.net/gostring"
"io/ioutil"
"log"
"net"
"os"
"strconv"
"strings"
)
const (
IndexBlockLength = 12
)
type Ip2Region struct {
// db file handler
dbFileHandler *os.File
//header block info
headerSip []int64
headerPtr []int64
headerLen int64
// super block index info
firstIndexPtr int64
lastIndexPtr int64
totalBlocks int64
// for memory mode only
// the original db binary string
dbFile string
}
//go:embed ip.db
var dbBinStr []byte
type IpInfo struct {
IP string `json:"ip,omitempty"` // 输入的ip地址
CityID int64 `json:"city_id,omitempty"` // 城市ID
Country string `json:"country,omitempty"` // 国家
Region string `json:"region,omitempty"` // 区域
Province string `json:"province,omitempty"` // 省份
City string `json:"city,omitempty"` // 城市
ISP string `json:"isp,omitempty"` // 运营商
}
func (ip IpInfo) String() string {
return ip.IP + "|" + strconv.FormatInt(ip.CityID, 10) + "|" + ip.Country + "|" + ip.Region + "|" + ip.Province + "|" + ip.City + "|" + ip.ISP
}
// 获取Ip信息
func getIpInfo(ipStr string, cityId int64, line []byte) (ipInfo IpInfo) {
lineSlice := strings.Split(string(line), "|")
length := len(lineSlice)
ipInfo.CityID = cityId
if length < 5 {
for i := 0; i <= 5-length; i++ {
lineSlice = append(lineSlice, "")
}
}
if lineSlice[0] != "0" {
ipInfo.Country = gostring.SpaceAndLineBreak(lineSlice[0])
}
if lineSlice[1] != "0" {
ipInfo.Region = gostring.SpaceAndLineBreak(lineSlice[1])
}
if lineSlice[2] != "0" {
ipInfo.Province = gostring.SpaceAndLineBreak(lineSlice[2])
}
if lineSlice[3] != "0" {
ipInfo.City = gostring.SpaceAndLineBreak(lineSlice[3])
}
if lineSlice[4] != "0" {
ipInfo.ISP = gostring.SpaceAndLineBreak(lineSlice[4])
}
ipInfo.IP = ipStr
return ipInfo
}
// MemorySearch memory算法整个数据库全部载入内存单次查询都在0.1x毫秒内
func (r *Ip2Region) MemorySearch(ipStr string) (ipInfo IpInfo, err error) {
ipInfo.IP = ipStr
if net.ParseIP(ipStr).To4() == nil {
if net.ParseIP(ipStr).To16() == nil {
return ipInfo, err
}
}
if r.totalBlocks == 0 {
if err != nil {
return ipInfo, err
}
r.firstIndexPtr = getLong(dbBinStr, 0)
r.lastIndexPtr = getLong(dbBinStr, 4)
r.totalBlocks = (r.lastIndexPtr-r.firstIndexPtr)/IndexBlockLength + 1
}
ip, err := ip2long(ipStr)
if err != nil {
return ipInfo, err
}
h := r.totalBlocks
var dataPtr, l int64
for l <= h {
m := (l + h) >> 1
p := r.firstIndexPtr + m*IndexBlockLength
sip := getLong(dbBinStr, p)
if ip < sip {
h = m - 1
} else {
eip := getLong(dbBinStr, p+4)
if ip > eip {
l = m + 1
} else {
dataPtr = getLong(dbBinStr, p+8)
break
}
}
}
if dataPtr == 0 {
return ipInfo, errors.New("not found")
}
dataLen := (dataPtr >> 24) & 0xFF
dataPtr = dataPtr & 0x00FFFFFF
ipInfo = getIpInfo(ipStr, getLong(dbBinStr, dataPtr), dbBinStr[(dataPtr)+4:dataPtr+dataLen])
return ipInfo, nil
}
func getLong(b []byte, offset int64) int64 {
val := int64(b[offset]) |
int64(b[offset+1])<<8 |
int64(b[offset+2])<<16 |
int64(b[offset+3])<<24
return val
}
func ip2long(IpStr string) (int64, error) {
bits := strings.Split(IpStr, ".")
if len(bits) != 4 {
return 0, errors.New("ip format error")
}
var sum int64
for i, n := range bits {
bit, _ := strconv.ParseInt(n, 10, 64)
sum += bit << uint(24-8*i)
}
return sum, nil
}
func (r *Ip2Region) OnlineDownload() (err error) {
tmpData, err := getOnline()
if err != nil {
return errors.New("下载失败 %s" + err.Error())
}
if err := ioutil.WriteFile("./ip2region.db", tmpData, 0644); err == nil {
log.Printf("已下载最新 ip2region 数据库 %s ", "./ip2region.db")
} else {
return errors.New("保存失败")
}
return nil
}

@ -1,58 +0,0 @@
package v4
import (
"bytes"
"compress/zlib"
"encoding/binary"
"io/ioutil"
"net/http"
)
// 解密key
// https://zhangzifan.com/update-qqwry-dat.html
func getKey() (uint32, error) {
resp, err := http.Get("https://update.cz88.net/ip/copywrite.rar")
if err != nil {
return 0, err
}
defer resp.Body.Close()
if body, err := ioutil.ReadAll(resp.Body); err != nil {
return 0, err
} else {
// @see https://stackoverflow.com/questions/34078427/how-to-read-packed-binary-data-in-go
return binary.LittleEndian.Uint32(body[5*4:]), nil
}
}
// 在线获取内容
func getOnline() ([]byte, error) {
resp, err := http.Get("https://update.cz88.net/ip/qqwry.rar")
if err != nil {
return nil, err
}
defer resp.Body.Close()
if body, err := ioutil.ReadAll(resp.Body); err != nil {
return nil, err
} else {
if key, err := getKey(); err != nil {
return nil, err
} else {
for i := 0; i < 0x200; i++ {
key = key * 0x805
key++
key = key & 0xff
body[i] = byte(uint32(body[i]) ^ key)
}
reader, err := zlib.NewReader(bytes.NewReader(body))
if err != nil {
return nil, err
}
return ioutil.ReadAll(reader)
}
}
}

BIN
vendor/go.dtapp.net/goip/v4/ip.dat generated vendored

Binary file not shown.

201
vendor/go.dtapp.net/goip/v4/ipv4.go generated vendored

@ -1,201 +0,0 @@
package v4
import (
_ "embed"
"encoding/binary"
"errors"
"go.dtapp.net/gostring"
"golang.org/x/text/encoding/simplifiedchinese"
"io/ioutil"
"log"
"net"
)
var (
header []byte
country []byte
area []byte
offset uint32
start uint32
end uint32
)
//go:embed ip.dat
var dat []byte
type Pointer struct {
Offset uint32
ItemLen uint32
IndexLen uint32
}
// Result 返回
type Result struct {
IP string `json:"ip,omitempty"` // 输入的ip地址
Country string `json:"country,omitempty"` // 国家或地区
Area string `json:"area,omitempty"` // 区域
}
// InitIPV4Data 加载
func (q *Pointer) InitIPV4Data() int64 {
buf := dat[0:8]
start := binary.LittleEndian.Uint32(buf[:4])
end := binary.LittleEndian.Uint32(buf[4:])
return int64((end-start)/7 + 1)
}
// ReadData 从文件中读取数据
func (q *Pointer) readData(length uint32) (rs []byte) {
end := q.Offset + length
dataNum := uint32(len(dat))
if q.Offset > dataNum {
return nil
}
if end > dataNum {
end = dataNum
}
rs = dat[q.Offset:end]
q.Offset = end
return rs
}
// Find ip地址查询对应归属地信息
func (q *Pointer) Find(ipStr string) (res Result) {
// 赋值
res.IP = ipStr
if net.ParseIP(ipStr).To4() == nil {
// 不是ip地址
return res
}
q.Offset = 0
// 偏移
offset = q.searchIndex(binary.BigEndian.Uint32(net.ParseIP(ipStr).To4()))
if offset <= 0 {
return
}
q.Offset = offset + q.ItemLen
country, area = q.getAddr()
enc := simplifiedchinese.GBK.NewDecoder()
res.Country, _ = enc.String(string(country))
res.Country = gostring.SpaceAndLineBreak(res.Country)
res.Area, _ = enc.String(string(area))
// Delete CZ88.NET (防止不相关的信息产生干扰)
if res.Area == " CZ88.NET" || res.Area == "" {
res.Area = ""
} else {
res.Area = " " + res.Area
}
res.Area = gostring.SpaceAndLineBreak(res.Area)
return
}
// 获取地址信息
func (q *Pointer) getAddr() ([]byte, []byte) {
mode := q.readData(1)[0]
if mode == 0x01 {
// [IP][0x01][国家和地区信息的绝对偏移地址]
q.Offset = byteToUInt32(q.readData(3))
return q.getAddr()
}
// [IP][0x02][信息的绝对偏移][...] or [IP][国家][...]
_offset := q.Offset - 1
c1 := q.readArea(_offset)
if mode == 0x02 {
q.Offset = 4 + _offset
} else {
q.Offset = _offset + uint32(1+len(c1))
}
c2 := q.readArea(q.Offset)
return c1, c2
}
// 读取区
func (q *Pointer) readArea(offset uint32) []byte {
q.Offset = offset
mode := q.readData(1)[0]
if mode == 0x01 || mode == 0x02 {
return q.readArea(byteToUInt32(q.readData(3)))
}
q.Offset = offset
return q.readString()
}
// 读取字符串
func (q *Pointer) readString() []byte {
data := make([]byte, 0)
for {
buf := q.readData(1)
if buf[0] == 0 {
break
}
data = append(data, buf[0])
}
return data
}
// 搜索索引
func (q *Pointer) searchIndex(ip uint32) uint32 {
q.ItemLen = 4
q.IndexLen = 7
header = dat[0:8]
start = binary.LittleEndian.Uint32(header[:4])
end = binary.LittleEndian.Uint32(header[4:])
buf := make([]byte, q.IndexLen)
for {
mid := start + q.IndexLen*(((end-start)/q.IndexLen)>>1)
buf = dat[mid : mid+q.IndexLen]
_ip := binary.LittleEndian.Uint32(buf[:q.ItemLen])
if end-start == q.IndexLen {
if ip >= binary.LittleEndian.Uint32(dat[end:end+q.ItemLen]) {
buf = dat[end : end+q.IndexLen]
}
return byteToUInt32(buf[q.ItemLen:])
}
if _ip > ip {
end = mid
} else if _ip < ip {
start = mid
} else if _ip == ip {
return byteToUInt32(buf[q.ItemLen:])
}
}
}
// 字节转UInt32
func byteToUInt32(data []byte) uint32 {
i := uint32(data[0]) & 0xff
i |= (uint32(data[1]) << 8) & 0xff00
i |= (uint32(data[2]) << 16) & 0xff0000
return i
}
// OnlineDownload 在线下载
func (q *Pointer) OnlineDownload() (err error) {
tmpData, err := getOnline()
if err != nil {
return errors.New("下载失败")
}
if err := ioutil.WriteFile("./qqwry.dat", tmpData, 0644); err == nil {
log.Printf("已下载最新 纯真 IPv4数据库 %s ", "./qqwry.dat")
} else {
return errors.New("保存失败")
}
return nil
}

@ -1,81 +0,0 @@
package v6
import (
"github.com/saracen/go7z"
"io"
"io/ioutil"
"log"
"net/http"
"os"
)
func getOnline() (data []byte, err error) {
resp, err := http.Get("https://ip.zxinc.org/ip.7z")
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
file7z, err := ioutil.TempFile("", "*")
if err != nil {
return nil, err
}
defer os.Remove(file7z.Name())
if err := ioutil.WriteFile(file7z.Name(), body, 0644); err == nil {
return Un7z(file7z.Name())
}
return
}
func Un7z(filePath string) (data []byte, err error) {
sz, err := go7z.OpenReader(filePath)
if err != nil {
return nil, err
}
defer sz.Close()
fileNoNeed, err := ioutil.TempFile("", "*")
if err != nil {
return nil, err
}
fileNeed, err := ioutil.TempFile("", "*")
if err != nil {
return nil, err
}
if err != nil {
return nil, err
}
for {
hdr, err := sz.Next()
if err == io.EOF {
break // End of archive
}
if err != nil {
return nil, err
}
if hdr.Name == "ipv6wry.db" {
if _, err := io.Copy(fileNeed, sz); err != nil {
log.Fatalln("ZX ipv6数据库解压出错", err.Error())
}
} else {
if _, err := io.Copy(fileNoNeed, sz); err != nil {
log.Fatalln("ZX ipv6数据库解压出错", err.Error())
}
}
}
err = fileNoNeed.Close()
if err != nil {
return nil, err
}
defer os.Remove(fileNoNeed.Name())
defer os.Remove(fileNeed.Name())
return ioutil.ReadFile(fileNeed.Name())
}

BIN
vendor/go.dtapp.net/goip/v6/ip.db generated vendored

Binary file not shown.

230
vendor/go.dtapp.net/goip/v6/ipv6.go generated vendored

@ -1,230 +0,0 @@
package v6
import (
_ "embed"
"encoding/binary"
"errors"
"go.dtapp.net/gostring"
"io/ioutil"
"log"
"math/big"
"net"
"strings"
)
var (
header []byte
country []byte
area []byte
v6ip uint64
offset uint32
start uint32
end uint32
)
type Result struct {
IP string `json:"ip,omitempty"` // 输入的ip地址
Country string `json:"country,omitempty"` // 国家
Province string `json:"province,omitempty"` // 省份
City string `json:"city,omitempty"` // 城市
Area string `json:"area,omitempty"` // 区域
Isp string `json:"isp,omitempty"` // 运营商
}
//go:embed ip.db
var dat []byte
type Pointer struct {
Offset uint32
ItemLen uint32
IndexLen uint32
}
// InitIPV4Data 加载
func (q *Pointer) InitIPV4Data() int64 {
buf := dat[0:8]
start := binary.LittleEndian.Uint32(buf[:4])
end := binary.LittleEndian.Uint32(buf[4:])
return int64((end-start)/7 + 1)
}
// ReadData 从文件中读取数据
func (q *Pointer) readData(length uint32) (rs []byte) {
end := q.Offset + length
dataNum := uint32(len(dat))
if q.Offset > dataNum {
return nil
}
if end > dataNum {
end = dataNum
}
rs = dat[q.Offset:end]
q.Offset = end
return rs
}
// Find ip地址查询对应归属地信息
func (q *Pointer) Find(ipStr string) (res Result) {
res = Result{}
res.IP = ipStr
if net.ParseIP(ipStr).To16() == nil {
return Result{}
}
q.Offset = 0
tp := big.NewInt(0)
op := big.NewInt(0)
tp.SetBytes(net.ParseIP(ipStr).To16())
op.SetString("18446744073709551616", 10)
op.Div(tp, op)
tp.SetString("FFFFFFFFFFFFFFFF", 16)
op.And(op, tp)
v6ip = op.Uint64()
offset = q.searchIndex(v6ip)
q.Offset = offset
country, area = q.getAddr()
// 解析地区数据
info := strings.Split(string(country), "\t")
if len(info) > 0 {
i := 1
for {
if i > len(info) {
break
}
switch i {
case 1:
res.Country = info[i-1]
res.Country = gostring.SpaceAndLineBreak(res.Country)
case 2:
res.Province = info[i-1]
res.Province = gostring.SpaceAndLineBreak(res.Province)
case 3:
res.City = info[i-1]
res.City = gostring.SpaceAndLineBreak(res.City)
case 4:
res.Area = info[i-1]
res.Area = gostring.SpaceAndLineBreak(res.Area)
}
i++ // 自增
}
} else {
res.Country = string(country)
res.Country = gostring.SpaceAndLineBreak(res.Country)
}
// 运营商
res.Isp = string(area)
// Delete ZX (防止不相关的信息产生干扰)
if res.Isp == "ZX" || res.Isp == "" {
res.Isp = ""
} else {
res.Isp = " " + res.Isp
}
res.Isp = gostring.SpaceAndLineBreak(res.Isp)
return
}
func (q *Pointer) getAddr() ([]byte, []byte) {
mode := q.readData(1)[0]
if mode == 0x01 {
// [IP][0x01][国家和地区信息的绝对偏移地址]
q.Offset = byteToUInt32(q.readData(3))
return q.getAddr()
}
// [IP][0x02][信息的绝对偏移][...] or [IP][国家][...]
_offset := q.Offset - 1
c1 := q.readArea(_offset)
if mode == 0x02 {
q.Offset = 4 + _offset
} else {
q.Offset = _offset + uint32(1+len(c1))
}
c2 := q.readArea(q.Offset)
return c1, c2
}
func (q *Pointer) readArea(offset uint32) []byte {
q.Offset = offset
mode := q.readData(1)[0]
if mode == 0x01 || mode == 0x02 {
return q.readArea(byteToUInt32(q.readData(3)))
}
q.Offset = offset
return q.readString()
}
func (q *Pointer) readString() []byte {
data := make([]byte, 0)
for {
buf := q.readData(1)
if buf[0] == 0 {
break
}
data = append(data, buf[0])
}
return data
}
func (q *Pointer) searchIndex(ip uint64) uint32 {
q.ItemLen = 8
q.IndexLen = 11
header = dat[8:24]
start = binary.LittleEndian.Uint32(header[8:])
counts := binary.LittleEndian.Uint32(header[:8])
end = start + counts*q.IndexLen
buf := make([]byte, q.IndexLen)
for {
mid := start + q.IndexLen*(((end-start)/q.IndexLen)>>1)
buf = dat[mid : mid+q.IndexLen]
_ip := binary.LittleEndian.Uint64(buf[:q.ItemLen])
if end-start == q.IndexLen {
if ip >= binary.LittleEndian.Uint64(dat[end:end+q.ItemLen]) {
buf = dat[end : end+q.IndexLen]
}
return byteToUInt32(buf[q.ItemLen:])
}
if _ip > ip {
end = mid
} else if _ip < ip {
start = mid
} else if _ip == ip {
return byteToUInt32(buf[q.ItemLen:])
}
}
}
func byteToUInt32(data []byte) uint32 {
i := uint32(data[0]) & 0xff
i |= (uint32(data[1]) << 8) & 0xff00
i |= (uint32(data[2]) << 16) & 0xff0000
return i
}
// OnlineDownload 在线下载
func (q *Pointer) OnlineDownload() (err error) {
tmpData, err := getOnline()
if err != nil {
return errors.New("下载失败")
}
if err := ioutil.WriteFile("./ipv6wry.db", tmpData, 0644); err == nil {
log.Printf("已下载最新 ZX IPv6数据库 %s ", "./ipv6wry.db")
} else {
return errors.New("保存失败")
}
return nil
}

@ -6,3 +6,4 @@
*.log *.log
gomod.sh gomod.sh
*_test.go *_test.go
/vendor/

125
vendor/go.dtapp.net/golog/api.go generated vendored

@ -6,62 +6,47 @@ import (
"go.dtapp.net/dorm" "go.dtapp.net/dorm"
"go.dtapp.net/goip" "go.dtapp.net/goip"
"go.dtapp.net/gorequest" "go.dtapp.net/gorequest"
"os"
"runtime"
) )
// ApiClientFun *ApiClient 驱动
type ApiClientFun func() *ApiClient
// ApiClient 接口 // ApiClient 接口
type ApiClient struct { type ApiClient struct {
gormClient *dorm.GormClient // 数据库驱动 gormClient *dorm.GormClient // 数据库驱动
mongoClient *dorm.MongoClient // 数据库驱动 mongoClient *dorm.MongoClient // 数据库驱动
zapLog *ZapLog // 日志服务 zapLog *ZapLog // 日志服务
logDebug bool // 日志开关 logDebug bool // 日志开关
currentIp string // 当前ip config struct {
gormConfig struct { systemHostName string // 主机名
systemInsideIp string // 内网ip
systemOs string // 系统类型
systemArch string // 系统架构
goVersion string // go版本
sdkVersion string // sdk版本
systemOutsideIp string // 外网ip
}
gormConfig struct {
stats bool // 状态
tableName string // 表名 tableName string // 表名
insideIp string // 内网ip
hostname string // 主机名
goVersion string // go版本
} }
mongoConfig struct { mongoConfig struct {
stats bool // 状态
databaseName string // 库名 databaseName string // 库名
collectionName string // 表名 collectionName string // 表名
insideIp string // 内网ip
hostname string // 主机名
goVersion string // go版本
}
log struct {
gorm bool // 日志开关
mongo bool // 日志开关
}
config struct {
os string // 系统类型
arch string // 系统架构
maxProCs int // CPU核数
} }
} }
// client 数据库服务
// string 表名
type apiGormClientFun func() (*dorm.GormClient, string)
// client 数据库服务
// string 库名
// string 表名
type apiMongoClientFun func() (*dorm.MongoClient, string, string)
// ApiClientConfig 接口实例配置 // ApiClientConfig 接口实例配置
type ApiClientConfig struct { type ApiClientConfig struct {
GormClientFun apiGormClientFun // 日志配置 GormClientFun dorm.GormClientTableFun // 日志配置
MongoClientFun apiMongoClientFun // 日志配置 MongoClientFun dorm.MongoClientCollectionFun // 日志配置
Debug bool // 日志开关 Debug bool // 日志开关
ZapLog *ZapLog // 日志服务 ZapLog *ZapLog // 日志服务
CurrentIp string // 当前ip CurrentIp string // 当前ip
} }
// NewApiClient 创建接口实例化 // NewApiClient 创建接口实例化
// client 数据库服务
// tableName 表名
func NewApiClient(config *ApiClientConfig) (*ApiClient, error) { func NewApiClient(config *ApiClientConfig) (*ApiClient, error) {
var ctx = context.Background() var ctx = context.Background()
@ -76,63 +61,56 @@ func NewApiClient(config *ApiClientConfig) (*ApiClient, error) {
config.CurrentIp = goip.GetOutsideIp(ctx) config.CurrentIp = goip.GetOutsideIp(ctx)
} }
if config.CurrentIp != "" && config.CurrentIp != "0.0.0.0" { if config.CurrentIp != "" && config.CurrentIp != "0.0.0.0" {
c.currentIp = config.CurrentIp c.config.systemOutsideIp = config.CurrentIp
}
if c.config.systemOutsideIp == "" {
return nil, currentIpNoConfig
} }
c.config.os = runtime.GOOS // 配置信息
c.config.arch = runtime.GOARCH c.setConfig(ctx)
c.config.maxProCs = runtime.GOMAXPROCS(0)
gormClient, gormTableName := config.GormClientFun() gormClient, gormTableName := config.GormClientFun()
mongoClient, mongoDatabaseName, mongoCollectionName := config.MongoClientFun() mongoClient, mongoDatabaseName, mongoCollectionName := config.MongoClientFun()
if (gormClient == nil || gormClient.Db == nil) || (mongoClient == nil || mongoClient.Db == nil) { if (gormClient == nil || gormClient.Db == nil) || (mongoClient == nil || mongoClient.Db == nil) {
return nil, errors.New("没有设置驱动") return nil, dbClientFunNoConfig
} }
hostname, _ := os.Hostname() // 配置关系数据库
if gormClient != nil || gormClient.Db != nil { if gormClient != nil || gormClient.Db != nil {
c.gormClient = gormClient c.gormClient = gormClient
if gormTableName == "" { if gormTableName == "" {
return nil, errors.New("没有设置表名") return nil, errors.New("没有设置表名")
} } else {
c.gormConfig.tableName = gormTableName c.gormConfig.tableName = gormTableName
err := c.gormAutoMigrate()
if err != nil {
return nil, errors.New("创建表失败:" + err.Error())
} }
c.gormConfig.hostname = hostname // 创建模型
c.gormConfig.insideIp = goip.GetInsideIp(ctx) c.gormAutoMigrate(ctx)
c.gormConfig.goVersion = runtime.Version()
c.log.gorm = true
c.gormConfig.stats = true
} }
// 配置非关系数据库
if mongoClient != nil || mongoClient.Db != nil { if mongoClient != nil || mongoClient.Db != nil {
c.mongoClient = mongoClient c.mongoClient = mongoClient
if mongoDatabaseName == "" { if mongoDatabaseName == "" {
return nil, errors.New("没有设置库名") return nil, errors.New("没有设置库名")
} else {
c.mongoConfig.databaseName = mongoDatabaseName
} }
c.mongoConfig.databaseName = mongoDatabaseName
if mongoCollectionName == "" { if mongoCollectionName == "" {
return nil, errors.New("没有设置表名") return nil, errors.New("没有设置表名")
} else {
c.mongoConfig.collectionName = mongoCollectionName
} }
c.mongoConfig.collectionName = mongoCollectionName
c.mongoConfig.hostname = hostname
c.mongoConfig.insideIp = goip.GetInsideIp(ctx)
c.mongoConfig.goVersion = runtime.Version()
c.log.mongo = true
// 创建时间序列集合 // 创建时间序列集合
c.mongoCreateCollection(ctx) c.mongoCreateCollection(ctx)
@ -140,6 +118,7 @@ func NewApiClient(config *ApiClientConfig) (*ApiClient, error) {
// 创建索引 // 创建索引
c.mongoCreateIndexes(ctx) c.mongoCreateIndexes(ctx)
c.mongoConfig.stats = true
} }
return c, nil return c, nil
@ -147,30 +126,30 @@ func NewApiClient(config *ApiClientConfig) (*ApiClient, error) {
// Middleware 中间件 // Middleware 中间件
func (c *ApiClient) Middleware(ctx context.Context, request gorequest.Response, sdkVersion string) { func (c *ApiClient) Middleware(ctx context.Context, request gorequest.Response, sdkVersion string) {
if c.log.gorm { if c.gormConfig.stats {
c.GormMiddleware(ctx, request, sdkVersion) c.gormMiddleware(ctx, request, sdkVersion)
} }
if c.log.mongo { if c.mongoConfig.stats {
c.MongoMiddleware(ctx, request, sdkVersion) c.mongoMiddleware(ctx, request, sdkVersion)
} }
} }
// MiddlewareXml 中间件 // MiddlewareXml 中间件
func (c *ApiClient) MiddlewareXml(ctx context.Context, request gorequest.Response, sdkVersion string) { func (c *ApiClient) MiddlewareXml(ctx context.Context, request gorequest.Response, sdkVersion string) {
if c.log.gorm { if c.gormConfig.stats {
c.GormMiddlewareXml(ctx, request, sdkVersion) c.gormMiddlewareXml(ctx, request, sdkVersion)
} }
if c.log.mongo { if c.mongoConfig.stats {
c.MongoMiddlewareXml(ctx, request, sdkVersion) c.mongoMiddlewareXml(ctx, request, sdkVersion)
} }
} }
// MiddlewareCustom 中间件 // MiddlewareCustom 中间件
func (c *ApiClient) MiddlewareCustom(ctx context.Context, api string, request gorequest.Response, sdkVersion string) { func (c *ApiClient) MiddlewareCustom(ctx context.Context, api string, request gorequest.Response, sdkVersion string) {
if c.log.gorm { if c.gormConfig.stats {
c.GormMiddlewareCustom(ctx, api, request, sdkVersion) c.gormMiddlewareCustom(ctx, api, request, sdkVersion)
} }
if c.log.mongo { if c.mongoConfig.stats {
c.MongoMiddlewareCustom(ctx, api, request, sdkVersion) c.mongoMiddlewareCustom(ctx, api, request, sdkVersion)
} }
} }

@ -2,151 +2,77 @@ package golog
import ( import (
"context" "context"
"errors"
"go.dtapp.net/dorm" "go.dtapp.net/dorm"
"go.dtapp.net/goip"
"go.dtapp.net/gorequest" "go.dtapp.net/gorequest"
"go.dtapp.net/gotime" "go.dtapp.net/gotime"
"go.dtapp.net/gotrace_id" "go.dtapp.net/gotrace_id"
"go.dtapp.net/gourl" "go.dtapp.net/gourl"
"gorm.io/gorm"
"os"
"runtime"
"time" "time"
"unicode/utf8" "unicode/utf8"
) )
// ApiGormClientConfig 接口实例配置 // 模型
type ApiGormClientConfig struct { type apiPostgresqlLog struct {
GormClientFun apiGormClientFun // 日志配置 LogId uint `gorm:"primaryKey;comment:【记录】编号" json:"log_id,omitempty"` //【记录】编号
Debug bool // 日志开关 TraceId string `gorm:"index;comment:【系统】跟踪编号" json:"trace_id,omitempty"` //【系统】跟踪编号
ZapLog *ZapLog // 日志服务 RequestTime time.Time `gorm:"index;comment:【请求】时间" json:"request_time,omitempty"` //【请求】时间
CurrentIp string // 当前ip RequestUri string `gorm:"comment:【请求】链接" json:"request_uri,omitempty"` //【请求】链接
} RequestUrl string `gorm:"comment:【请求】链接" json:"request_url,omitempty"` //【请求】链接
RequestApi string `gorm:"index;comment:【请求】接口" json:"request_api,omitempty"` //【请求】接口
// NewApiGormClient 创建接口实例化 RequestMethod string `gorm:"index;comment:【请求】方式" json:"request_method,omitempty"` //【请求】方式
// client 数据库服务 RequestParams string `gorm:"comment:【请求】参数" json:"request_params,omitempty"` //【请求】参数
// tableName 表名 RequestHeader string `gorm:"comment:【请求】头部" json:"request_header,omitempty"` //【请求】头部
func NewApiGormClient(config *ApiGormClientConfig) (*ApiClient, error) { RequestIp string `gorm:"default:0.0.0.0;index;comment:【请求】请求Ip" json:"request_ip,omitempty"` //【请求】请求Ip
ResponseHeader string `gorm:"comment:【返回】头部" json:"response_header,omitempty"` //【返回】头部
var ctx = context.Background() ResponseStatusCode int `gorm:"index;comment:【返回】状态码" json:"response_status_code,omitempty"` //【返回】状态码
ResponseBody string `gorm:"comment:【返回】数据" json:"response_content,omitempty"` //【返回】数据
c := &ApiClient{} ResponseContentLength int64 `gorm:"comment:【返回】大小" json:"response_content_length,omitempty"` //【返回】大小
ResponseTime time.Time `gorm:"index;comment:【返回】时间" json:"response_time,omitempty"` //【返回】时间
c.zapLog = config.ZapLog SystemHostName string `gorm:"index;comment:【系统】主机名" json:"system_host_name,omitempty"` //【系统】主机名
SystemInsideIp string `gorm:"default:0.0.0.0;comment:【系统】内网ip" json:"system_inside_ip,omitempty"` //【系统】内网ip
c.logDebug = config.Debug SystemOs string `gorm:"index;comment:【系统】系统类型" json:"system_os,omitempty"` //【系统】系统类型
SystemArch string `gorm:"index;comment:【系统】系统架构" json:"system_arch,omitempty"` //【系统】系统架构
if config.CurrentIp == "" { GoVersion string `gorm:"comment:【程序】Go版本" json:"go_version,omitempty"` //【程序】Go版本
config.CurrentIp = goip.GetOutsideIp(ctx) SdkVersion string `gorm:"comment:【程序】Sdk版本" json:"sdk_version,omitempty"` //【程序】Sdk版本
}
if config.CurrentIp != "" && config.CurrentIp != "0.0.0.0" {
c.currentIp = config.CurrentIp
}
client, tableName := config.GormClientFun()
if client == nil || client.Db == nil {
return nil, errors.New("没有设置驱动")
}
c.gormClient = client
if tableName == "" {
return nil, errors.New("没有设置表名")
}
c.gormConfig.tableName = tableName
err := c.gormAutoMigrate()
if err != nil {
return nil, errors.New("创建表失败:" + err.Error())
}
hostname, _ := os.Hostname()
c.gormConfig.hostname = hostname
c.gormConfig.insideIp = goip.GetInsideIp(ctx)
c.gormConfig.goVersion = runtime.Version()
c.log.gorm = true
return c, nil
} }
// 创建模型 // 创建模型
func (c *ApiClient) gormAutoMigrate() (err error) { func (c *ApiClient) gormAutoMigrate(ctx context.Context) {
err = c.gormClient.Db.Table(c.gormConfig.tableName).AutoMigrate(&apiPostgresqlLog{}) err := c.gormClient.Db.Table(c.gormConfig.tableName).AutoMigrate(&apiPostgresqlLog{})
if err != nil { if err != nil {
c.zapLog.WithLogger().Sugar().Infof("[golog.api.gormAutoMigrate]%s", err) c.zapLog.WithTraceId(ctx).Sugar().Errorf("创建模型:%s", err)
} }
return
}
// 模型结构体
type apiPostgresqlLog struct {
LogId uint `gorm:"primaryKey;comment:【记录】编号" json:"log_id,omitempty"` //【记录】编号
TraceId string `gorm:"index;comment:【系统】跟踪编号" json:"trace_id,omitempty"` //【系统】跟踪编号
RequestTime time.Time `gorm:"index;comment:【请求】时间" json:"request_time,omitempty"` //【请求】时间
RequestUri string `gorm:"comment:【请求】链接" json:"request_uri,omitempty"` //【请求】链接
RequestUrl string `gorm:"comment:【请求】链接" json:"request_url,omitempty"` //【请求】链接
RequestApi string `gorm:"index;comment:【请求】接口" json:"request_api,omitempty"` //【请求】接口
RequestMethod string `gorm:"index;comment:【请求】方式" json:"request_method,omitempty"` //【请求】方式
RequestParams string `gorm:"comment:【请求】参数" json:"request_params,omitempty"` //【请求】参数
RequestHeader string `gorm:"comment:【请求】头部" json:"request_header,omitempty"` //【请求】头部
RequestIp string `gorm:"index;comment:【请求】请求Ip" json:"request_ip,omitempty"` //【请求】请求Ip
ResponseHeader string `gorm:"comment:【返回】头部" json:"response_header,omitempty"` //【返回】头部
ResponseStatusCode int `gorm:"index;comment:【返回】状态码" json:"response_status_code,omitempty"` //【返回】状态码
ResponseBody string `gorm:"comment:【返回】数据" json:"response_content,omitempty"` //【返回】数据
ResponseContentLength int64 `gorm:"comment:【返回】大小" json:"response_content_length,omitempty"` //【返回】大小
ResponseTime time.Time `gorm:"index;comment:【返回】时间" json:"response_time,omitempty"` //【返回】时间
SystemHostName string `gorm:"index;comment:【系统】主机名" json:"system_host_name,omitempty"` //【系统】主机名
SystemInsideIp string `gorm:"index;comment:【系统】内网ip" json:"system_inside_ip,omitempty"` //【系统】内网ip
SystemOs string `gorm:"index;comment:【系统】系统类型" json:"system_os,omitempty"` //【系统】系统类型
SystemArch string `gorm:"index;comment:【系统】系统架构" json:"system_arch,omitempty"` //【系统】系统架构
SystemCpuQuantity int `gorm:"index;comment:【系统】CPU核数" json:"system_cpu_quantity,omitempty"` //【系统】CPU核数
GoVersion string `gorm:"index;comment:【程序】Go版本" json:"go_version,omitempty"` //【程序】Go版本
SdkVersion string `gorm:"index;comment:【程序】Sdk版本" json:"sdk_version,omitempty"` //【程序】Sdk版本
} }
// 记录日志 // 记录日志
func (c *ApiClient) gormRecord(ctx context.Context, postgresqlLog apiPostgresqlLog) (err error) { func (c *ApiClient) gormRecord(ctx context.Context, data apiPostgresqlLog) (err error) {
if utf8.ValidString(postgresqlLog.ResponseBody) == false { if utf8.ValidString(data.ResponseBody) == false {
postgresqlLog.ResponseBody = "" data.ResponseBody = ""
} }
postgresqlLog.SystemHostName = c.gormConfig.hostname data.SystemHostName = c.config.systemHostName //【系统】主机名
postgresqlLog.SystemInsideIp = c.gormConfig.insideIp data.SystemInsideIp = c.config.systemInsideIp //【系统】内网ip
postgresqlLog.GoVersion = c.gormConfig.goVersion data.GoVersion = c.config.goVersion //【程序】Go版本
data.TraceId = gotrace_id.GetTraceIdContext(ctx) //【记录】跟踪编号
postgresqlLog.TraceId = gotrace_id.GetTraceIdContext(ctx) data.RequestIp = c.config.systemOutsideIp //【请求】请求Ip
data.SystemOs = c.config.systemOs //【系统】系统类型
postgresqlLog.RequestIp = c.currentIp data.SystemArch = c.config.systemArch //【系统】系统架构
postgresqlLog.SystemOs = c.config.os err = c.gormClient.Db.Table(c.gormConfig.tableName).Create(&data).Error
postgresqlLog.SystemArch = c.config.arch
postgresqlLog.SystemCpuQuantity = c.config.maxProCs
err = c.gormClient.Db.Table(c.gormConfig.tableName).Create(&postgresqlLog).Error
if err != nil { if err != nil {
c.zapLog.WithTraceId(ctx).Sugar().Errorf("[golog.api.gormRecord]%s", err) c.zapLog.WithTraceId(ctx).Sugar().Errorf("记录日志失败:%s", err)
} }
return return
} }
// GormQuery 查询
func (c *ApiClient) GormQuery(ctx context.Context) *gorm.DB {
return c.gormClient.Db.Table(c.gormConfig.tableName)
}
// GormDelete 删除 // GormDelete 删除
func (c *ApiClient) GormDelete(ctx context.Context, hour int64) error { func (c *ApiClient) GormDelete(ctx context.Context, hour int64) error {
return c.gormClient.Db.Table(c.gormConfig.tableName).Where("request_time < ?", gotime.Current().BeforeHour(hour).Format()).Delete(&apiPostgresqlLog{}).Error return c.gormClient.Db.Table(c.gormConfig.tableName).Where("request_time < ?", gotime.Current().BeforeHour(hour).Format()).Delete(&apiPostgresqlLog{}).Error
} }
// GormMiddleware 中间件 // 中间件
func (c *ApiClient) GormMiddleware(ctx context.Context, request gorequest.Response, sdkVersion string) { func (c *ApiClient) gormMiddleware(ctx context.Context, request gorequest.Response, sdkVersion string) {
data := apiPostgresqlLog{ data := apiPostgresqlLog{
RequestTime: request.RequestTime, //【请求】时间 RequestTime: request.RequestTime, //【请求】时间
RequestUri: request.RequestUri, //【请求】链接 RequestUri: request.RequestUri, //【请求】链接
@ -162,29 +88,29 @@ func (c *ApiClient) GormMiddleware(ctx context.Context, request gorequest.Respon
SdkVersion: sdkVersion, //【程序】Sdk版本 SdkVersion: sdkVersion, //【程序】Sdk版本
} }
if request.HeaderIsImg() { if request.HeaderIsImg() {
c.zapLog.WithTraceId(ctx).Sugar().Infof("[golog.api.GormMiddleware.isimg]%s%s", data.RequestUri, request.ResponseHeader.Get("Content-Type")) c.zapLog.WithTraceId(ctx).Sugar().Infof("[golog.api.gormMiddleware.isimg]%s%s", data.RequestUri, request.ResponseHeader.Get("Content-Type"))
} else { } else {
if len(request.ResponseBody) > 0 { if len(request.ResponseBody) > 0 {
data.ResponseBody = dorm.JsonEncodeNoError(dorm.JsonDecodeNoError(request.ResponseBody)) //【返回】数据 data.ResponseBody = dorm.JsonEncodeNoError(dorm.JsonDecodeNoError(request.ResponseBody)) //【返回】数据
} else { } else {
if c.logDebug { if c.logDebug {
c.zapLog.WithTraceId(ctx).Sugar().Infof("[golog.api.GormMiddleware.len]%s%s", data.RequestUri, request.ResponseBody) c.zapLog.WithTraceId(ctx).Sugar().Infof("[golog.api.gormMiddleware.len]%s%s", data.RequestUri, request.ResponseBody)
} }
} }
} }
if c.logDebug { if c.logDebug {
c.zapLog.WithTraceId(ctx).Sugar().Infof("[golog.api.GormMiddleware.data]%+v", data) c.zapLog.WithTraceId(ctx).Sugar().Infof("[golog.api.gormMiddleware.data]%+v", data)
} }
err := c.gormRecord(ctx, data) err := c.gormRecord(ctx, data)
if err != nil { if err != nil {
c.zapLog.WithTraceId(ctx).Sugar().Errorf("[golog.api.GormMiddleware]%s", err.Error()) c.zapLog.WithTraceId(ctx).Sugar().Errorf("[golog.api.gormMiddleware]%s", err.Error())
} }
} }
// GormMiddlewareXml 中间件 // 中间件
func (c *ApiClient) GormMiddlewareXml(ctx context.Context, request gorequest.Response, sdkVersion string) { func (c *ApiClient) gormMiddlewareXml(ctx context.Context, request gorequest.Response, sdkVersion string) {
data := apiPostgresqlLog{ data := apiPostgresqlLog{
RequestTime: request.RequestTime, //【请求】时间 RequestTime: request.RequestTime, //【请求】时间
RequestUri: request.RequestUri, //【请求】链接 RequestUri: request.RequestUri, //【请求】链接
@ -200,29 +126,29 @@ func (c *ApiClient) GormMiddlewareXml(ctx context.Context, request gorequest.Res
SdkVersion: sdkVersion, //【程序】Sdk版本 SdkVersion: sdkVersion, //【程序】Sdk版本
} }
if request.HeaderIsImg() { if request.HeaderIsImg() {
c.zapLog.WithTraceId(ctx).Sugar().Infof("[golog.api.GormMiddlewareXml.isimg]%s%s", data.RequestUri, request.ResponseHeader.Get("Content-Type")) c.zapLog.WithTraceId(ctx).Sugar().Infof("[golog.api.gormMiddlewareXml.isimg]%s%s", data.RequestUri, request.ResponseHeader.Get("Content-Type"))
} else { } else {
if len(request.ResponseBody) > 0 { if len(request.ResponseBody) > 0 {
data.ResponseBody = dorm.JsonEncodeNoError(request.ResponseBody) //【返回】内容 data.ResponseBody = dorm.JsonEncodeNoError(request.ResponseBody) //【返回】内容
} else { } else {
if c.logDebug { if c.logDebug {
c.zapLog.WithTraceId(ctx).Sugar().Infof("[golog.api.GormMiddlewareXml.len]%s%s", data.RequestUri, request.ResponseBody) c.zapLog.WithTraceId(ctx).Sugar().Infof("[golog.api.gormMiddlewareXml.len]%s%s", data.RequestUri, request.ResponseBody)
} }
} }
} }
if c.logDebug { if c.logDebug {
c.zapLog.WithTraceId(ctx).Sugar().Infof("[golog.api.GormMiddlewareXml.data]%+v", data) c.zapLog.WithTraceId(ctx).Sugar().Infof("[golog.api.gormMiddlewareXml.data]%+v", data)
} }
err := c.gormRecord(ctx, data) err := c.gormRecord(ctx, data)
if err != nil { if err != nil {
c.zapLog.WithTraceId(ctx).Sugar().Errorf("[golog.api.GormMiddlewareXml]%s", err.Error()) c.zapLog.WithTraceId(ctx).Sugar().Errorf("[golog.api.gormMiddlewareXml]%s", err.Error())
} }
} }
// GormMiddlewareCustom 中间件 // 中间件
func (c *ApiClient) GormMiddlewareCustom(ctx context.Context, api string, request gorequest.Response, sdkVersion string) { func (c *ApiClient) gormMiddlewareCustom(ctx context.Context, api string, request gorequest.Response, sdkVersion string) {
data := apiPostgresqlLog{ data := apiPostgresqlLog{
RequestTime: request.RequestTime, //【请求】时间 RequestTime: request.RequestTime, //【请求】时间
RequestUri: request.RequestUri, //【请求】链接 RequestUri: request.RequestUri, //【请求】链接
@ -238,23 +164,23 @@ func (c *ApiClient) GormMiddlewareCustom(ctx context.Context, api string, reques
SdkVersion: sdkVersion, //【程序】Sdk版本 SdkVersion: sdkVersion, //【程序】Sdk版本
} }
if request.HeaderIsImg() { if request.HeaderIsImg() {
c.zapLog.WithTraceId(ctx).Sugar().Infof("[golog.api.GormMiddlewareCustom.isimg]%s%s", data.RequestUri, request.ResponseHeader.Get("Content-Type")) c.zapLog.WithTraceId(ctx).Sugar().Infof("[golog.api.gormMiddlewareCustom.isimg]%s%s", data.RequestUri, request.ResponseHeader.Get("Content-Type"))
} else { } else {
if len(request.ResponseBody) > 0 { if len(request.ResponseBody) > 0 {
data.ResponseBody = dorm.JsonEncodeNoError(dorm.JsonDecodeNoError(request.ResponseBody)) //【返回】数据 data.ResponseBody = dorm.JsonEncodeNoError(dorm.JsonDecodeNoError(request.ResponseBody)) //【返回】数据
} else { } else {
if c.logDebug { if c.logDebug {
c.zapLog.WithTraceId(ctx).Sugar().Infof("[golog.api.GormMiddlewareCustom.len]%s%s", data.RequestUri, request.ResponseBody) c.zapLog.WithTraceId(ctx).Sugar().Infof("[golog.api.gormMiddlewareCustom.len]%s%s", data.RequestUri, request.ResponseBody)
} }
} }
} }
if c.logDebug { if c.logDebug {
c.zapLog.WithTraceId(ctx).Sugar().Infof("[golog.api.GormMiddlewareCustom.data]%+v", data) c.zapLog.WithTraceId(ctx).Sugar().Infof("[golog.api.gormMiddlewareCustom.data]%+v", data)
} }
err := c.gormRecord(ctx, data) err := c.gormRecord(ctx, data)
if err != nil { if err != nil {
c.zapLog.WithTraceId(ctx).Sugar().Errorf("[golog.api.GormMiddlewareCustom]%s", err.Error()) c.zapLog.WithTraceId(ctx).Sugar().Errorf("[golog.api.gormMiddlewareCustom]%s", err.Error())
} }
} }

@ -2,9 +2,7 @@ package golog
import ( import (
"context" "context"
"errors"
"go.dtapp.net/dorm" "go.dtapp.net/dorm"
"go.dtapp.net/goip"
"go.dtapp.net/gorequest" "go.dtapp.net/gorequest"
"go.dtapp.net/gotime" "go.dtapp.net/gotime"
"go.dtapp.net/gotrace_id" "go.dtapp.net/gotrace_id"
@ -13,137 +11,12 @@ import (
"go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options" "go.mongodb.org/mongo-driver/mongo/options"
"os"
"runtime"
) )
// ApiMongoClientConfig 接口实例配置
type ApiMongoClientConfig struct {
MongoClientFun apiMongoClientFun // 日志配置
Debug bool // 日志开关
ZapLog *ZapLog // 日志服务
CurrentIp string // 当前ip
}
// NewApiMongoClient 创建接口实例化
// client 数据库服务
// databaseName 库名
// collectionName 表名
func NewApiMongoClient(config *ApiMongoClientConfig) (*ApiClient, error) {
var ctx = context.Background()
c := &ApiClient{}
c.zapLog = config.ZapLog
c.logDebug = config.Debug
if config.CurrentIp == "" {
config.CurrentIp = goip.GetOutsideIp(ctx)
}
if config.CurrentIp != "" && config.CurrentIp != "0.0.0.0" {
c.currentIp = config.CurrentIp
}
client, databaseName, collectionName := config.MongoClientFun()
if client == nil || client.Db == nil {
return nil, errors.New("没有设置驱动")
}
c.mongoClient = client
if databaseName == "" {
return nil, errors.New("没有设置库名")
}
c.mongoConfig.databaseName = databaseName
if collectionName == "" {
return nil, errors.New("没有设置表名")
}
c.mongoConfig.collectionName = collectionName
hostname, _ := os.Hostname()
c.mongoConfig.hostname = hostname
c.mongoConfig.insideIp = goip.GetInsideIp(ctx)
c.mongoConfig.goVersion = runtime.Version()
c.log.mongo = true
// 创建时间序列集合
c.mongoCreateCollection(ctx)
// 创建索引
c.mongoCreateIndexes(ctx)
return c, nil
}
// 创建时间序列集合
func (c *ApiClient) mongoCreateCollection(ctx context.Context) {
var commandResult bson.M
commandErr := c.mongoClient.Db.Database(c.mongoConfig.databaseName).RunCommand(ctx, bson.D{{
"listCollections", 1,
}}).Decode(&commandResult)
if commandErr != nil {
c.zapLog.WithLogger().Sugar().Errorf("检查时间序列集合:%s", commandErr)
} else {
err := c.mongoClient.Db.Database(c.mongoConfig.databaseName).CreateCollection(ctx, c.mongoConfig.collectionName, options.CreateCollection().SetTimeSeriesOptions(options.TimeSeries().SetTimeField("log_time")))
if err != nil {
c.zapLog.WithLogger().Sugar().Errorf("创建时间序列集合:%s", err)
}
}
}
// 创建索引
func (c *ApiClient) mongoCreateIndexes(ctx context.Context) {
c.zapLog.WithLogger().Sugar().Infof(c.mongoClient.Db.Database(c.mongoConfig.databaseName).Collection(c.mongoConfig.collectionName).Indexes().CreateOne(ctx, mongo.IndexModel{Keys: bson.D{
{"trace_id", 1},
}}))
c.zapLog.WithLogger().Sugar().Infof(c.mongoClient.Db.Database(c.mongoConfig.databaseName).Collection(c.mongoConfig.collectionName).Indexes().CreateOne(ctx, mongo.IndexModel{Keys: bson.D{
{"log_time", -1},
}}))
c.zapLog.WithLogger().Sugar().Infof(c.mongoClient.Db.Database(c.mongoConfig.databaseName).Collection(c.mongoConfig.collectionName).Indexes().CreateOne(ctx, mongo.IndexModel{Keys: bson.D{
{"request_time", -1},
}}))
c.zapLog.WithLogger().Sugar().Infof(c.mongoClient.Db.Database(c.mongoConfig.databaseName).Collection(c.mongoConfig.collectionName).Indexes().CreateOne(ctx, mongo.IndexModel{Keys: bson.D{
{"request_method", 1},
}}))
c.zapLog.WithLogger().Sugar().Infof(c.mongoClient.Db.Database(c.mongoConfig.databaseName).Collection(c.mongoConfig.collectionName).Indexes().CreateOne(ctx, mongo.IndexModel{Keys: bson.D{
{"response_status_code", 1},
}}))
c.zapLog.WithLogger().Sugar().Infof(c.mongoClient.Db.Database(c.mongoConfig.databaseName).Collection(c.mongoConfig.collectionName).Indexes().CreateOne(ctx, mongo.IndexModel{Keys: bson.D{
{"response_time", -1},
}}))
c.zapLog.WithLogger().Sugar().Infof(c.mongoClient.Db.Database(c.mongoConfig.databaseName).Collection(c.mongoConfig.collectionName).Indexes().CreateOne(ctx, mongo.IndexModel{Keys: bson.D{
{"system_host_name", 1},
}}))
c.zapLog.WithLogger().Sugar().Infof(c.mongoClient.Db.Database(c.mongoConfig.databaseName).Collection(c.mongoConfig.collectionName).Indexes().CreateOne(ctx, mongo.IndexModel{Keys: bson.D{
{"system_inside_ip", 1},
}}))
c.zapLog.WithLogger().Sugar().Infof(c.mongoClient.Db.Database(c.mongoConfig.databaseName).Collection(c.mongoConfig.collectionName).Indexes().CreateOne(ctx, mongo.IndexModel{Keys: bson.D{
{"system_os", -1},
}}))
c.zapLog.WithLogger().Sugar().Infof(c.mongoClient.Db.Database(c.mongoConfig.databaseName).Collection(c.mongoConfig.collectionName).Indexes().CreateOne(ctx, mongo.IndexModel{Keys: bson.D{
{"system_arch", -1},
}}))
c.zapLog.WithLogger().Sugar().Infof(c.mongoClient.Db.Database(c.mongoConfig.databaseName).Collection(c.mongoConfig.collectionName).Indexes().CreateOne(ctx, mongo.IndexModel{Keys: bson.D{
{"system_cpu_quantity", 1},
}}))
c.zapLog.WithLogger().Sugar().Infof(c.mongoClient.Db.Database(c.mongoConfig.databaseName).Collection(c.mongoConfig.collectionName).Indexes().CreateOne(ctx, mongo.IndexModel{Keys: bson.D{
{"go_version", -1},
}}))
c.zapLog.WithLogger().Sugar().Infof(c.mongoClient.Db.Database(c.mongoConfig.databaseName).Collection(c.mongoConfig.collectionName).Indexes().CreateOne(ctx, mongo.IndexModel{Keys: bson.D{
{"sdk_version", -1},
}}))
}
// 模型结构体 // 模型结构体
type apiMongolLog struct { type apiMongolLog struct {
LogId primitive.ObjectID `json:"log_id,omitempty" bson:"_id,omitempty"` //【记录】编号 LogId primitive.ObjectID `json:"log_id,omitempty" bson:"_id,omitempty"` //【记录】编号
LogTime primitive.DateTime `json:"log_time,omitempty" bson:"log_time,omitempty"` //【记录】时间 LogTime primitive.DateTime `json:"log_time,omitempty" bson:"log_time"` //【记录】时间
TraceId string `json:"trace_id,omitempty" bson:"trace_id,omitempty"` //【记录】跟踪编号 TraceId string `json:"trace_id,omitempty" bson:"trace_id,omitempty"` //【记录】跟踪编号
RequestTime dorm.BsonTime `json:"request_time,omitempty" bson:"request_time,omitempty"` //【请求】时间 RequestTime dorm.BsonTime `json:"request_time,omitempty" bson:"request_time,omitempty"` //【请求】时间
RequestUri string `json:"request_uri,omitempty" bson:"request_uri,omitempty"` //【请求】链接 RequestUri string `json:"request_uri,omitempty" bson:"request_uri,omitempty"` //【请求】链接
@ -162,48 +35,63 @@ type apiMongolLog struct {
SystemInsideIp string `json:"system_inside_ip,omitempty" bson:"system_inside_ip,omitempty"` //【系统】内网ip SystemInsideIp string `json:"system_inside_ip,omitempty" bson:"system_inside_ip,omitempty"` //【系统】内网ip
SystemOs string `json:"system_os,omitempty" bson:"system_os,omitempty"` //【系统】系统类型 SystemOs string `json:"system_os,omitempty" bson:"system_os,omitempty"` //【系统】系统类型
SystemArch string `json:"system_arch,omitempty" bson:"system_arch,omitempty"` //【系统】系统架构 SystemArch string `json:"system_arch,omitempty" bson:"system_arch,omitempty"` //【系统】系统架构
SystemCpuQuantity int `json:"system_cpu_quantity,omitempty" bson:"system_cpu_quantity,omitempty"` //【系统】CPU核数
GoVersion string `json:"go_version,omitempty" bson:"go_version,omitempty"` //【程序】Go版本 GoVersion string `json:"go_version,omitempty" bson:"go_version,omitempty"` //【程序】Go版本
SdkVersion string `json:"sdk_version,omitempty" bson:"sdk_version,omitempty"` //【程序】Sdk版本 SdkVersion string `json:"sdk_version,omitempty" bson:"sdk_version,omitempty"` //【程序】Sdk版本
} }
// 创建时间序列集合
func (c *ApiClient) mongoCreateCollection(ctx context.Context) {
err := c.mongoClient.Database(c.mongoConfig.databaseName).CreateCollection(ctx, c.mongoConfig.collectionName, options.CreateCollection().SetTimeSeriesOptions(options.TimeSeries().SetTimeField("log_time")))
if err != nil {
c.zapLog.WithTraceId(ctx).Sugar().Errorf("创建时间序列集合:%s", err)
}
}
// 创建索引
func (c *ApiClient) mongoCreateIndexes(ctx context.Context) {
indexes, err := c.mongoClient.Database(c.mongoConfig.databaseName).Collection(c.mongoConfig.collectionName).CreateManyIndexes(ctx, []mongo.IndexModel{
{
Keys: bson.D{{
Key: "log_time",
Value: -1,
}},
}})
if err != nil {
c.zapLog.WithTraceId(ctx).Sugar().Errorf("创建索引:%s", err)
}
c.zapLog.WithTraceId(ctx).Sugar().Infof("创建索引:%s", indexes)
}
// 记录日志 // 记录日志
func (c *ApiClient) mongoRecord(ctx context.Context, mongoLog apiMongolLog) (err error) { func (c *ApiClient) mongoRecord(ctx context.Context, mongoLog apiMongolLog) (err error) {
mongoLog.SystemHostName = c.mongoConfig.hostname //【系统】主机名 mongoLog.SystemHostName = c.config.systemHostName //【系统】主机名
mongoLog.SystemInsideIp = c.mongoConfig.insideIp //【系统】内网ip mongoLog.SystemInsideIp = c.config.systemInsideIp //【系统】内网ip
mongoLog.GoVersion = c.mongoConfig.goVersion //【程序】Go版本 mongoLog.GoVersion = c.config.goVersion //【程序】Go版本
mongoLog.TraceId = gotrace_id.GetTraceIdContext(ctx) //【记录】跟踪编号 mongoLog.TraceId = gotrace_id.GetTraceIdContext(ctx) //【记录】跟踪编号
mongoLog.RequestIp = c.currentIp //【请求】请求Ip mongoLog.RequestIp = c.config.systemOutsideIp //【请求】请求Ip
mongoLog.SystemOs = c.config.os //【系统】系统类型 mongoLog.SystemOs = c.config.systemOs //【系统】系统类型
mongoLog.SystemArch = c.config.arch //【系统】系统架构 mongoLog.SystemArch = c.config.systemArch //【系统】系统架构
mongoLog.SystemCpuQuantity = c.config.maxProCs //【系统】CPU核数
mongoLog.LogId = primitive.NewObjectID() //【记录】编号 mongoLog.LogId = primitive.NewObjectID() //【记录】编号
_, err = c.mongoClient.Database(c.mongoConfig.databaseName).Collection(c.mongoConfig.collectionName).InsertOne(mongoLog) _, err = c.mongoClient.Database(c.mongoConfig.databaseName).Collection(c.mongoConfig.collectionName).InsertOne(ctx, mongoLog)
if err != nil { if err != nil {
c.zapLog.WithTraceId(ctx).Sugar().Errorf("[golog.api.mongoRecord]%s", err) c.zapLog.WithTraceId(ctx).Sugar().Errorf("记录日志失败%s", err)
} }
return err return err
} }
// MongoQuery 查询
func (c *ApiClient) MongoQuery(ctx context.Context) *mongo.Collection {
return c.mongoClient.Db.Database(c.mongoConfig.databaseName).Collection(c.mongoConfig.collectionName)
}
// MongoDelete 删除 // MongoDelete 删除
func (c *ApiClient) MongoDelete(ctx context.Context, hour int64) (*mongo.DeleteResult, error) { func (c *ApiClient) MongoDelete(ctx context.Context, hour int64) (*mongo.DeleteResult, error) {
filter := bson.D{{"log_time", bson.D{{"$lt", primitive.NewDateTimeFromTime(gotime.Current().BeforeHour(hour).Time)}}}} filter := bson.D{{"log_time", bson.D{{"$lt", primitive.NewDateTimeFromTime(gotime.Current().BeforeHour(hour).Time)}}}}
return c.mongoClient.Db.Database(c.mongoConfig.databaseName).Collection(c.mongoConfig.collectionName).DeleteMany(ctx, filter) return c.mongoClient.Db.Database(c.mongoConfig.databaseName).Collection(c.mongoConfig.collectionName).DeleteMany(ctx, filter)
} }
// MongoMiddleware 中间件 // 中间件
func (c *ApiClient) MongoMiddleware(ctx context.Context, request gorequest.Response, sdkVersion string) { func (c *ApiClient) mongoMiddleware(ctx context.Context, request gorequest.Response, sdkVersion string) {
data := apiMongolLog{ data := apiMongolLog{
LogTime: primitive.NewDateTimeFromTime(request.RequestTime), //【记录】时间 LogTime: primitive.NewDateTimeFromTime(request.RequestTime), //【记录】时间
RequestTime: dorm.BsonTime(request.RequestTime), //【请求】时间 RequestTime: dorm.NewBsonTimeFromTime(request.RequestTime), //【请求】时间
RequestUri: request.RequestUri, //【请求】链接 RequestUri: request.RequestUri, //【请求】链接
RequestUrl: gourl.UriParse(request.RequestUri).Url, //【请求】链接 RequestUrl: gourl.UriParse(request.RequestUri).Url, //【请求】链接
RequestApi: gourl.UriParse(request.RequestUri).Path, //【请求】接口 RequestApi: gourl.UriParse(request.RequestUri).Path, //【请求】接口
@ -213,36 +101,36 @@ func (c *ApiClient) MongoMiddleware(ctx context.Context, request gorequest.Respo
ResponseHeader: request.ResponseHeader, //【返回】头部 ResponseHeader: request.ResponseHeader, //【返回】头部
ResponseStatusCode: request.ResponseStatusCode, //【返回】状态码 ResponseStatusCode: request.ResponseStatusCode, //【返回】状态码
ResponseContentLength: request.ResponseContentLength, //【返回】大小 ResponseContentLength: request.ResponseContentLength, //【返回】大小
ResponseTime: dorm.BsonTime(request.ResponseTime), //【返回】时间 ResponseTime: dorm.NewBsonTimeFromTime(request.ResponseTime), //【返回】时间
SdkVersion: sdkVersion, //【程序】Sdk版本 SdkVersion: sdkVersion, //【程序】Sdk版本
} }
if request.ResponseHeader.Get("Content-Type") == "image/jpeg" || request.ResponseHeader.Get("Content-Type") == "image/png" || request.ResponseHeader.Get("Content-Type") == "image/jpg" { if request.HeaderIsImg() {
c.zapLog.WithTraceId(ctx).Sugar().Infof("[golog.api.MongoMiddleware.type]%s %s", data.RequestUri, request.ResponseHeader.Get("Content-Type")) c.zapLog.WithTraceId(ctx).Sugar().Infof("[golog.api.mongoMiddleware.type]%s %s", data.RequestUri, request.ResponseHeader.Get("Content-Type"))
} else { } else {
if len(request.ResponseBody) > 0 { if len(request.ResponseBody) > 0 {
data.ResponseBody = dorm.JsonDecodeNoError(request.ResponseBody) //【返回】内容 data.ResponseBody = dorm.JsonDecodeNoError(request.ResponseBody) //【返回】内容
} else { } else {
if c.logDebug { if c.logDebug {
c.zapLog.WithTraceId(ctx).Sugar().Infof("[golog.api.MongoMiddleware.len]%s %s", data.RequestUri, request.ResponseBody) c.zapLog.WithTraceId(ctx).Sugar().Infof("[golog.api.mongoMiddleware.len]%s %s", data.RequestUri, request.ResponseBody)
} }
} }
} }
if c.logDebug { if c.logDebug {
c.zapLog.WithTraceId(ctx).Sugar().Infof("[golog.api.MongoMiddleware.data]%+v", data) c.zapLog.WithTraceId(ctx).Sugar().Infof("[golog.api.mongoMiddleware.data]%+v", data)
} }
err := c.mongoRecord(ctx, data) err := c.mongoRecord(ctx, data)
if err != nil { if err != nil {
c.zapLog.WithTraceId(ctx).Sugar().Errorf("[golog.api.MongoMiddleware]%s", err.Error()) c.zapLog.WithTraceId(ctx).Sugar().Errorf("[golog.api.mongoMiddleware]%s", err.Error())
} }
} }
// MongoMiddlewareXml 中间件 // 中间件
func (c *ApiClient) MongoMiddlewareXml(ctx context.Context, request gorequest.Response, sdkVersion string) { func (c *ApiClient) mongoMiddlewareXml(ctx context.Context, request gorequest.Response, sdkVersion string) {
data := apiMongolLog{ data := apiMongolLog{
LogTime: primitive.NewDateTimeFromTime(request.RequestTime), //【记录】时间 LogTime: primitive.NewDateTimeFromTime(request.RequestTime), //【记录】时间
RequestTime: dorm.BsonTime(request.RequestTime), //【请求】时间 RequestTime: dorm.NewBsonTimeFromTime(request.RequestTime), //【请求】时间
RequestUri: request.RequestUri, //【请求】链接 RequestUri: request.RequestUri, //【请求】链接
RequestUrl: gourl.UriParse(request.RequestUri).Url, //【请求】链接 RequestUrl: gourl.UriParse(request.RequestUri).Url, //【请求】链接
RequestApi: gourl.UriParse(request.RequestUri).Path, //【请求】接口 RequestApi: gourl.UriParse(request.RequestUri).Path, //【请求】接口
@ -252,36 +140,36 @@ func (c *ApiClient) MongoMiddlewareXml(ctx context.Context, request gorequest.Re
ResponseHeader: request.ResponseHeader, //【返回】头部 ResponseHeader: request.ResponseHeader, //【返回】头部
ResponseStatusCode: request.ResponseStatusCode, //【返回】状态码 ResponseStatusCode: request.ResponseStatusCode, //【返回】状态码
ResponseContentLength: request.ResponseContentLength, //【返回】大小 ResponseContentLength: request.ResponseContentLength, //【返回】大小
ResponseTime: dorm.BsonTime(request.ResponseTime), //【返回】时间 ResponseTime: dorm.NewBsonTimeFromTime(request.ResponseTime), //【返回】时间
SdkVersion: sdkVersion, //【程序】Sdk版本 SdkVersion: sdkVersion, //【程序】Sdk版本
} }
if request.ResponseHeader.Get("Content-Type") == "image/jpeg" || request.ResponseHeader.Get("Content-Type") == "image/png" || request.ResponseHeader.Get("Content-Type") == "image/jpg" { if request.HeaderIsImg() {
c.zapLog.WithTraceId(ctx).Sugar().Infof("[golog.api.MongoMiddlewareXml.type]%s %s", data.RequestUri, request.ResponseHeader.Get("Content-Type")) c.zapLog.WithTraceId(ctx).Sugar().Infof("[golog.api.mongoMiddlewareXml.type]%s %s", data.RequestUri, request.ResponseHeader.Get("Content-Type"))
} else { } else {
if len(request.ResponseBody) > 0 { if len(request.ResponseBody) > 0 {
data.ResponseBody = dorm.XmlDecodeNoError(request.ResponseBody) //【返回】内容 data.ResponseBody = dorm.XmlDecodeNoError(request.ResponseBody) //【返回】内容
} else { } else {
if c.logDebug { if c.logDebug {
c.zapLog.WithTraceId(ctx).Sugar().Infof("[golog.api.MongoMiddlewareXml]%s %s", data.RequestUri, request.ResponseBody) c.zapLog.WithTraceId(ctx).Sugar().Infof("[golog.api.mongoMiddlewareXml]%s %s", data.RequestUri, request.ResponseBody)
} }
} }
} }
if c.logDebug { if c.logDebug {
c.zapLog.WithTraceId(ctx).Sugar().Infof("[golog.api.MongoMiddlewareXml.data]%+v", data) c.zapLog.WithTraceId(ctx).Sugar().Infof("[golog.api.mongoMiddlewareXml.data]%+v", data)
} }
err := c.mongoRecord(ctx, data) err := c.mongoRecord(ctx, data)
if err != nil { if err != nil {
c.zapLog.WithTraceId(ctx).Sugar().Errorf("[golog.api.MongoMiddlewareXml]%s", err.Error()) c.zapLog.WithTraceId(ctx).Sugar().Errorf("[golog.api.mongoMiddlewareXml]%s", err.Error())
} }
} }
// MongoMiddlewareCustom 中间件 // 中间件
func (c *ApiClient) MongoMiddlewareCustom(ctx context.Context, api string, request gorequest.Response, sdkVersion string) { func (c *ApiClient) mongoMiddlewareCustom(ctx context.Context, api string, request gorequest.Response, sdkVersion string) {
data := apiMongolLog{ data := apiMongolLog{
LogTime: primitive.NewDateTimeFromTime(request.RequestTime), //【记录】时间 LogTime: primitive.NewDateTimeFromTime(request.RequestTime), //【记录】时间
RequestTime: dorm.BsonTime(request.RequestTime), //【请求】时间 RequestTime: dorm.NewBsonTimeFromTime(request.RequestTime), //【请求】时间
RequestUri: request.RequestUri, //【请求】链接 RequestUri: request.RequestUri, //【请求】链接
RequestUrl: gourl.UriParse(request.RequestUri).Url, //【请求】链接 RequestUrl: gourl.UriParse(request.RequestUri).Url, //【请求】链接
RequestApi: api, //【请求】接口 RequestApi: api, //【请求】接口
@ -291,27 +179,27 @@ func (c *ApiClient) MongoMiddlewareCustom(ctx context.Context, api string, reque
ResponseHeader: request.ResponseHeader, //【返回】头部 ResponseHeader: request.ResponseHeader, //【返回】头部
ResponseStatusCode: request.ResponseStatusCode, //【返回】状态码 ResponseStatusCode: request.ResponseStatusCode, //【返回】状态码
ResponseContentLength: request.ResponseContentLength, //【返回】大小 ResponseContentLength: request.ResponseContentLength, //【返回】大小
ResponseTime: dorm.BsonTime(request.ResponseTime), //【返回】时间 ResponseTime: dorm.NewBsonTimeFromTime(request.ResponseTime), //【返回】时间
SdkVersion: sdkVersion, //【程序】Sdk版本 SdkVersion: sdkVersion, //【程序】Sdk版本
} }
if request.ResponseHeader.Get("Content-Type") == "image/jpeg" || request.ResponseHeader.Get("Content-Type") == "image/png" || request.ResponseHeader.Get("Content-Type") == "image/jpg" { if request.HeaderIsImg() {
c.zapLog.WithTraceId(ctx).Sugar().Infof("[golog.api.MongoMiddlewareCustom.type]%s %s", data.RequestUri, request.ResponseHeader.Get("Content-Type")) c.zapLog.WithTraceId(ctx).Sugar().Infof("[golog.api.mongoMiddlewareCustom.type]%s %s", data.RequestUri, request.ResponseHeader.Get("Content-Type"))
} else { } else {
if len(request.ResponseBody) > 0 { if len(request.ResponseBody) > 0 {
data.ResponseBody = dorm.JsonDecodeNoError(request.ResponseBody) //【返回】内容 data.ResponseBody = dorm.JsonDecodeNoError(request.ResponseBody) //【返回】内容
} else { } else {
if c.logDebug { if c.logDebug {
c.zapLog.WithTraceId(ctx).Sugar().Infof("[golog.api.MongoMiddlewareCustom]%s %s", data.RequestUri, request.ResponseBody) c.zapLog.WithTraceId(ctx).Sugar().Infof("[golog.api.mongoMiddlewareCustom]%s %s", data.RequestUri, request.ResponseBody)
} }
} }
} }
if c.logDebug { if c.logDebug {
c.zapLog.WithTraceId(ctx).Sugar().Infof("[golog.api.mongoRecordJson.data]%+v", data) c.zapLog.WithTraceId(ctx).Sugar().Infof("[golog.api.mongoMiddlewareCustom.data]%+v", data)
} }
err := c.mongoRecord(ctx, data) err := c.mongoRecord(ctx, data)
if err != nil { if err != nil {
c.zapLog.WithTraceId(ctx).Sugar().Errorf("[golog.api.MongoMiddlewareCustom]%s", err.Error()) c.zapLog.WithTraceId(ctx).Sugar().Errorf("[golog.api.mongoMiddlewareCustom]%s", err.Error())
} }
} }

@ -1,5 +1,5 @@
package golog package golog
const ( const (
Version = "1.0.73" Version = "1.0.87"
) )

143
vendor/go.dtapp.net/golog/gin.go generated vendored

@ -12,11 +12,11 @@ import (
"go.dtapp.net/gotime" "go.dtapp.net/gotime"
"go.dtapp.net/gotrace_id" "go.dtapp.net/gotrace_id"
"io/ioutil" "io/ioutil"
"net"
"os"
"runtime"
) )
// GinClientFun *GinClient 驱动
type GinClientFun func() *GinClient
// GinClient 框架 // GinClient 框架
type GinClient struct { type GinClient struct {
gormClient *dorm.GormClient // 数据库驱动 gormClient *dorm.GormClient // 数据库驱动
@ -24,52 +24,36 @@ type GinClient struct {
ipService *goip.Client // ip服务 ipService *goip.Client // ip服务
zapLog *ZapLog // 日志服务 zapLog *ZapLog // 日志服务
logDebug bool // 日志开关 logDebug bool // 日志开关
gormConfig struct { config struct {
systemHostName string // 主机名
systemInsideIp string // 内网ip
systemOs string // 系统类型
systemArch string // 系统架构
goVersion string // go版本
sdkVersion string // sdk版本
systemOutsideIp string // 外网ip
}
gormConfig struct {
stats bool // 状态
tableName string // 表名 tableName string // 表名
insideIp string // 内网ip
hostname string // 主机名
goVersion string // go版本
} }
mongoConfig struct { mongoConfig struct {
stats bool // 状态
databaseName string // 库名 databaseName string // 库名
collectionName string // 表名 collectionName string // 表名
insideIp string // 内网ip
hostname string // 主机名
goVersion string // go版本
}
log struct {
gorm bool // 日志开关
mongo bool // 日志开关
}
config struct {
os string // 系统类型
arch string // 系统架构
maxProCs int // CPU核数
} }
} }
// client 数据库服务
// string 表名
type ginGormClientFun func() (*dorm.GormClient, string)
// client 数据库服务
// string 库名
// string 表名
type ginMongoClientFun func() (*dorm.MongoClient, string, string)
// GinClientConfig 框架实例配置 // GinClientConfig 框架实例配置
type GinClientConfig struct { type GinClientConfig struct {
IpService *goip.Client // ip服务 IpService *goip.Client // ip服务
GormClientFun ginGormClientFun // 日志配置 GormClientFun dorm.GormClientTableFun // 日志配置
MongoClientFun apiMongoClientFun // 日志配置 MongoClientFun dorm.MongoClientCollectionFun // 日志配置
Debug bool // 日志开关 Debug bool // 日志开关
ZapLog *ZapLog // 日志服务 ZapLog *ZapLog // 日志服务
} }
// NewGinClient 创建框架实例化 // NewGinClient 创建框架实例化
// client 数据库服务
// tableName 表名
// ipService ip服务
func NewGinClient(config *GinClientConfig) (*GinClient, error) { func NewGinClient(config *GinClientConfig) (*GinClient, error) {
var ctx = context.Background() var ctx = context.Background()
@ -80,64 +64,50 @@ func NewGinClient(config *GinClientConfig) (*GinClient, error) {
c.logDebug = config.Debug c.logDebug = config.Debug
c.config.os = runtime.GOOS c.ipService = config.IpService
c.config.arch = runtime.GOARCH
c.config.maxProCs = runtime.GOMAXPROCS(0) // 配置信息
c.setConfig(ctx)
gormClient, gormTableName := config.GormClientFun() gormClient, gormTableName := config.GormClientFun()
mongoClient, mongoDatabaseName, mongoCollectionName := config.MongoClientFun() mongoClient, mongoDatabaseName, mongoCollectionName := config.MongoClientFun()
if (gormClient == nil || gormClient.Db == nil) || (mongoClient == nil || mongoClient.Db == nil) { if (gormClient == nil || gormClient.Db == nil) || (mongoClient == nil || mongoClient.Db == nil) {
return nil, errors.New("没有设置驱动") return nil, dbClientFunNoConfig
} }
hostname, _ := os.Hostname() // 配置关系数据库
if gormClient != nil || gormClient.Db != nil { if gormClient != nil || gormClient.Db != nil {
c.gormClient = gormClient c.gormClient = gormClient
if gormTableName == "" { if gormTableName == "" {
return nil, errors.New("没有设置表名") return nil, errors.New("没有设置表名")
} else {
c.gormConfig.tableName = gormTableName
} }
c.gormConfig.tableName = gormTableName
c.ipService = config.IpService c.gormAutoMigrate(ctx)
err := c.gormAutoMigrate()
if err != nil {
return nil, errors.New("创建表失败:" + err.Error())
}
c.gormConfig.hostname = hostname
c.gormConfig.insideIp = goip.GetInsideIp(ctx)
c.gormConfig.goVersion = runtime.Version()
c.log.gorm = true
c.gormConfig.stats = true
} }
// 配置非关系数据库
if mongoClient != nil || mongoClient.Db != nil { if mongoClient != nil || mongoClient.Db != nil {
c.mongoClient = mongoClient c.mongoClient = mongoClient
if mongoDatabaseName == "" { if mongoDatabaseName == "" {
return nil, errors.New("没有设置库名") return nil, errors.New("没有设置库名")
} else {
c.mongoConfig.databaseName = mongoDatabaseName
} }
c.mongoConfig.databaseName = mongoDatabaseName
if mongoCollectionName == "" { if mongoCollectionName == "" {
return nil, errors.New("没有设置表名") return nil, errors.New("没有设置表名")
} else {
c.mongoConfig.collectionName = mongoCollectionName
} }
c.mongoConfig.collectionName = mongoCollectionName
c.ipService = config.IpService
c.mongoConfig.hostname = hostname
c.mongoConfig.insideIp = goip.GetInsideIp(ctx)
c.mongoConfig.goVersion = runtime.Version()
c.log.mongo = true
// 创建时间序列集合 // 创建时间序列集合
c.mongoCreateCollection(ctx) c.mongoCreateCollection(ctx)
@ -145,6 +115,7 @@ func NewGinClient(config *GinClientConfig) (*GinClient, error) {
// 创建索引 // 创建索引
c.mongoCreateIndexes(ctx) c.mongoCreateIndexes(ctx)
c.mongoConfig.stats = true
} }
return c, nil return c, nil
@ -214,53 +185,49 @@ func (c *GinClient) Middleware() gin.HandlerFunc {
clientIp := gorequest.ClientIp(ginCtx.Request) clientIp := gorequest.ClientIp(ginCtx.Request)
requestClientIpCountry, requestClientIpRegion, requestClientIpProvince, requestClientIpCity, requestClientIpIsp := "", "", "", "", "" var requestClientIpCountry string
var requestClientIpProvince string
var requestClientIpCity string
var requestClientIpIsp string
var requestClientIpLocationLatitude float64
var requestClientIpLocationLongitude float64
if c.ipService != nil { if c.ipService != nil {
if net.ParseIP(clientIp).To4() != nil { info := c.ipService.Analyse(clientIp)
// IPv4 requestClientIpCountry = info.Country
_, info := c.ipService.Ipv4(clientIp) requestClientIpProvince = info.Province
requestClientIpCountry = info.Country requestClientIpCity = info.City
requestClientIpRegion = info.Region requestClientIpIsp = info.Isp
requestClientIpProvince = info.Province requestClientIpLocationLatitude = info.LocationLatitude
requestClientIpCity = info.City requestClientIpLocationLongitude = info.LocationLongitude
requestClientIpIsp = info.ISP
} else if net.ParseIP(clientIp).To16() != nil {
// IPv6
info := c.ipService.Ipv6(clientIp)
requestClientIpCountry = info.Country
requestClientIpProvince = info.Province
requestClientIpCity = info.City
}
} }
var traceId = gotrace_id.GetGinTraceId(ginCtx) var traceId = gotrace_id.GetGinTraceId(ginCtx)
// 记录 // 记录
if c.log.gorm { if c.gormConfig.stats {
if dataJson { if dataJson {
if c.logDebug { if c.logDebug {
c.zapLog.WithTraceIdStr(traceId).Sugar().Infof("[golog.gin.Middleware]准备使用{gormRecordJson}保存数据:%s", data) c.zapLog.WithTraceIdStr(traceId).Sugar().Infof("[golog.gin.Middleware]准备使用{gormRecordJson}保存数据:%s", data)
} }
c.gormRecordJson(ginCtx, traceId, requestTime, data, responseCode, responseBody, startTime, endTime, clientIp, requestClientIpCountry, requestClientIpRegion, requestClientIpProvince, requestClientIpCity, requestClientIpIsp) c.gormRecordJson(ginCtx, traceId, requestTime, data, responseCode, responseBody, startTime, endTime, clientIp, requestClientIpCountry, requestClientIpProvince, requestClientIpCity, requestClientIpIsp, requestClientIpLocationLatitude, requestClientIpLocationLongitude)
} else { } else {
if c.logDebug { if c.logDebug {
c.zapLog.WithTraceIdStr(traceId).Sugar().Infof("[golog.gin.Middleware]准备使用{gormRecordXml}保存数据:%s", data) c.zapLog.WithTraceIdStr(traceId).Sugar().Infof("[golog.gin.Middleware]准备使用{gormRecordXml}保存数据:%s", data)
} }
c.gormRecordXml(ginCtx, traceId, requestTime, data, responseCode, responseBody, startTime, endTime, clientIp, requestClientIpCountry, requestClientIpRegion, requestClientIpProvince, requestClientIpCity, requestClientIpIsp) c.gormRecordXml(ginCtx, traceId, requestTime, data, responseCode, responseBody, startTime, endTime, clientIp, requestClientIpCountry, requestClientIpProvince, requestClientIpCity, requestClientIpIsp, requestClientIpLocationLatitude, requestClientIpLocationLongitude)
} }
} }
// 记录 if c.mongoConfig.stats {
if c.log.mongo {
if dataJson { if dataJson {
if c.logDebug { if c.logDebug {
c.zapLog.WithTraceIdStr(traceId).Sugar().Infof("[golog.gin.Middleware]准备使用{mongoRecordJson}保存数据:%s", data) c.zapLog.WithTraceIdStr(traceId).Sugar().Infof("[golog.gin.Middleware]准备使用{mongoRecordJson}保存数据:%s", data)
} }
c.mongoRecordJson(ginCtx, traceId, requestTime, data, responseCode, responseBody, startTime, endTime, clientIp, requestClientIpCountry, requestClientIpRegion, requestClientIpProvince, requestClientIpCity, requestClientIpIsp) c.mongoRecordJson(ginCtx, traceId, requestTime, data, responseCode, responseBody, startTime, endTime, clientIp, requestClientIpCountry, requestClientIpProvince, requestClientIpCity, requestClientIpIsp, requestClientIpLocationLatitude, requestClientIpLocationLongitude)
} else { } else {
if c.logDebug { if c.logDebug {
c.zapLog.WithTraceIdStr(traceId).Sugar().Infof("[golog.gin.Middleware]准备使用{mongoRecordXml}保存数据:%s", data) c.zapLog.WithTraceIdStr(traceId).Sugar().Infof("[golog.gin.Middleware]准备使用{mongoRecordXml}保存数据:%s", data)
} }
c.mongoRecordXml(ginCtx, traceId, requestTime, data, responseCode, responseBody, startTime, endTime, clientIp, requestClientIpCountry, requestClientIpRegion, requestClientIpProvince, requestClientIpCity, requestClientIpIsp) c.mongoRecordXml(ginCtx, traceId, requestTime, data, responseCode, responseBody, startTime, endTime, clientIp, requestClientIpCountry, requestClientIpProvince, requestClientIpCity, requestClientIpIsp, requestClientIpLocationLatitude, requestClientIpLocationLongitude)
} }
} }
}() }()

@ -1,170 +1,102 @@
package golog package golog
import ( import (
"bytes"
"context" "context"
"encoding/json"
"errors"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"go.dtapp.net/dorm" "go.dtapp.net/dorm"
"go.dtapp.net/goip"
"go.dtapp.net/gorequest"
"go.dtapp.net/gotime" "go.dtapp.net/gotime"
"go.dtapp.net/gotrace_id"
"go.dtapp.net/gourl" "go.dtapp.net/gourl"
"gorm.io/gorm"
"io/ioutil"
"net"
"os"
"runtime"
"time" "time"
) )
// GinGormClientConfig 框架实例配置 // 模型
type GinGormClientConfig struct { type ginPostgresqlLog struct {
IpService *goip.Client // ip服务 LogId uint `gorm:"primaryKey;comment:【记录】编号" json:"log_id,omitempty"` //【记录】编号
GormClientFun ginGormClientFun // 日志配置 TraceId string `gorm:"index;comment:【系统】跟踪编号" json:"trace_id,omitempty"` //【系统】跟踪编号
Debug bool // 日志开关 RequestTime time.Time `gorm:"index;comment:【请求】时间" json:"request_time,omitempty"` //【请求】时间
ZapLog *ZapLog // 日志服务 RequestUri string `gorm:"comment:【请求】请求链接 域名+路径+参数" json:"request_uri,omitempty"` //【请求】请求链接 域名+路径+参数
} RequestUrl string `gorm:"comment:【请求】请求链接 域名+路径" json:"request_url,omitempty"` //【请求】请求链接 域名+路径
RequestApi string `gorm:"index;comment:【请求】请求接口 路径" json:"request_api,omitempty"` //【请求】请求接口 路径
// NewGinGormClient 创建框架实例化 RequestMethod string `gorm:"index;comment:【请求】请求方式" json:"request_method,omitempty"` //【请求】请求方式
// client 数据库服务 RequestProto string `gorm:"comment:【请求】请求协议" json:"request_proto,omitempty"` //【请求】请求协议
// tableName 表名 RequestUa string `gorm:"comment:【请求】请求UA" json:"request_ua,omitempty"` //【请求】请求UA
// ipService ip服务 RequestReferer string `gorm:"comment:【请求】请求referer" json:"request_referer,omitempty"` //【请求】请求referer
func NewGinGormClient(config *GinGormClientConfig) (*GinClient, error) { RequestBody string `gorm:"comment:【请求】请求主体" json:"request_body,omitempty"` //【请求】请求主体
RequestUrlQuery string `gorm:"comment:【请求】请求URL参数" json:"request_url_query,omitempty"` //【请求】请求URL参数
var ctx = context.Background() RequestIp string `gorm:"default:0.0.0.0;index;comment:【请求】请求客户端Ip" json:"request_ip,omitempty"` //【请求】请求客户端Ip
RequestIpCountry string `gorm:"index;comment:【请求】请求客户端城市" json:"request_ip_country,omitempty"` //【请求】请求客户端城市
c := &GinClient{} RequestIpProvince string `gorm:"index;comment:【请求】请求客户端省份" json:"request_ip_province,omitempty"` //【请求】请求客户端省份
RequestIpCity string `gorm:"index;comment:【请求】请求客户端城市" json:"request_ip_city,omitempty"` //【请求】请求客户端城市
c.zapLog = config.ZapLog RequestIpIsp string `gorm:"index;comment:【请求】请求客户端运营商" json:"request_ip_isp,omitempty"` //【请求】请求客户端运营商
RequestIpLongitude float64 `gorm:"index;comment:【请求】请求客户端经度" json:"request_ip_longitude,omitempty"` //【请求】请求客户端经度
c.logDebug = config.Debug RequestIpLatitude float64 `gorm:"index;comment:【请求】请求客户端纬度" json:"request_ip_latitude,omitempty"` //【请求】请求客户端纬度
RequestHeader string `gorm:"comment:【请求】请求头" json:"request_header,omitempty"` //【请求】请求头
client, tableName := config.GormClientFun() ResponseTime time.Time `gorm:"index;comment:【返回】时间" json:"response_time,omitempty"` //【返回】时间
ResponseCode int `gorm:"index;comment:【返回】状态码" json:"response_code,omitempty"` //【返回】状态码
if client == nil || client.Db == nil { ResponseMsg string `gorm:"comment:【返回】描述" json:"response_msg,omitempty"` //【返回】描述
return nil, errors.New("没有设置驱动") ResponseData string `gorm:"comment:【返回】数据" json:"response_data,omitempty"` //【返回】数据
} CostTime int64 `gorm:"comment:【系统】花费时间" json:"cost_time,omitempty"` //【系统】花费时间
SystemHostName string `gorm:"index;comment:【系统】主机名" json:"system_host_name,omitempty"` //【系统】主机名
c.gormClient = client SystemInsideIp string `gorm:"default:0.0.0.0;comment:【系统】内网ip" json:"system_inside_ip,omitempty"` //【系统】内网ip
SystemOs string `gorm:"index;comment:【系统】系统类型" json:"system_os,omitempty"` //【系统】系统类型
if tableName == "" { SystemArch string `gorm:"index;comment:【系统】系统架构" json:"system_arch,omitempty"` //【系统】系统架构
return nil, errors.New("没有设置表名") GoVersion string `gorm:"comment:【程序】Go版本" json:"go_version,omitempty"` //【程序】Go版本
} SdkVersion string `gorm:"comment:【程序】Sdk版本" json:"sdk_version,omitempty"` //【程序】Sdk版本
c.gormConfig.tableName = tableName
c.ipService = config.IpService
err := c.gormAutoMigrate()
if err != nil {
return nil, errors.New("创建表失败:" + err.Error())
}
hostname, _ := os.Hostname()
c.gormConfig.hostname = hostname
c.gormConfig.insideIp = goip.GetInsideIp(ctx)
c.gormConfig.goVersion = runtime.Version()
c.log.gorm = true
return c, nil
} }
// 创建模型 // 创建模型
func (c *GinClient) gormAutoMigrate() (err error) { func (c *GinClient) gormAutoMigrate(ctx context.Context) {
err = c.gormClient.Db.Table(c.gormConfig.tableName).AutoMigrate(&ginPostgresqlLog{}) err := c.gormClient.Db.Table(c.gormConfig.tableName).AutoMigrate(&ginPostgresqlLog{})
if err != nil { if err != nil {
c.zapLog.WithLogger().Sugar().Infof("[golog.gin.gormAutoMigrate]%s", err) c.zapLog.WithTraceId(ctx).Sugar().Errorf("创建模型:%s", err)
} }
return err
}
// 模型结构体
type ginPostgresqlLog struct {
LogId uint `gorm:"primaryKey;comment:【记录】编号" json:"log_id,omitempty"` //【记录】编号
TraceId string `gorm:"index;comment:【系统】跟踪编号" json:"trace_id,omitempty"` //【系统】跟踪编号
RequestTime time.Time `gorm:"index;comment:【请求】时间" json:"request_time,omitempty"` //【请求】时间
RequestUri string `gorm:"comment:【请求】请求链接 域名+路径+参数" json:"request_uri,omitempty"` //【请求】请求链接 域名+路径+参数
RequestUrl string `gorm:"comment:【请求】请求链接 域名+路径" json:"request_url,omitempty"` //【请求】请求链接 域名+路径
RequestApi string `gorm:"index;comment:【请求】请求接口 路径" json:"request_api,omitempty"` //【请求】请求接口 路径
RequestMethod string `gorm:"index;comment:【请求】请求方式" json:"request_method,omitempty"` //【请求】请求方式
RequestProto string `gorm:"comment:【请求】请求协议" json:"request_proto,omitempty"` //【请求】请求协议
RequestUa string `gorm:"comment:【请求】请求UA" json:"request_ua,omitempty"` //【请求】请求UA
RequestReferer string `gorm:"comment:【请求】请求referer" json:"request_referer,omitempty"` //【请求】请求referer
RequestBody string `gorm:"comment:【请求】请求主体" json:"request_body,omitempty"` //【请求】请求主体
RequestUrlQuery string `gorm:"comment:【请求】请求URL参数" json:"request_url_query,omitempty"` //【请求】请求URL参数
RequestIp string `gorm:"index;comment:【请求】请求客户端Ip" json:"request_ip,omitempty"` //【请求】请求客户端Ip
RequestIpCountry string `gorm:"index;comment:【请求】请求客户端城市" json:"request_ip_country,omitempty"` //【请求】请求客户端城市
RequestIpRegion string `gorm:"index;comment:【请求】请求客户端区域" json:"request_ip_region,omitempty"` //【请求】请求客户端区域
RequestIpProvince string `gorm:"index;comment:【请求】请求客户端省份" json:"request_ip_province,omitempty"` //【请求】请求客户端省份
RequestIpCity string `gorm:"index;comment:【请求】请求客户端城市" json:"request_ip_city,omitempty"` //【请求】请求客户端城市
RequestIpIsp string `gorm:"index;comment:【请求】请求客户端运营商" json:"request_ip_isp,omitempty"` //【请求】请求客户端运营商
RequestHeader string `gorm:"comment:【请求】请求头" json:"request_header,omitempty"` //【请求】请求头
ResponseTime time.Time `gorm:"index;comment:【返回】时间" json:"response_time,omitempty"` //【返回】时间
ResponseCode int `gorm:"index;comment:【返回】状态码" json:"response_code,omitempty"` //【返回】状态码
ResponseMsg string `gorm:"comment:【返回】描述" json:"response_msg,omitempty"` //【返回】描述
ResponseData string `gorm:"comment:【返回】数据" json:"response_data,omitempty"` //【返回】数据
CostTime int64 `gorm:"comment:【系统】花费时间" json:"cost_time,omitempty"` //【系统】花费时间
SystemHostName string `gorm:"index;comment:【系统】主机名" json:"system_host_name,omitempty"` //【系统】主机名
SystemInsideIp string `gorm:"index;comment:【系统】内网ip" json:"system_inside_ip,omitempty"` //【系统】内网ip
SystemOs string `gorm:"index;comment:【系统】系统类型" json:"system_os,omitempty"` //【系统】系统类型
SystemArch string `gorm:"index;comment:【系统】系统架构" json:"system_arch,omitempty"` //【系统】系统架构
SystemCpuQuantity int `gorm:"index;comment:【系统】CPU核数" json:"system_cpu_quantity,omitempty"` //【系统】CPU核数
GoVersion string `gorm:"index;comment:【程序】Go版本" json:"go_version,omitempty"` //【程序】Go版本
SdkVersion string `gorm:"index;comment:【程序】Sdk版本" json:"sdk_version,omitempty"` //【程序】Sdk版本
} }
// gormRecord 记录日志 // gormRecord 记录日志
func (c *GinClient) gormRecord(postgresqlLog ginPostgresqlLog) (err error) { func (c *GinClient) gormRecord(data ginPostgresqlLog) (err error) {
postgresqlLog.SystemHostName = c.gormConfig.hostname
postgresqlLog.SystemInsideIp = c.gormConfig.insideIp
postgresqlLog.GoVersion = c.gormConfig.goVersion
postgresqlLog.SdkVersion = Version
postgresqlLog.SystemOs = c.config.os data.SystemHostName = c.config.systemHostName //【系统】主机名
postgresqlLog.SystemArch = c.config.arch data.SystemInsideIp = c.config.systemInsideIp //【系统】内网ip
postgresqlLog.SystemCpuQuantity = c.config.maxProCs data.GoVersion = c.config.goVersion //【程序】Go版本
data.SdkVersion = c.config.sdkVersion //【程序】Sdk版本
data.SystemOs = c.config.systemOs //【系统】系统类型
data.SystemArch = c.config.systemArch //【系统】系统架构
err = c.gormClient.Db.Table(c.gormConfig.tableName).Create(&postgresqlLog).Error err = c.gormClient.Db.Table(c.gormConfig.tableName).Create(&data).Error
if err != nil { if err != nil {
c.zapLog.WithTraceIdStr(postgresqlLog.TraceId).Sugar().Errorf("[golog.gin.gormRecord]%s", err) c.zapLog.WithTraceIdStr(data.TraceId).Sugar().Errorf("记录日志失败:%s", err)
} }
return return
} }
func (c *GinClient) gormRecordJson(ginCtx *gin.Context, traceId string, requestTime time.Time, requestBody []byte, responseCode int, responseBody string, startTime, endTime int64, clientIp, requestClientIpCountry, requestClientIpRegion, requestClientIpProvince, requestClientIpCity, requestClientIpIsp string) { func (c *GinClient) gormRecordJson(ginCtx *gin.Context, traceId string, requestTime time.Time, requestBody []byte, responseCode int, responseBody string, startTime, endTime int64, clientIp, requestClientIpCountry, requestClientIpProvince, requestClientIpCity, requestClientIpIsp string, requestClientIpLocationLatitude, requestClientIpLocationLongitude float64) {
if c.logDebug { if c.logDebug {
c.zapLog.WithLogger().Sugar().Infof("[golog.gin.gormRecordJson]收到保存数据要求:%s", c.gormConfig.tableName) c.zapLog.WithLogger().Sugar().Infof("[golog.gin.gormRecordJson]收到保存数据要求:%s", c.gormConfig.tableName)
} }
data := ginPostgresqlLog{ data := ginPostgresqlLog{
TraceId: traceId, //【系统】跟踪编号 TraceId: traceId, //【系统】跟踪编号
RequestTime: requestTime, //【请求】时间 RequestTime: requestTime, //【请求】时间
RequestUrl: ginCtx.Request.RequestURI, //【请求】请求链接 RequestUrl: ginCtx.Request.RequestURI, //【请求】请求链接
RequestApi: gourl.UriFilterExcludeQueryString(ginCtx.Request.RequestURI), //【请求】请求接口 RequestApi: gourl.UriFilterExcludeQueryString(ginCtx.Request.RequestURI), //【请求】请求接口
RequestMethod: ginCtx.Request.Method, //【请求】请求方式 RequestMethod: ginCtx.Request.Method, //【请求】请求方式
RequestProto: ginCtx.Request.Proto, //【请求】请求协议 RequestProto: ginCtx.Request.Proto, //【请求】请求协议
RequestUa: ginCtx.Request.UserAgent(), //【请求】请求UA RequestUa: ginCtx.Request.UserAgent(), //【请求】请求UA
RequestReferer: ginCtx.Request.Referer(), //【请求】请求referer RequestReferer: ginCtx.Request.Referer(), //【请求】请求referer
RequestUrlQuery: dorm.JsonEncodeNoError(ginCtx.Request.URL.Query()), //【请求】请求URL参数 RequestUrlQuery: dorm.JsonEncodeNoError(ginCtx.Request.URL.Query()), //【请求】请求URL参数
RequestIp: clientIp, //【请求】请求客户端Ip RequestIp: clientIp, //【请求】请求客户端Ip
RequestIpCountry: requestClientIpCountry, //【请求】请求客户端城市 RequestIpCountry: requestClientIpCountry, //【请求】请求客户端城市
RequestIpRegion: requestClientIpRegion, //【请求】请求客户端区域 RequestIpProvince: requestClientIpProvince, //【请求】请求客户端省份
RequestIpProvince: requestClientIpProvince, //【请求】请求客户端省份 RequestIpCity: requestClientIpCity, //【请求】请求客户端城市
RequestIpCity: requestClientIpCity, //【请求】请求客户端城市 RequestIpIsp: requestClientIpIsp, //【请求】请求客户端运营商
RequestIpIsp: requestClientIpIsp, //【请求】请求客户端运营商 RequestIpLatitude: requestClientIpLocationLatitude, // 【请求】请求客户端纬度
RequestHeader: dorm.JsonEncodeNoError(ginCtx.Request.Header), //【请求】请求头 RequestIpLongitude: requestClientIpLocationLongitude, // 【请求】请求客户端经度
ResponseTime: gotime.Current().Time, //【返回】时间 RequestHeader: dorm.JsonEncodeNoError(ginCtx.Request.Header), //【请求】请求头
ResponseCode: responseCode, //【返回】状态码 ResponseTime: gotime.Current().Time, //【返回】时间
ResponseData: responseBody, //【返回】数据 ResponseCode: responseCode, //【返回】状态码
CostTime: endTime - startTime, //【系统】花费时间 ResponseData: responseBody, //【返回】数据
CostTime: endTime - startTime, //【系统】花费时间
} }
if ginCtx.Request.TLS == nil { if ginCtx.Request.TLS == nil {
data.RequestUri = "http://" + ginCtx.Request.Host + ginCtx.Request.RequestURI //【请求】请求链接 data.RequestUri = "http://" + ginCtx.Request.Host + ginCtx.Request.RequestURI //【请求】请求链接
@ -192,33 +124,34 @@ func (c *GinClient) gormRecordJson(ginCtx *gin.Context, traceId string, requestT
} }
} }
func (c *GinClient) gormRecordXml(ginCtx *gin.Context, traceId string, requestTime time.Time, requestBody []byte, responseCode int, responseBody string, startTime, endTime int64, clientIp, requestClientIpCountry, requestClientIpRegion, requestClientIpProvince, requestClientIpCity, requestClientIpIsp string) { func (c *GinClient) gormRecordXml(ginCtx *gin.Context, traceId string, requestTime time.Time, requestBody []byte, responseCode int, responseBody string, startTime, endTime int64, clientIp, requestClientIpCountry, requestClientIpProvince, requestClientIpCity, requestClientIpIsp string, requestClientIpLocationLatitude, requestClientIpLocationLongitude float64) {
if c.logDebug { if c.logDebug {
c.zapLog.WithLogger().Sugar().Infof("[golog.gin.gormRecordXml]收到保存数据要求:%s", c.gormConfig.tableName) c.zapLog.WithLogger().Sugar().Infof("[golog.gin.gormRecordXml]收到保存数据要求:%s", c.gormConfig.tableName)
} }
data := ginPostgresqlLog{ data := ginPostgresqlLog{
TraceId: traceId, //【系统】跟踪编号 TraceId: traceId, //【系统】跟踪编号
RequestTime: requestTime, //【请求】时间 RequestTime: requestTime, //【请求】时间
RequestUrl: ginCtx.Request.RequestURI, //【请求】请求链接 RequestUrl: ginCtx.Request.RequestURI, //【请求】请求链接
RequestApi: gourl.UriFilterExcludeQueryString(ginCtx.Request.RequestURI), //【请求】请求接口 RequestApi: gourl.UriFilterExcludeQueryString(ginCtx.Request.RequestURI), //【请求】请求接口
RequestMethod: ginCtx.Request.Method, //【请求】请求方式 RequestMethod: ginCtx.Request.Method, //【请求】请求方式
RequestProto: ginCtx.Request.Proto, //【请求】请求协议 RequestProto: ginCtx.Request.Proto, //【请求】请求协议
RequestUa: ginCtx.Request.UserAgent(), //【请求】请求UA RequestUa: ginCtx.Request.UserAgent(), //【请求】请求UA
RequestReferer: ginCtx.Request.Referer(), //【请求】请求referer RequestReferer: ginCtx.Request.Referer(), //【请求】请求referer
RequestUrlQuery: dorm.JsonEncodeNoError(ginCtx.Request.URL.Query()), //【请求】请求URL参数 RequestUrlQuery: dorm.JsonEncodeNoError(ginCtx.Request.URL.Query()), //【请求】请求URL参数
RequestIp: clientIp, //【请求】请求客户端Ip RequestIp: clientIp, //【请求】请求客户端Ip
RequestIpCountry: requestClientIpCountry, //【请求】请求客户端城市 RequestIpCountry: requestClientIpCountry, //【请求】请求客户端城市
RequestIpRegion: requestClientIpRegion, //【请求】请求客户端区域 RequestIpProvince: requestClientIpProvince, //【请求】请求客户端省份
RequestIpProvince: requestClientIpProvince, //【请求】请求客户端省份 RequestIpCity: requestClientIpCity, //【请求】请求客户端城市
RequestIpCity: requestClientIpCity, //【请求】请求客户端城市 RequestIpIsp: requestClientIpIsp, //【请求】请求客户端运营商
RequestIpIsp: requestClientIpIsp, //【请求】请求客户端运营商 RequestIpLatitude: requestClientIpLocationLatitude, // 【请求】请求客户端纬度
RequestHeader: dorm.JsonEncodeNoError(ginCtx.Request.Header), //【请求】请求头 RequestIpLongitude: requestClientIpLocationLongitude, // 【请求】请求客户端经度
ResponseTime: gotime.Current().Time, //【返回】时间 RequestHeader: dorm.JsonEncodeNoError(ginCtx.Request.Header), //【请求】请求头
ResponseCode: responseCode, //【返回】状态码 ResponseTime: gotime.Current().Time, //【返回】时间
ResponseData: responseBody, //【返回】数据 ResponseCode: responseCode, //【返回】状态码
CostTime: endTime - startTime, //【系统】花费时间 ResponseData: responseBody, //【返回】数据
CostTime: endTime - startTime, //【系统】花费时间
} }
if ginCtx.Request.TLS == nil { if ginCtx.Request.TLS == nil {
data.RequestUri = "http://" + ginCtx.Request.Host + ginCtx.Request.RequestURI //【请求】请求链接 data.RequestUri = "http://" + ginCtx.Request.Host + ginCtx.Request.RequestURI //【请求】请求链接
@ -247,100 +180,7 @@ func (c *GinClient) gormRecordXml(ginCtx *gin.Context, traceId string, requestTi
} }
} }
// GormQuery 查询
func (c *GinClient) GormQuery(ctx context.Context) *gorm.DB {
return c.gormClient.Db.Table(c.gormConfig.tableName)
}
// GormDelete 删除 // GormDelete 删除
func (c *GinClient) GormDelete(ctx context.Context, hour int64) error { func (c *GinClient) GormDelete(ctx context.Context, hour int64) error {
return c.gormClient.Db.Table(c.gormConfig.tableName).Where("request_time < ?", gotime.Current().BeforeHour(hour).Format()).Delete(&ginPostgresqlLog{}).Error return c.gormClient.Db.Table(c.gormConfig.tableName).Where("request_time < ?", gotime.Current().BeforeHour(hour).Format()).Delete(&ginPostgresqlLog{}).Error
} }
// GormMiddleware 中间件
func (c *GinClient) GormMiddleware() gin.HandlerFunc {
return func(ginCtx *gin.Context) {
// 开始时间
startTime := gotime.Current().TimestampWithMillisecond()
requestTime := gotime.Current().Time
// 获取
data, _ := ioutil.ReadAll(ginCtx.Request.Body)
if c.logDebug {
c.zapLog.WithLogger().Sugar().Infof("[golog.gin.GormMiddleware]%s", data)
}
// 复用
ginCtx.Request.Body = ioutil.NopCloser(bytes.NewBuffer(data))
blw := &bodyLogWriter{body: bytes.NewBufferString(""), ResponseWriter: ginCtx.Writer}
ginCtx.Writer = blw
// 处理请求
ginCtx.Next()
// 响应
responseCode := ginCtx.Writer.Status()
responseBody := blw.body.String()
//结束时间
endTime := gotime.Current().TimestampWithMillisecond()
go func() {
var dataJson = true
// 解析请求内容
var jsonBody map[string]interface{}
// 判断是否有内容
if len(data) > 0 {
err := json.Unmarshal(data, &jsonBody)
if err != nil {
dataJson = false
}
}
clientIp := gorequest.ClientIp(ginCtx.Request)
requestClientIpCountry, requestClientIpRegion, requestClientIpProvince, requestClientIpCity, requestClientIpIsp := "", "", "", "", ""
if c.ipService != nil {
if net.ParseIP(clientIp).To4() != nil {
// IPv4
_, info := c.ipService.Ipv4(clientIp)
requestClientIpCountry = info.Country
requestClientIpRegion = info.Region
requestClientIpProvince = info.Province
requestClientIpCity = info.City
requestClientIpIsp = info.ISP
} else if net.ParseIP(clientIp).To16() != nil {
// IPv6
info := c.ipService.Ipv6(clientIp)
requestClientIpCountry = info.Country
requestClientIpProvince = info.Province
requestClientIpCity = info.City
}
}
// 记录
if c.gormClient != nil && c.gormClient.Db != nil {
var traceId = gotrace_id.GetGinTraceId(ginCtx)
if dataJson {
if c.logDebug {
c.zapLog.WithTraceIdStr(traceId).Sugar().Infof("[golog.gin.GormMiddleware]准备使用{gormRecordJson}保存数据:%s", data)
}
c.gormRecordJson(ginCtx, traceId, requestTime, data, responseCode, responseBody, startTime, endTime, clientIp, requestClientIpCountry, requestClientIpRegion, requestClientIpProvince, requestClientIpCity, requestClientIpIsp)
} else {
if c.logDebug {
c.zapLog.WithTraceIdStr(traceId).Sugar().Infof("[golog.gin.GormMiddleware]准备使用{gormRecordXml}保存数据:%s", data)
}
c.gormRecordXml(ginCtx, traceId, requestTime, data, responseCode, responseBody, startTime, endTime, clientIp, requestClientIpCountry, requestClientIpRegion, requestClientIpProvince, requestClientIpCity, requestClientIpIsp)
}
}
}()
}
}

@ -1,14 +1,9 @@
package golog package golog
import ( import (
"bytes"
"context" "context"
"encoding/json"
"errors"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"go.dtapp.net/dorm" "go.dtapp.net/dorm"
"go.dtapp.net/goip"
"go.dtapp.net/gorequest"
"go.dtapp.net/gotime" "go.dtapp.net/gotime"
"go.dtapp.net/gotrace_id" "go.dtapp.net/gotrace_id"
"go.dtapp.net/gourl" "go.dtapp.net/gourl"
@ -16,230 +11,167 @@ import (
"go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options" "go.mongodb.org/mongo-driver/mongo/options"
"io/ioutil"
"net"
"os"
"runtime"
"time" "time"
) )
// GinMongoClientConfig 框架实例配置 type ginMongoLogRequestIpLocationLocation struct {
type GinMongoClientConfig struct { Type string `json:"type,omitempty" bson:"type,omitempty"` // GeoJSON类型
IpService *goip.Client // ip服务 Coordinates []float64 `json:"coordinates,omitempty" bson:"coordinates,omitempty"` // 经度,纬度
MongoClientFun ginMongoClientFun // 日志配置
Debug bool // 日志开关
ZapLog *ZapLog // 日志服务
} }
// NewGinMongoClient 创建框架实例化 // 模型结构体
// client 数据库服务 type ginMongoLog struct {
// databaseName 库名 LogId primitive.ObjectID `json:"log_id,omitempty" bson:"_id,omitempty"` //【记录】编号
// collectionName 表名 LogTime primitive.DateTime `json:"log_time,omitempty" bson:"log_time"` //【记录】时间
// ipService ip服务 TraceId string `json:"trace_id,omitempty" bson:"trace_id,omitempty"` //【记录】跟踪编号
func NewGinMongoClient(config *GinMongoClientConfig) (*GinClient, error) { RequestTime dorm.BsonTime `json:"request_time,omitempty" bson:"request_time,omitempty"` //【请求】时间
RequestUri string `json:"request_uri,omitempty" bson:"request_uri,omitempty"` //【请求】请求链接 域名+路径+参数
var ctx = context.Background() RequestUrl string `json:"request_url,omitempty" bson:"request_url,omitempty"` //【请求】请求链接 域名+路径
RequestApi string `json:"request_api,omitempty" bson:"request_api,omitempty"` //【请求】请求接口 路径
c := &GinClient{} RequestMethod string `json:"request_method,omitempty" bson:"request_method,omitempty"` //【请求】请求方式
RequestProto string `json:"request_proto,omitempty" bson:"request_proto,omitempty"` //【请求】请求协议
c.zapLog = config.ZapLog RequestUa string `json:"request_ua,omitempty" bson:"request_ua,omitempty"` //【请求】请求UA
RequestReferer string `json:"request_referer,omitempty" bson:"request_referer,omitempty"` //【请求】请求referer
c.logDebug = config.Debug RequestBody interface{} `json:"request_body,omitempty" bson:"request_body,omitempty"` //【请求】请求主体
RequestUrlQuery interface{} `json:"request_url_query,omitempty" bson:"request_url_query,omitempty"` //【请求】请求URL参数
client, databaseName, collectionName := config.MongoClientFun() RequestIp string `json:"request_ip,omitempty" bson:"request_ip,omitempty"` //【请求】请求客户端Ip
RequestIpCountry string `json:"request_ip_country,omitempty" bson:"request_ip_country,omitempty"` //【请求】请求客户端国家
if client == nil || client.Db == nil { RequestIpProvince string `json:"request_ip_province,omitempty" bson:"request_ip_province,omitempty"` //【请求】请求客户端省份
return nil, errors.New("没有设置驱动") RequestIpCity string `json:"request_ip_city,omitempty" bson:"request_ip_city,omitempty"` //【请求】请求客户端城市
} RequestIpIsp string `json:"request_ip_isp,omitempty" bson:"request_ip_isp,omitempty"` //【请求】请求客户端运营商
RequestIpLocation ginMongoLogRequestIpLocationLocation `json:"request_ip_location,omitempty" bson:"request_ip_location,omitempty"` //【请求】请求客户端位置
c.mongoClient = client RequestHeader interface{} `json:"request_header,omitempty" bson:"request_header,omitempty"` //【请求】请求头
ResponseTime dorm.BsonTime `json:"response_time,omitempty" bson:"response_time,omitempty"` //【返回】时间
if databaseName == "" { ResponseCode int `json:"response_code,omitempty" bson:"response_code,omitempty"` //【返回】状态码
return nil, errors.New("没有设置库名") ResponseMsg string `json:"response_msg,omitempty" bson:"response_msg,omitempty"` //【返回】描述
} ResponseData interface{} `json:"response_data,omitempty" bson:"response_data,omitempty"` //【返回】数据
c.mongoConfig.databaseName = databaseName CostTime int64 `json:"cost_time,omitempty" bson:"cost_time,omitempty"` //【系统】花费时间
SystemHostName string `json:"system_host_name,omitempty" bson:"system_host_name,omitempty"` //【系统】主机名
if collectionName == "" { SystemInsideIp string `json:"system_inside_ip,omitempty" bson:"system_inside_ip,omitempty"` //【系统】内网ip
return nil, errors.New("没有设置表名") SystemOs string `json:"system_os,omitempty" bson:"system_os,omitempty"` //【系统】系统类型
} SystemArch string `json:"system_arch,omitempty" bson:"system_arch,omitempty"` //【系统】系统架构
c.mongoConfig.collectionName = collectionName GoVersion string `json:"go_version,omitempty" bson:"go_version,omitempty"` //【程序】Go版本
SdkVersion string `json:"sdk_version,omitempty" bson:"sdk_version,omitempty"` //【程序】Sdk版本
c.ipService = config.IpService
hostname, _ := os.Hostname()
c.mongoConfig.hostname = hostname
c.mongoConfig.insideIp = goip.GetInsideIp(ctx)
c.mongoConfig.goVersion = runtime.Version()
c.log.mongo = true
// 创建时间序列集合
c.mongoCreateCollection(ctx)
// 创建索引
c.mongoCreateIndexes(ctx)
return c, nil
} }
// 创建时间序列集合 // 创建集合
func (c *GinClient) mongoCreateCollection(ctx context.Context) { func (c *GinClient) mongoCreateCollection(ctx context.Context) {
var commandResult bson.M err := c.mongoClient.Database(c.mongoConfig.databaseName).CreateCollection(ctx, c.mongoConfig.collectionName, options.CreateCollection().SetCollation(&options.Collation{
commandErr := c.mongoClient.Db.Database(c.mongoConfig.databaseName).RunCommand(ctx, bson.D{{ Locale: "request_time",
"listCollections", 1, Strength: -1,
}}).Decode(&commandResult) }))
if commandErr != nil { if err != nil {
c.zapLog.WithLogger().Sugar().Error("检查时间序列集合:", commandErr) c.zapLog.WithTraceId(ctx).Sugar().Error("创建集合:", err)
} else {
err := c.mongoClient.Db.Database(c.mongoConfig.databaseName).CreateCollection(ctx, c.mongoConfig.collectionName, options.CreateCollection().SetTimeSeriesOptions(options.TimeSeries().SetTimeField("log_time")))
if err != nil {
c.zapLog.WithLogger().Sugar().Error("创建时间序列集合:", err)
}
} }
} }
// 创建索引 // 创建索引
func (c *GinClient) mongoCreateIndexes(ctx context.Context) { func (c *GinClient) mongoCreateIndexes(ctx context.Context) {
c.zapLog.WithLogger().Sugar().Infof(c.mongoClient.Db.Database(c.mongoConfig.databaseName).Collection(c.mongoConfig.collectionName).Indexes().CreateOne(ctx, mongo.IndexModel{ indexes, err := c.mongoClient.Database(c.mongoConfig.databaseName).Collection(c.mongoConfig.collectionName).CreateManyIndexes(ctx, []mongo.IndexModel{
Keys: bson.D{ {
{"trace_id", 1}, Keys: bson.D{{
}})) Key: "trace_id",
c.zapLog.WithLogger().Sugar().Infof(c.mongoClient.Db.Database(c.mongoConfig.databaseName).Collection(c.mongoConfig.collectionName).Indexes().CreateOne(ctx, mongo.IndexModel{ Value: -1,
Keys: bson.D{ }},
{"log_time", -1}, }, {
}})) Keys: bson.D{{
c.zapLog.WithLogger().Sugar().Infof(c.mongoClient.Db.Database(c.mongoConfig.databaseName).Collection(c.mongoConfig.collectionName).Indexes().CreateOne(ctx, mongo.IndexModel{ Key: "request_method",
Keys: bson.D{ Value: 1,
{"request_time", -1}, }},
}})) }, {
c.zapLog.WithLogger().Sugar().Infof(c.mongoClient.Db.Database(c.mongoConfig.databaseName).Collection(c.mongoConfig.collectionName).Indexes().CreateOne(ctx, mongo.IndexModel{ Keys: bson.D{{
Keys: bson.D{ Key: "request_ip",
{"request_method", 1}, Value: 1,
}})) }},
c.zapLog.WithLogger().Sugar().Infof(c.mongoClient.Db.Database(c.mongoConfig.databaseName).Collection(c.mongoConfig.collectionName).Indexes().CreateOne(ctx, mongo.IndexModel{ }, {
Keys: bson.D{ Keys: bson.D{{
{"request_proto", 1}, Key: "request_ip_country",
}})) Value: 1,
c.zapLog.WithLogger().Sugar().Infof(c.mongoClient.Db.Database(c.mongoConfig.databaseName).Collection(c.mongoConfig.collectionName).Indexes().CreateOne(ctx, mongo.IndexModel{ }},
Keys: bson.D{ }, {
{"request_ip", 1}, Keys: bson.D{{
}})) Key: "request_ip_province",
c.zapLog.WithLogger().Sugar().Infof(c.mongoClient.Db.Database(c.mongoConfig.databaseName).Collection(c.mongoConfig.collectionName).Indexes().CreateOne(ctx, mongo.IndexModel{ Value: 1,
Keys: bson.D{ }},
{"request_ip_country", 1}, }, {
}})) Keys: bson.D{{
c.zapLog.WithLogger().Sugar().Infof(c.mongoClient.Db.Database(c.mongoConfig.databaseName).Collection(c.mongoConfig.collectionName).Indexes().CreateOne(ctx, mongo.IndexModel{ Key: "request_ip_city",
Keys: bson.D{ Value: 1,
{"request_ip_region", 1}, }},
}})) }, {
c.zapLog.WithLogger().Sugar().Infof(c.mongoClient.Db.Database(c.mongoConfig.databaseName).Collection(c.mongoConfig.collectionName).Indexes().CreateOne(ctx, mongo.IndexModel{ Keys: bson.D{{
Keys: bson.D{ Key: "request_ip_isp",
{"request_ip_province", 1}, Value: 1,
}})) }},
c.zapLog.WithLogger().Sugar().Infof(c.mongoClient.Db.Database(c.mongoConfig.databaseName).Collection(c.mongoConfig.collectionName).Indexes().CreateOne(ctx, mongo.IndexModel{ }, {
Keys: bson.D{ Keys: bson.D{{
{"request_ip_city", 1}, Key: "response_time",
}})) Value: -1,
c.zapLog.WithLogger().Sugar().Infof(c.mongoClient.Db.Database(c.mongoConfig.databaseName).Collection(c.mongoConfig.collectionName).Indexes().CreateOne(ctx, mongo.IndexModel{ }},
Keys: bson.D{ }, {
{"request_ip_isp", 1}, Keys: bson.D{{
}})) Key: "response_code",
c.zapLog.WithLogger().Sugar().Infof(c.mongoClient.Db.Database(c.mongoConfig.databaseName).Collection(c.mongoConfig.collectionName).Indexes().CreateOne(ctx, mongo.IndexModel{ Value: 1,
Keys: bson.D{ }},
{"response_time", -1}, }, {
}})) Keys: bson.D{{
c.zapLog.WithLogger().Sugar().Infof(c.mongoClient.Db.Database(c.mongoConfig.databaseName).Collection(c.mongoConfig.collectionName).Indexes().CreateOne(ctx, mongo.IndexModel{ Key: "system_host_name",
Keys: bson.D{ Value: 1,
{"response_code", 1}, }},
}})) }, {
c.zapLog.WithLogger().Sugar().Infof(c.mongoClient.Db.Database(c.mongoConfig.databaseName).Collection(c.mongoConfig.collectionName).Indexes().CreateOne(ctx, mongo.IndexModel{ Keys: bson.D{{
Keys: bson.D{ Key: "system_os",
{"system_host_name", 1}, Value: 1,
}})) }},
c.zapLog.WithLogger().Sugar().Infof(c.mongoClient.Db.Database(c.mongoConfig.databaseName).Collection(c.mongoConfig.collectionName).Indexes().CreateOne(ctx, mongo.IndexModel{ }, {
Keys: bson.D{ Keys: bson.D{{
{"system_inside_ip", 1}, Key: "system_arch",
}})) Value: -1,
c.zapLog.WithLogger().Sugar().Infof(c.mongoClient.Db.Database(c.mongoConfig.databaseName).Collection(c.mongoConfig.collectionName).Indexes().CreateOne(ctx, mongo.IndexModel{ }},
Keys: bson.D{ }, {
{"system_os", -1}, Keys: bson.D{{
}})) Key: "go_version",
c.zapLog.WithLogger().Sugar().Infof(c.mongoClient.Db.Database(c.mongoConfig.databaseName).Collection(c.mongoConfig.collectionName).Indexes().CreateOne(ctx, mongo.IndexModel{ Value: -1,
Keys: bson.D{ }},
{"system_arch", -1}, }, {
}})) Keys: bson.D{{
c.zapLog.WithLogger().Sugar().Infof(c.mongoClient.Db.Database(c.mongoConfig.databaseName).Collection(c.mongoConfig.collectionName).Indexes().CreateOne(ctx, mongo.IndexModel{ Key: "sdk_version",
Keys: bson.D{ Value: -1,
{"system_cpu_quantity", 1}, }},
}})) }, {
c.zapLog.WithLogger().Sugar().Infof(c.mongoClient.Db.Database(c.mongoConfig.databaseName).Collection(c.mongoConfig.collectionName).Indexes().CreateOne(ctx, mongo.IndexModel{ Keys: bson.D{{
Keys: bson.D{ Key: "request_ip_location",
{"go_version", -1}, Value: "2dsphere",
}})) }},
c.zapLog.WithLogger().Sugar().Infof(c.mongoClient.Db.Database(c.mongoConfig.databaseName).Collection(c.mongoConfig.collectionName).Indexes().CreateOne(ctx, mongo.IndexModel{ },
Keys: bson.D{ })
{"sdk_version", -1}, if err != nil {
}})) c.zapLog.WithTraceId(ctx).Sugar().Errorf("创建索引:%s", err)
} }
c.zapLog.WithTraceId(ctx).Sugar().Infof("创建索引:%s", indexes)
// 模型结构体
type ginMongoLog struct {
LogId primitive.ObjectID `json:"log_id,omitempty" bson:"_id,omitempty"` //【记录】编号
LogTime primitive.DateTime `json:"log_time,omitempty" bson:"log_time,omitempty"` //【记录】时间
TraceId string `json:"trace_id,omitempty" bson:"trace_id,omitempty"` //【记录】跟踪编号
RequestTime dorm.BsonTime `json:"request_time,omitempty" bson:"request_time,omitempty"` //【请求】时间
RequestUri string `json:"request_uri,omitempty" bson:"request_uri,omitempty"` //【请求】请求链接 域名+路径+参数
RequestUrl string `json:"request_url,omitempty" bson:"request_url,omitempty"` //【请求】请求链接 域名+路径
RequestApi string `json:"request_api,omitempty" bson:"request_api,omitempty"` //【请求】请求接口 路径
RequestMethod string `json:"request_method,omitempty" bson:"request_method,omitempty"` //【请求】请求方式
RequestProto string `json:"request_proto,omitempty" bson:"request_proto,omitempty"` //【请求】请求协议
RequestUa string `json:"request_ua,omitempty" bson:"request_ua,omitempty"` //【请求】请求UA
RequestReferer string `json:"request_referer,omitempty" bson:"request_referer,omitempty"` //【请求】请求referer
RequestBody interface{} `json:"request_body,omitempty" bson:"request_body,omitempty"` //【请求】请求主体
RequestUrlQuery interface{} `json:"request_url_query,omitempty" bson:"request_url_query,omitempty"` //【请求】请求URL参数
RequestIp string `json:"request_ip,omitempty" bson:"request_ip,omitempty"` //【请求】请求客户端Ip
RequestIpCountry string `json:"request_ip_country,omitempty" bson:"request_ip_country,omitempty"` //【请求】请求客户端城市
RequestIpRegion string `json:"request_ip_region,omitempty" bson:"request_ip_region,omitempty"` //【请求】请求客户端区域
RequestIpProvince string `json:"request_ip_province,omitempty" bson:"request_ip_province,omitempty"` //【请求】请求客户端省份
RequestIpCity string `json:"request_ip_city,omitempty" bson:"request_ip_city,omitempty"` //【请求】请求客户端城市
RequestIpIsp string `json:"request_ip_isp,omitempty" bson:"request_ip_isp,omitempty"` //【请求】请求客户端运营商
RequestHeader interface{} `json:"request_header,omitempty" bson:"request_header,omitempty"` //【请求】请求头
ResponseTime dorm.BsonTime `json:"response_time,omitempty" bson:"response_time,omitempty"` //【返回】时间
ResponseCode int `json:"response_code,omitempty" bson:"response_code,omitempty"` //【返回】状态码
ResponseMsg string `json:"response_msg,omitempty" bson:"response_msg,omitempty"` //【返回】描述
ResponseData interface{} `json:"response_data,omitempty" bson:"response_data,omitempty"` //【返回】数据
CostTime int64 `json:"cost_time,omitempty" bson:"cost_time,omitempty"` //【系统】花费时间
SystemHostName string `json:"system_host_name,omitempty" bson:"system_host_name,omitempty"` //【系统】主机名
SystemInsideIp string `json:"system_inside_ip,omitempty" bson:"system_inside_ip,omitempty"` //【系统】内网ip
SystemOs string `json:"system_os,omitempty" bson:"system_os,omitempty"` //【系统】系统类型
SystemArch string `json:"system_arch,omitempty" bson:"system_arch,omitempty"` //【系统】系统架构
SystemCpuQuantity int `json:"system_cpu_quantity,omitempty" bson:"system_cpu_quantity,omitempty"` //【系统】CPU核数
GoVersion string `json:"go_version,omitempty" bson:"go_version,omitempty"` //【程序】Go版本
SdkVersion string `json:"sdk_version,omitempty" bson:"sdk_version,omitempty"` //【程序】Sdk版本
} }
// 记录日志 // 记录日志
func (c *GinClient) mongoRecord(mongoLog ginMongoLog) (err error) { func (c *GinClient) mongoRecord(ctx context.Context, mongoLog ginMongoLog) (err error) {
mongoLog.SystemHostName = c.mongoConfig.hostname //【系统】主机名 mongoLog.SystemHostName = c.config.systemHostName //【系统】主机名
mongoLog.SystemInsideIp = c.mongoConfig.insideIp //【系统】内网ip mongoLog.SystemInsideIp = c.config.systemInsideIp //【系统】内网ip
mongoLog.GoVersion = c.mongoConfig.goVersion //【程序】Go版本 mongoLog.GoVersion = c.config.goVersion //【程序】Go版本
mongoLog.SdkVersion = Version //【程序】Sdk版本 mongoLog.SdkVersion = c.config.sdkVersion //【程序】Sdk版本
mongoLog.SystemOs = c.config.os //【系统】系统类型 mongoLog.SystemOs = c.config.systemOs //【系统】系统类型
mongoLog.SystemArch = c.config.arch //【系统】系统架构 mongoLog.SystemArch = c.config.systemArch //【系统】系统架构
mongoLog.SystemCpuQuantity = c.config.maxProCs //【系统】CPU核数 mongoLog.LogId = primitive.NewObjectID() //【记录】编号
mongoLog.LogId = primitive.NewObjectID() //【记录】编号
_, err = c.mongoClient.Database(c.mongoConfig.databaseName).Collection(c.mongoConfig.collectionName).InsertOne(mongoLog) _, err = c.mongoClient.Database(c.mongoConfig.databaseName).Collection(c.mongoConfig.collectionName).InsertOne(ctx, mongoLog)
if err != nil { if err != nil {
c.zapLog.WithTraceIdStr(mongoLog.TraceId).Sugar().Errorf("[golog.gin.mongoRecord]%s", err) c.zapLog.WithTraceIdStr(mongoLog.TraceId).Sugar().Errorf("记录日志失败:%s", err)
} }
return err return err
} }
func (c *GinClient) mongoRecordJson(ginCtx *gin.Context, traceId string, requestTime time.Time, requestBody []byte, responseCode int, responseBody string, startTime, endTime int64, clientIp, requestClientIpCountry, requestClientIpRegion, requestClientIpProvince, requestClientIpCity, requestClientIpIsp string) { func (c *GinClient) mongoRecordJson(ginCtx *gin.Context, traceId string, requestTime time.Time, requestBody []byte, responseCode int, responseBody string, startTime, endTime int64, clientIp, requestClientIpCountry, requestClientIpProvince, requestClientIpCity, requestClientIpIsp string, requestClientIpLocationLatitude, requestClientIpLocationLongitude float64) {
var ctx = gotrace_id.SetGinTraceIdContext(context.Background(), ginCtx)
if c.logDebug { if c.logDebug {
c.zapLog.WithLogger().Sugar().Infof("[golog.gin.mongoRecordJson]收到保存数据要求:%s,%s", c.mongoConfig.databaseName, c.mongoConfig.collectionName) c.zapLog.WithLogger().Sugar().Infof("[golog.gin.mongoRecordJson]收到保存数据要求:%s,%s", c.mongoConfig.databaseName, c.mongoConfig.collectionName)
@ -248,7 +180,7 @@ func (c *GinClient) mongoRecordJson(ginCtx *gin.Context, traceId string, request
data := ginMongoLog{ data := ginMongoLog{
TraceId: traceId, //【记录】跟踪编号 TraceId: traceId, //【记录】跟踪编号
LogTime: primitive.NewDateTimeFromTime(requestTime), //【记录】时间 LogTime: primitive.NewDateTimeFromTime(requestTime), //【记录】时间
RequestTime: dorm.BsonTime(requestTime), //【请求】时间 RequestTime: dorm.NewBsonTimeFromTime(requestTime), //【请求】时间
RequestUrl: ginCtx.Request.RequestURI, //【请求】请求链接 RequestUrl: ginCtx.Request.RequestURI, //【请求】请求链接
RequestApi: gourl.UriFilterExcludeQueryString(ginCtx.Request.RequestURI), //【请求】请求接口 RequestApi: gourl.UriFilterExcludeQueryString(ginCtx.Request.RequestURI), //【请求】请求接口
RequestMethod: ginCtx.Request.Method, //【请求】请求方式 RequestMethod: ginCtx.Request.Method, //【请求】请求方式
@ -257,13 +189,12 @@ func (c *GinClient) mongoRecordJson(ginCtx *gin.Context, traceId string, request
RequestReferer: ginCtx.Request.Referer(), //【请求】请求referer RequestReferer: ginCtx.Request.Referer(), //【请求】请求referer
RequestUrlQuery: ginCtx.Request.URL.Query(), //【请求】请求URL参数 RequestUrlQuery: ginCtx.Request.URL.Query(), //【请求】请求URL参数
RequestIp: clientIp, //【请求】请求客户端Ip RequestIp: clientIp, //【请求】请求客户端Ip
RequestIpCountry: requestClientIpCountry, //【请求】请求客户端城市 RequestIpCountry: requestClientIpCountry, //【请求】请求客户端国家
RequestIpRegion: requestClientIpRegion, //【请求】请求客户端区域
RequestIpProvince: requestClientIpProvince, //【请求】请求客户端省份 RequestIpProvince: requestClientIpProvince, //【请求】请求客户端省份
RequestIpCity: requestClientIpCity, //【请求】请求客户端城市 RequestIpCity: requestClientIpCity, //【请求】请求客户端城市
RequestIpIsp: requestClientIpIsp, //【请求】请求客户端运营商 RequestIpIsp: requestClientIpIsp, //【请求】请求客户端运营商
RequestHeader: ginCtx.Request.Header, //【请求】请求头 RequestHeader: ginCtx.Request.Header, //【请求】请求头
ResponseTime: dorm.BsonTime(gotime.Current().Time), //【返回】时间 ResponseTime: dorm.NewBsonTimeCurrent(), //【返回】时间
ResponseCode: responseCode, //【返回】状态码 ResponseCode: responseCode, //【返回】状态码
ResponseData: c.jsonUnmarshal(responseBody), //【返回】数据 ResponseData: c.jsonUnmarshal(responseBody), //【返回】数据
CostTime: endTime - startTime, //【系统】花费时间 CostTime: endTime - startTime, //【系统】花费时间
@ -282,17 +213,26 @@ func (c *GinClient) mongoRecordJson(ginCtx *gin.Context, traceId string, request
} }
} }
if requestClientIpLocationLatitude != 0 && requestClientIpLocationLongitude != 0 {
data.RequestIpLocation = ginMongoLogRequestIpLocationLocation{
Type: "Point",
Coordinates: []float64{requestClientIpLocationLongitude, requestClientIpLocationLatitude},
}
}
if c.logDebug { if c.logDebug {
c.zapLog.WithTraceIdStr(traceId).Sugar().Infof("[golog.gin.mongoRecordJson.data]%+v", data) c.zapLog.WithTraceIdStr(traceId).Sugar().Infof("[golog.gin.mongoRecordJson.data]%+v", data)
} }
err := c.mongoRecord(data) err := c.mongoRecord(ctx, data)
if err != nil { if err != nil {
c.zapLog.WithTraceIdStr(traceId).Sugar().Errorf("[golog.gin.mongoRecordJson]%s", err) c.zapLog.WithTraceIdStr(traceId).Sugar().Errorf("[golog.gin.mongoRecordJson]%s", err)
} }
} }
func (c *GinClient) mongoRecordXml(ginCtx *gin.Context, traceId string, requestTime time.Time, requestBody []byte, responseCode int, responseBody string, startTime, endTime int64, clientIp, requestClientIpCountry, requestClientIpRegion, requestClientIpProvince, requestClientIpCity, requestClientIpIsp string) { func (c *GinClient) mongoRecordXml(ginCtx *gin.Context, traceId string, requestTime time.Time, requestBody []byte, responseCode int, responseBody string, startTime, endTime int64, clientIp, requestClientIpCountry, requestClientIpProvince, requestClientIpCity, requestClientIpIsp string, requestClientIpLocationLatitude, requestClientIpLocationLongitude float64) {
var ctx = gotrace_id.SetGinTraceIdContext(context.Background(), ginCtx)
if c.logDebug { if c.logDebug {
c.zapLog.WithLogger().Sugar().Infof("[golog.gin.mongoRecordXml]收到保存数据要求:%s,%s", c.mongoConfig.databaseName, c.mongoConfig.collectionName) c.zapLog.WithLogger().Sugar().Infof("[golog.gin.mongoRecordXml]收到保存数据要求:%s,%s", c.mongoConfig.databaseName, c.mongoConfig.collectionName)
@ -301,7 +241,7 @@ func (c *GinClient) mongoRecordXml(ginCtx *gin.Context, traceId string, requestT
data := ginMongoLog{ data := ginMongoLog{
TraceId: traceId, //【记录】跟踪编号 TraceId: traceId, //【记录】跟踪编号
LogTime: primitive.NewDateTimeFromTime(requestTime), //【记录】时间 LogTime: primitive.NewDateTimeFromTime(requestTime), //【记录】时间
RequestTime: dorm.BsonTime(requestTime), //【请求】时间 RequestTime: dorm.NewBsonTimeFromTime(requestTime), //【请求】时间
RequestUrl: ginCtx.Request.RequestURI, //【请求】请求链接 RequestUrl: ginCtx.Request.RequestURI, //【请求】请求链接
RequestApi: gourl.UriFilterExcludeQueryString(ginCtx.Request.RequestURI), //【请求】请求接口 RequestApi: gourl.UriFilterExcludeQueryString(ginCtx.Request.RequestURI), //【请求】请求接口
RequestMethod: ginCtx.Request.Method, //【请求】请求方式 RequestMethod: ginCtx.Request.Method, //【请求】请求方式
@ -310,13 +250,12 @@ func (c *GinClient) mongoRecordXml(ginCtx *gin.Context, traceId string, requestT
RequestReferer: ginCtx.Request.Referer(), //【请求】请求referer RequestReferer: ginCtx.Request.Referer(), //【请求】请求referer
RequestUrlQuery: ginCtx.Request.URL.Query(), //【请求】请求URL参数 RequestUrlQuery: ginCtx.Request.URL.Query(), //【请求】请求URL参数
RequestIp: clientIp, //【请求】请求客户端Ip RequestIp: clientIp, //【请求】请求客户端Ip
RequestIpCountry: requestClientIpCountry, //【请求】请求客户端城市 RequestIpCountry: requestClientIpCountry, //【请求】请求客户端国家
RequestIpRegion: requestClientIpRegion, //【请求】请求客户端区域
RequestIpProvince: requestClientIpProvince, //【请求】请求客户端省份 RequestIpProvince: requestClientIpProvince, //【请求】请求客户端省份
RequestIpCity: requestClientIpCity, //【请求】请求客户端城市 RequestIpCity: requestClientIpCity, //【请求】请求客户端城市
RequestIpIsp: requestClientIpIsp, //【请求】请求客户端运营商 RequestIpIsp: requestClientIpIsp, //【请求】请求客户端运营商
RequestHeader: ginCtx.Request.Header, //【请求】请求头 RequestHeader: ginCtx.Request.Header, //【请求】请求头
ResponseTime: dorm.BsonTime(gotime.Current().Time), //【返回】时间 ResponseTime: dorm.NewBsonTimeCurrent(), //【返回】时间
ResponseCode: responseCode, //【返回】状态码 ResponseCode: responseCode, //【返回】状态码
ResponseData: c.jsonUnmarshal(responseBody), //【返回】数据 ResponseData: c.jsonUnmarshal(responseBody), //【返回】数据
CostTime: endTime - startTime, //【系统】花费时间 CostTime: endTime - startTime, //【系统】花费时间
@ -335,111 +274,25 @@ func (c *GinClient) mongoRecordXml(ginCtx *gin.Context, traceId string, requestT
} }
} }
if requestClientIpLocationLatitude != 0 && requestClientIpLocationLongitude != 0 {
data.RequestIpLocation = ginMongoLogRequestIpLocationLocation{
Type: "Point",
Coordinates: []float64{requestClientIpLocationLongitude, requestClientIpLocationLatitude},
}
}
if c.logDebug { if c.logDebug {
c.zapLog.WithTraceIdStr(traceId).Sugar().Infof("[golog.gin.mongoRecordXml.data]%+v", data) c.zapLog.WithTraceIdStr(traceId).Sugar().Infof("[golog.gin.mongoRecordXml.data]%+v", data)
} }
err := c.mongoRecord(data) err := c.mongoRecord(ctx, data)
if err != nil { if err != nil {
c.zapLog.WithTraceIdStr(traceId).Sugar().Errorf("[golog.gin.mongoRecordXml]%s", err) c.zapLog.WithTraceIdStr(traceId).Sugar().Errorf("[golog.gin.mongoRecordXml]%s", err)
} }
} }
// MongoQuery 查询
func (c *GinClient) MongoQuery(ctx context.Context) *mongo.Collection {
return c.mongoClient.Db.Database(c.mongoConfig.databaseName).Collection(c.mongoConfig.collectionName)
}
// MongoDelete 删除 // MongoDelete 删除
func (c *GinClient) MongoDelete(ctx context.Context, hour int64) (*mongo.DeleteResult, error) { func (c *GinClient) MongoDelete(ctx context.Context, hour int64) (*mongo.DeleteResult, error) {
filter := bson.D{{"log_time", bson.D{{"$lt", primitive.NewDateTimeFromTime(gotime.Current().BeforeHour(hour).Time)}}}} filter := bson.D{{"log_time", bson.D{{"$lt", primitive.NewDateTimeFromTime(gotime.Current().BeforeHour(hour).Time)}}}}
return c.mongoClient.Db.Database(c.mongoConfig.databaseName).Collection(c.mongoConfig.collectionName).DeleteMany(ctx, filter) return c.mongoClient.Db.Database(c.mongoConfig.databaseName).Collection(c.mongoConfig.collectionName).DeleteMany(ctx, filter)
} }
// MongoMiddleware 中间件
func (c *GinClient) MongoMiddleware() gin.HandlerFunc {
return func(ginCtx *gin.Context) {
// 开始时间
startTime := gotime.Current().TimestampWithMillisecond()
requestTime := gotime.Current().Time
// 获取
data, _ := ioutil.ReadAll(ginCtx.Request.Body)
if c.logDebug {
c.zapLog.WithLogger().Sugar().Infof("[golog.gin.MongoMiddleware] %s", data)
}
// 复用
ginCtx.Request.Body = ioutil.NopCloser(bytes.NewBuffer(data))
blw := &bodyLogWriter{body: bytes.NewBufferString(""), ResponseWriter: ginCtx.Writer}
ginCtx.Writer = blw
// 处理请求
ginCtx.Next()
// 响应
responseCode := ginCtx.Writer.Status()
responseBody := blw.body.String()
//结束时间
endTime := gotime.Current().TimestampWithMillisecond()
go func() {
var dataJson = true
// 解析请求内容
var jsonBody map[string]interface{}
// 判断是否有内容
if len(data) > 0 {
err := json.Unmarshal(data, &jsonBody)
if err != nil {
dataJson = false
}
}
clientIp := gorequest.ClientIp(ginCtx.Request)
requestClientIpCountry, requestClientIpRegion, requestClientIpProvince, requestClientIpCity, requestClientIpIsp := "", "", "", "", ""
if c.ipService != nil {
if net.ParseIP(clientIp).To4() != nil {
// IPv4
_, info := c.ipService.Ipv4(clientIp)
requestClientIpCountry = info.Country
requestClientIpRegion = info.Region
requestClientIpProvince = info.Province
requestClientIpCity = info.City
requestClientIpIsp = info.ISP
} else if net.ParseIP(clientIp).To16() != nil {
// IPv6
info := c.ipService.Ipv6(clientIp)
requestClientIpCountry = info.Country
requestClientIpProvince = info.Province
requestClientIpCity = info.City
}
}
// 记录
if c.mongoClient != nil && c.mongoClient.Db != nil {
var traceId = gotrace_id.GetGinTraceId(ginCtx)
if dataJson {
if c.logDebug {
c.zapLog.WithTraceIdStr(traceId).Sugar().Infof("[golog.gin.MongoMiddleware]准备使用{mongoRecordJson}保存数据:%s", data)
}
c.mongoRecordJson(ginCtx, traceId, requestTime, data, responseCode, responseBody, startTime, endTime, clientIp, requestClientIpCountry, requestClientIpRegion, requestClientIpProvince, requestClientIpCity, requestClientIpIsp)
} else {
if c.logDebug {
c.zapLog.WithTraceIdStr(traceId).Sugar().Infof("[golog.gin.MongoMiddleware]准备使用{mongoRecordXml}保存数据:%s", data)
}
c.mongoRecordXml(ginCtx, traceId, requestTime, data, responseCode, responseBody, startTime, endTime, clientIp, requestClientIpCountry, requestClientIpRegion, requestClientIpProvince, requestClientIpCity, requestClientIpIsp)
}
}
}()
}
}

@ -70,6 +70,15 @@ func NewHandler(h http.Handler, s *http2.Server) http.Handler {
} }
} }
// extractServer extracts existing http.Server instance from http.Request or create an empty http.Server
func extractServer(r *http.Request) *http.Server {
server, ok := r.Context().Value(http.ServerContextKey).(*http.Server)
if ok {
return server
}
return new(http.Server)
}
// ServeHTTP implement the h2c support that is enabled by h2c.GetH2CHandler. // ServeHTTP implement the h2c support that is enabled by h2c.GetH2CHandler.
func (s h2cHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (s h2cHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Handle h2c with prior knowledge (RFC 7540 Section 3.4) // Handle h2c with prior knowledge (RFC 7540 Section 3.4)
@ -87,6 +96,7 @@ func (s h2cHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
defer conn.Close() defer conn.Close()
s.s.ServeConn(conn, &http2.ServeConnOpts{ s.s.ServeConn(conn, &http2.ServeConnOpts{
Context: r.Context(), Context: r.Context(),
BaseConfig: extractServer(r),
Handler: s.Handler, Handler: s.Handler,
SawClientPreface: true, SawClientPreface: true,
}) })
@ -104,6 +114,7 @@ func (s h2cHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
defer conn.Close() defer conn.Close()
s.s.ServeConn(conn, &http2.ServeConnOpts{ s.s.ServeConn(conn, &http2.ServeConnOpts{
Context: r.Context(), Context: r.Context(),
BaseConfig: extractServer(r),
Handler: s.Handler, Handler: s.Handler,
UpgradeRequest: r, UpgradeRequest: r,
Settings: settings, Settings: settings,

@ -2099,12 +2099,6 @@ func (sc *serverConn) newWriterAndRequest(st *stream, f *MetaHeadersFrame) (*res
return nil, nil, sc.countError("bad_path_method", streamError(f.StreamID, ErrCodeProtocol)) return nil, nil, sc.countError("bad_path_method", streamError(f.StreamID, ErrCodeProtocol))
} }
bodyOpen := !f.StreamEnded()
if rp.method == "HEAD" && bodyOpen {
// HEAD requests can't have bodies
return nil, nil, sc.countError("head_body", streamError(f.StreamID, ErrCodeProtocol))
}
rp.header = make(http.Header) rp.header = make(http.Header)
for _, hf := range f.RegularFields() { for _, hf := range f.RegularFields() {
rp.header.Add(sc.canonicalHeader(hf.Name), hf.Value) rp.header.Add(sc.canonicalHeader(hf.Name), hf.Value)
@ -2117,6 +2111,7 @@ func (sc *serverConn) newWriterAndRequest(st *stream, f *MetaHeadersFrame) (*res
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
bodyOpen := !f.StreamEnded()
if bodyOpen { if bodyOpen {
if vv, ok := rp.header["Content-Length"]; ok { if vv, ok := rp.header["Content-Length"]; ok {
if cl, err := strconv.ParseUint(vv[0], 10, 63); err == nil { if cl, err := strconv.ParseUint(vv[0], 10, 63); err == nil {

@ -214,11 +214,6 @@ esac
if [ "$GOOSARCH" == "aix_ppc64" ]; then if [ "$GOOSARCH" == "aix_ppc64" ]; then
# aix/ppc64 script generates files instead of writing to stdin. # aix/ppc64 script generates files instead of writing to stdin.
echo "$mksyscall -tags $GOOS,$GOARCH $syscall_goos $GOOSARCH_in && gofmt -w zsyscall_$GOOSARCH.go && gofmt -w zsyscall_"$GOOSARCH"_gccgo.go && gofmt -w zsyscall_"$GOOSARCH"_gc.go " ; echo "$mksyscall -tags $GOOS,$GOARCH $syscall_goos $GOOSARCH_in && gofmt -w zsyscall_$GOOSARCH.go && gofmt -w zsyscall_"$GOOSARCH"_gccgo.go && gofmt -w zsyscall_"$GOOSARCH"_gc.go " ;
elif [ "$GOOS" == "darwin" ]; then
# 1.12 and later, syscalls via libSystem
echo "$mksyscall -tags $GOOS,$GOARCH,go1.12 $syscall_goos $GOOSARCH_in |gofmt >zsyscall_$GOOSARCH.go";
# 1.13 and later, syscalls via libSystem (including syscallPtr)
echo "$mksyscall -tags $GOOS,$GOARCH,go1.13 syscall_darwin.1_13.go |gofmt >zsyscall_$GOOSARCH.1_13.go";
elif [ "$GOOS" == "illumos" ]; then elif [ "$GOOS" == "illumos" ]; then
# illumos code generation requires a --illumos switch # illumos code generation requires a --illumos switch
echo "$mksyscall -illumos -tags illumos,$GOARCH syscall_illumos.go |gofmt > zsyscall_illumos_$GOARCH.go"; echo "$mksyscall -illumos -tags illumos,$GOARCH syscall_illumos.go |gofmt > zsyscall_illumos_$GOARCH.go";

@ -1,32 +0,0 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build darwin && go1.12 && !go1.13
// +build darwin,go1.12,!go1.13
package unix
import (
"unsafe"
)
const _SYS_GETDIRENTRIES64 = 344
func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) {
// To implement this using libSystem we'd need syscall_syscallPtr for
// fdopendir. However, syscallPtr was only added in Go 1.13, so we fall
// back to raw syscalls for this func on Go 1.12.
var p unsafe.Pointer
if len(buf) > 0 {
p = unsafe.Pointer(&buf[0])
} else {
p = unsafe.Pointer(&_zero)
}
r0, _, e1 := Syscall6(_SYS_GETDIRENTRIES64, uintptr(fd), uintptr(p), uintptr(len(buf)), uintptr(unsafe.Pointer(basep)), 0, 0)
n = int(r0)
if e1 != 0 {
return n, errnoErr(e1)
}
return n, nil
}

@ -1,100 +0,0 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build darwin && go1.13
// +build darwin,go1.13
package unix
import "unsafe"
//sys closedir(dir uintptr) (err error)
//sys readdir_r(dir uintptr, entry *Dirent, result **Dirent) (res Errno)
func fdopendir(fd int) (dir uintptr, err error) {
r0, _, e1 := syscall_syscallPtr(libc_fdopendir_trampoline_addr, uintptr(fd), 0, 0)
dir = uintptr(r0)
if e1 != 0 {
err = errnoErr(e1)
}
return
}
var libc_fdopendir_trampoline_addr uintptr
//go:cgo_import_dynamic libc_fdopendir fdopendir "/usr/lib/libSystem.B.dylib"
func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) {
// Simulate Getdirentries using fdopendir/readdir_r/closedir.
// We store the number of entries to skip in the seek
// offset of fd. See issue #31368.
// It's not the full required semantics, but should handle the case
// of calling Getdirentries or ReadDirent repeatedly.
// It won't handle assigning the results of lseek to *basep, or handle
// the directory being edited underfoot.
skip, err := Seek(fd, 0, 1 /* SEEK_CUR */)
if err != nil {
return 0, err
}
// We need to duplicate the incoming file descriptor
// because the caller expects to retain control of it, but
// fdopendir expects to take control of its argument.
// Just Dup'ing the file descriptor is not enough, as the
// result shares underlying state. Use Openat to make a really
// new file descriptor referring to the same directory.
fd2, err := Openat(fd, ".", O_RDONLY, 0)
if err != nil {
return 0, err
}
d, err := fdopendir(fd2)
if err != nil {
Close(fd2)
return 0, err
}
defer closedir(d)
var cnt int64
for {
var entry Dirent
var entryp *Dirent
e := readdir_r(d, &entry, &entryp)
if e != 0 {
return n, errnoErr(e)
}
if entryp == nil {
break
}
if skip > 0 {
skip--
cnt++
continue
}
reclen := int(entry.Reclen)
if reclen > len(buf) {
// Not enough room. Return for now.
// The counter will let us know where we should start up again.
// Note: this strategy for suspending in the middle and
// restarting is O(n^2) in the length of the directory. Oh well.
break
}
// Copy entry into return buffer.
s := unsafe.Slice((*byte)(unsafe.Pointer(&entry)), reclen)
copy(buf, s)
buf = buf[reclen:]
n += reclen
cnt++
}
// Set the seek offset of the input fd to record
// how many files we've already returned.
_, err = Seek(fd, cnt, 0 /* SEEK_SET */)
if err != nil {
return n, err
}
return n, nil
}

@ -19,6 +19,96 @@ import (
"unsafe" "unsafe"
) )
//sys closedir(dir uintptr) (err error)
//sys readdir_r(dir uintptr, entry *Dirent, result **Dirent) (res Errno)
func fdopendir(fd int) (dir uintptr, err error) {
r0, _, e1 := syscall_syscallPtr(libc_fdopendir_trampoline_addr, uintptr(fd), 0, 0)
dir = uintptr(r0)
if e1 != 0 {
err = errnoErr(e1)
}
return
}
var libc_fdopendir_trampoline_addr uintptr
//go:cgo_import_dynamic libc_fdopendir fdopendir "/usr/lib/libSystem.B.dylib"
func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) {
// Simulate Getdirentries using fdopendir/readdir_r/closedir.
// We store the number of entries to skip in the seek
// offset of fd. See issue #31368.
// It's not the full required semantics, but should handle the case
// of calling Getdirentries or ReadDirent repeatedly.
// It won't handle assigning the results of lseek to *basep, or handle
// the directory being edited underfoot.
skip, err := Seek(fd, 0, 1 /* SEEK_CUR */)
if err != nil {
return 0, err
}
// We need to duplicate the incoming file descriptor
// because the caller expects to retain control of it, but
// fdopendir expects to take control of its argument.
// Just Dup'ing the file descriptor is not enough, as the
// result shares underlying state. Use Openat to make a really
// new file descriptor referring to the same directory.
fd2, err := Openat(fd, ".", O_RDONLY, 0)
if err != nil {
return 0, err
}
d, err := fdopendir(fd2)
if err != nil {
Close(fd2)
return 0, err
}
defer closedir(d)
var cnt int64
for {
var entry Dirent
var entryp *Dirent
e := readdir_r(d, &entry, &entryp)
if e != 0 {
return n, errnoErr(e)
}
if entryp == nil {
break
}
if skip > 0 {
skip--
cnt++
continue
}
reclen := int(entry.Reclen)
if reclen > len(buf) {
// Not enough room. Return for now.
// The counter will let us know where we should start up again.
// Note: this strategy for suspending in the middle and
// restarting is O(n^2) in the length of the directory. Oh well.
break
}
// Copy entry into return buffer.
s := unsafe.Slice((*byte)(unsafe.Pointer(&entry)), reclen)
copy(buf, s)
buf = buf[reclen:]
n += reclen
cnt++
}
// Set the seek offset of the input fd to record
// how many files we've already returned.
_, err = Seek(fd, cnt, 0 /* SEEK_SET */)
if err != nil {
return n, err
}
return n, nil
}
// SockaddrDatalink implements the Sockaddr interface for AF_LINK type sockets. // SockaddrDatalink implements the Sockaddr interface for AF_LINK type sockets.
type SockaddrDatalink struct { type SockaddrDatalink struct {
Len uint8 Len uint8

@ -1,40 +0,0 @@
// go run mksyscall.go -tags darwin,amd64,go1.13 syscall_darwin.1_13.go
// Code generated by the command above; see README.md. DO NOT EDIT.
//go:build darwin && amd64 && go1.13
// +build darwin,amd64,go1.13
package unix
import (
"syscall"
"unsafe"
)
var _ syscall.Errno
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func closedir(dir uintptr) (err error) {
_, _, e1 := syscall_syscall(libc_closedir_trampoline_addr, uintptr(dir), 0, 0)
if e1 != 0 {
err = errnoErr(e1)
}
return
}
var libc_closedir_trampoline_addr uintptr
//go:cgo_import_dynamic libc_closedir closedir "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func readdir_r(dir uintptr, entry *Dirent, result **Dirent) (res Errno) {
r0, _, _ := syscall_syscall(libc_readdir_r_trampoline_addr, uintptr(dir), uintptr(unsafe.Pointer(entry)), uintptr(unsafe.Pointer(result)))
res = Errno(r0)
return
}
var libc_readdir_r_trampoline_addr uintptr
//go:cgo_import_dynamic libc_readdir_r readdir_r "/usr/lib/libSystem.B.dylib"

@ -1,25 +0,0 @@
// go run mkasm.go darwin amd64
// Code generated by the command above; DO NOT EDIT.
//go:build go1.13
// +build go1.13
#include "textflag.h"
TEXT libc_fdopendir_trampoline<>(SB),NOSPLIT,$0-0
JMP libc_fdopendir(SB)
GLOBL ·libc_fdopendir_trampoline_addr(SB), RODATA, $8
DATA ·libc_fdopendir_trampoline_addr(SB)/8, $libc_fdopendir_trampoline<>(SB)
TEXT libc_closedir_trampoline<>(SB),NOSPLIT,$0-0
JMP libc_closedir(SB)
GLOBL ·libc_closedir_trampoline_addr(SB), RODATA, $8
DATA ·libc_closedir_trampoline_addr(SB)/8, $libc_closedir_trampoline<>(SB)
TEXT libc_readdir_r_trampoline<>(SB),NOSPLIT,$0-0
JMP libc_readdir_r(SB)
GLOBL ·libc_readdir_r_trampoline_addr(SB), RODATA, $8
DATA ·libc_readdir_r_trampoline_addr(SB)/8, $libc_readdir_r_trampoline<>(SB)

@ -1,8 +1,8 @@
// go run mksyscall.go -tags darwin,amd64,go1.12 syscall_bsd.go syscall_darwin.go syscall_darwin_amd64.go // go run mksyscall.go -tags darwin,amd64 syscall_bsd.go syscall_darwin.go syscall_darwin_amd64.go
// Code generated by the command above; see README.md. DO NOT EDIT. // Code generated by the command above; see README.md. DO NOT EDIT.
//go:build darwin && amd64 && go1.12 //go:build darwin && amd64
// +build darwin,amd64,go1.12 // +build darwin,amd64
package unix package unix
@ -463,6 +463,32 @@ var libc_munlockall_trampoline_addr uintptr
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func closedir(dir uintptr) (err error) {
_, _, e1 := syscall_syscall(libc_closedir_trampoline_addr, uintptr(dir), 0, 0)
if e1 != 0 {
err = errnoErr(e1)
}
return
}
var libc_closedir_trampoline_addr uintptr
//go:cgo_import_dynamic libc_closedir closedir "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func readdir_r(dir uintptr, entry *Dirent, result **Dirent) (res Errno) {
r0, _, _ := syscall_syscall(libc_readdir_r_trampoline_addr, uintptr(dir), uintptr(unsafe.Pointer(entry)), uintptr(unsafe.Pointer(result)))
res = Errno(r0)
return
}
var libc_readdir_r_trampoline_addr uintptr
//go:cgo_import_dynamic libc_readdir_r readdir_r "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func pipe(p *[2]int32) (err error) { func pipe(p *[2]int32) (err error) {
_, _, e1 := syscall_rawSyscall(libc_pipe_trampoline_addr, uintptr(unsafe.Pointer(p)), 0, 0) _, _, e1 := syscall_rawSyscall(libc_pipe_trampoline_addr, uintptr(unsafe.Pointer(p)), 0, 0)
if e1 != 0 { if e1 != 0 {

@ -1,11 +1,14 @@
// go run mkasm.go darwin amd64 // go run mkasm.go darwin amd64
// Code generated by the command above; DO NOT EDIT. // Code generated by the command above; DO NOT EDIT.
//go:build go1.12
// +build go1.12
#include "textflag.h" #include "textflag.h"
TEXT libc_fdopendir_trampoline<>(SB),NOSPLIT,$0-0
JMP libc_fdopendir(SB)
GLOBL ·libc_fdopendir_trampoline_addr(SB), RODATA, $8
DATA ·libc_fdopendir_trampoline_addr(SB)/8, $libc_fdopendir_trampoline<>(SB)
TEXT libc_getgroups_trampoline<>(SB),NOSPLIT,$0-0 TEXT libc_getgroups_trampoline<>(SB),NOSPLIT,$0-0
JMP libc_getgroups(SB) JMP libc_getgroups(SB)
@ -174,6 +177,18 @@ TEXT libc_munlockall_trampoline<>(SB),NOSPLIT,$0-0
GLOBL ·libc_munlockall_trampoline_addr(SB), RODATA, $8 GLOBL ·libc_munlockall_trampoline_addr(SB), RODATA, $8
DATA ·libc_munlockall_trampoline_addr(SB)/8, $libc_munlockall_trampoline<>(SB) DATA ·libc_munlockall_trampoline_addr(SB)/8, $libc_munlockall_trampoline<>(SB)
TEXT libc_closedir_trampoline<>(SB),NOSPLIT,$0-0
JMP libc_closedir(SB)
GLOBL ·libc_closedir_trampoline_addr(SB), RODATA, $8
DATA ·libc_closedir_trampoline_addr(SB)/8, $libc_closedir_trampoline<>(SB)
TEXT libc_readdir_r_trampoline<>(SB),NOSPLIT,$0-0
JMP libc_readdir_r(SB)
GLOBL ·libc_readdir_r_trampoline_addr(SB), RODATA, $8
DATA ·libc_readdir_r_trampoline_addr(SB)/8, $libc_readdir_r_trampoline<>(SB)
TEXT libc_pipe_trampoline<>(SB),NOSPLIT,$0-0 TEXT libc_pipe_trampoline<>(SB),NOSPLIT,$0-0
JMP libc_pipe(SB) JMP libc_pipe(SB)

@ -1,40 +0,0 @@
// go run mksyscall.go -tags darwin,arm64,go1.13 syscall_darwin.1_13.go
// Code generated by the command above; see README.md. DO NOT EDIT.
//go:build darwin && arm64 && go1.13
// +build darwin,arm64,go1.13
package unix
import (
"syscall"
"unsafe"
)
var _ syscall.Errno
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func closedir(dir uintptr) (err error) {
_, _, e1 := syscall_syscall(libc_closedir_trampoline_addr, uintptr(dir), 0, 0)
if e1 != 0 {
err = errnoErr(e1)
}
return
}
var libc_closedir_trampoline_addr uintptr
//go:cgo_import_dynamic libc_closedir closedir "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func readdir_r(dir uintptr, entry *Dirent, result **Dirent) (res Errno) {
r0, _, _ := syscall_syscall(libc_readdir_r_trampoline_addr, uintptr(dir), uintptr(unsafe.Pointer(entry)), uintptr(unsafe.Pointer(result)))
res = Errno(r0)
return
}
var libc_readdir_r_trampoline_addr uintptr
//go:cgo_import_dynamic libc_readdir_r readdir_r "/usr/lib/libSystem.B.dylib"

@ -1,25 +0,0 @@
// go run mkasm.go darwin arm64
// Code generated by the command above; DO NOT EDIT.
//go:build go1.13
// +build go1.13
#include "textflag.h"
TEXT libc_fdopendir_trampoline<>(SB),NOSPLIT,$0-0
JMP libc_fdopendir(SB)
GLOBL ·libc_fdopendir_trampoline_addr(SB), RODATA, $8
DATA ·libc_fdopendir_trampoline_addr(SB)/8, $libc_fdopendir_trampoline<>(SB)
TEXT libc_closedir_trampoline<>(SB),NOSPLIT,$0-0
JMP libc_closedir(SB)
GLOBL ·libc_closedir_trampoline_addr(SB), RODATA, $8
DATA ·libc_closedir_trampoline_addr(SB)/8, $libc_closedir_trampoline<>(SB)
TEXT libc_readdir_r_trampoline<>(SB),NOSPLIT,$0-0
JMP libc_readdir_r(SB)
GLOBL ·libc_readdir_r_trampoline_addr(SB), RODATA, $8
DATA ·libc_readdir_r_trampoline_addr(SB)/8, $libc_readdir_r_trampoline<>(SB)

@ -1,8 +1,8 @@
// go run mksyscall.go -tags darwin,arm64,go1.12 syscall_bsd.go syscall_darwin.go syscall_darwin_arm64.go // go run mksyscall.go -tags darwin,arm64 syscall_bsd.go syscall_darwin.go syscall_darwin_arm64.go
// Code generated by the command above; see README.md. DO NOT EDIT. // Code generated by the command above; see README.md. DO NOT EDIT.
//go:build darwin && arm64 && go1.12 //go:build darwin && arm64
// +build darwin,arm64,go1.12 // +build darwin,arm64
package unix package unix
@ -463,6 +463,32 @@ var libc_munlockall_trampoline_addr uintptr
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func closedir(dir uintptr) (err error) {
_, _, e1 := syscall_syscall(libc_closedir_trampoline_addr, uintptr(dir), 0, 0)
if e1 != 0 {
err = errnoErr(e1)
}
return
}
var libc_closedir_trampoline_addr uintptr
//go:cgo_import_dynamic libc_closedir closedir "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func readdir_r(dir uintptr, entry *Dirent, result **Dirent) (res Errno) {
r0, _, _ := syscall_syscall(libc_readdir_r_trampoline_addr, uintptr(dir), uintptr(unsafe.Pointer(entry)), uintptr(unsafe.Pointer(result)))
res = Errno(r0)
return
}
var libc_readdir_r_trampoline_addr uintptr
//go:cgo_import_dynamic libc_readdir_r readdir_r "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func pipe(p *[2]int32) (err error) { func pipe(p *[2]int32) (err error) {
_, _, e1 := syscall_rawSyscall(libc_pipe_trampoline_addr, uintptr(unsafe.Pointer(p)), 0, 0) _, _, e1 := syscall_rawSyscall(libc_pipe_trampoline_addr, uintptr(unsafe.Pointer(p)), 0, 0)
if e1 != 0 { if e1 != 0 {

@ -1,11 +1,14 @@
// go run mkasm.go darwin arm64 // go run mkasm.go darwin arm64
// Code generated by the command above; DO NOT EDIT. // Code generated by the command above; DO NOT EDIT.
//go:build go1.12
// +build go1.12
#include "textflag.h" #include "textflag.h"
TEXT libc_fdopendir_trampoline<>(SB),NOSPLIT,$0-0
JMP libc_fdopendir(SB)
GLOBL ·libc_fdopendir_trampoline_addr(SB), RODATA, $8
DATA ·libc_fdopendir_trampoline_addr(SB)/8, $libc_fdopendir_trampoline<>(SB)
TEXT libc_getgroups_trampoline<>(SB),NOSPLIT,$0-0 TEXT libc_getgroups_trampoline<>(SB),NOSPLIT,$0-0
JMP libc_getgroups(SB) JMP libc_getgroups(SB)
@ -174,6 +177,18 @@ TEXT libc_munlockall_trampoline<>(SB),NOSPLIT,$0-0
GLOBL ·libc_munlockall_trampoline_addr(SB), RODATA, $8 GLOBL ·libc_munlockall_trampoline_addr(SB), RODATA, $8
DATA ·libc_munlockall_trampoline_addr(SB)/8, $libc_munlockall_trampoline<>(SB) DATA ·libc_munlockall_trampoline_addr(SB)/8, $libc_munlockall_trampoline<>(SB)
TEXT libc_closedir_trampoline<>(SB),NOSPLIT,$0-0
JMP libc_closedir(SB)
GLOBL ·libc_closedir_trampoline_addr(SB), RODATA, $8
DATA ·libc_closedir_trampoline_addr(SB)/8, $libc_closedir_trampoline<>(SB)
TEXT libc_readdir_r_trampoline<>(SB),NOSPLIT,$0-0
JMP libc_readdir_r(SB)
GLOBL ·libc_readdir_r_trampoline_addr(SB), RODATA, $8
DATA ·libc_readdir_r_trampoline_addr(SB)/8, $libc_readdir_r_trampoline<>(SB)
TEXT libc_pipe_trampoline<>(SB),NOSPLIT,$0-0 TEXT libc_pipe_trampoline<>(SB),NOSPLIT,$0-0
JMP libc_pipe(SB) JMP libc_pipe(SB)

@ -442,6 +442,10 @@ func NewCallbackCDecl(fn interface{}) uintptr {
//sys RtlAddFunctionTable(functionTable *RUNTIME_FUNCTION, entryCount uint32, baseAddress uintptr) (ret bool) = ntdll.RtlAddFunctionTable //sys RtlAddFunctionTable(functionTable *RUNTIME_FUNCTION, entryCount uint32, baseAddress uintptr) (ret bool) = ntdll.RtlAddFunctionTable
//sys RtlDeleteFunctionTable(functionTable *RUNTIME_FUNCTION) (ret bool) = ntdll.RtlDeleteFunctionTable //sys RtlDeleteFunctionTable(functionTable *RUNTIME_FUNCTION) (ret bool) = ntdll.RtlDeleteFunctionTable
// Desktop Window Manager API (Dwmapi)
//sys DwmGetWindowAttribute(hwnd HWND, attribute uint32, value unsafe.Pointer, size uint32) (ret error) = dwmapi.DwmGetWindowAttribute
//sys DwmSetWindowAttribute(hwnd HWND, attribute uint32, value unsafe.Pointer, size uint32) (ret error) = dwmapi.DwmSetWindowAttribute
// syscall interface implementation for other packages // syscall interface implementation for other packages
// GetCurrentProcess returns the handle for the current process. // GetCurrentProcess returns the handle for the current process.

@ -3232,3 +3232,29 @@ type GUIThreadInfo struct {
CaretHandle HWND CaretHandle HWND
CaretRect Rect CaretRect Rect
} }
const (
DWMWA_NCRENDERING_ENABLED = 1
DWMWA_NCRENDERING_POLICY = 2
DWMWA_TRANSITIONS_FORCEDISABLED = 3
DWMWA_ALLOW_NCPAINT = 4
DWMWA_CAPTION_BUTTON_BOUNDS = 5
DWMWA_NONCLIENT_RTL_LAYOUT = 6
DWMWA_FORCE_ICONIC_REPRESENTATION = 7
DWMWA_FLIP3D_POLICY = 8
DWMWA_EXTENDED_FRAME_BOUNDS = 9
DWMWA_HAS_ICONIC_BITMAP = 10
DWMWA_DISALLOW_PEEK = 11
DWMWA_EXCLUDED_FROM_PEEK = 12
DWMWA_CLOAK = 13
DWMWA_CLOAKED = 14
DWMWA_FREEZE_REPRESENTATION = 15
DWMWA_PASSIVE_UPDATE_MODE = 16
DWMWA_USE_HOSTBACKDROPBRUSH = 17
DWMWA_USE_IMMERSIVE_DARK_MODE = 20
DWMWA_WINDOW_CORNER_PREFERENCE = 33
DWMWA_BORDER_COLOR = 34
DWMWA_CAPTION_COLOR = 35
DWMWA_TEXT_COLOR = 36
DWMWA_VISIBLE_FRAME_BORDER_THICKNESS = 37
)

@ -40,6 +40,7 @@ var (
modadvapi32 = NewLazySystemDLL("advapi32.dll") modadvapi32 = NewLazySystemDLL("advapi32.dll")
modcrypt32 = NewLazySystemDLL("crypt32.dll") modcrypt32 = NewLazySystemDLL("crypt32.dll")
moddnsapi = NewLazySystemDLL("dnsapi.dll") moddnsapi = NewLazySystemDLL("dnsapi.dll")
moddwmapi = NewLazySystemDLL("dwmapi.dll")
modiphlpapi = NewLazySystemDLL("iphlpapi.dll") modiphlpapi = NewLazySystemDLL("iphlpapi.dll")
modkernel32 = NewLazySystemDLL("kernel32.dll") modkernel32 = NewLazySystemDLL("kernel32.dll")
modmswsock = NewLazySystemDLL("mswsock.dll") modmswsock = NewLazySystemDLL("mswsock.dll")
@ -175,6 +176,8 @@ var (
procDnsNameCompare_W = moddnsapi.NewProc("DnsNameCompare_W") procDnsNameCompare_W = moddnsapi.NewProc("DnsNameCompare_W")
procDnsQuery_W = moddnsapi.NewProc("DnsQuery_W") procDnsQuery_W = moddnsapi.NewProc("DnsQuery_W")
procDnsRecordListFree = moddnsapi.NewProc("DnsRecordListFree") procDnsRecordListFree = moddnsapi.NewProc("DnsRecordListFree")
procDwmGetWindowAttribute = moddwmapi.NewProc("DwmGetWindowAttribute")
procDwmSetWindowAttribute = moddwmapi.NewProc("DwmSetWindowAttribute")
procGetAdaptersAddresses = modiphlpapi.NewProc("GetAdaptersAddresses") procGetAdaptersAddresses = modiphlpapi.NewProc("GetAdaptersAddresses")
procGetAdaptersInfo = modiphlpapi.NewProc("GetAdaptersInfo") procGetAdaptersInfo = modiphlpapi.NewProc("GetAdaptersInfo")
procGetBestInterfaceEx = modiphlpapi.NewProc("GetBestInterfaceEx") procGetBestInterfaceEx = modiphlpapi.NewProc("GetBestInterfaceEx")
@ -1534,6 +1537,22 @@ func DnsRecordListFree(rl *DNSRecord, freetype uint32) {
return return
} }
func DwmGetWindowAttribute(hwnd HWND, attribute uint32, value unsafe.Pointer, size uint32) (ret error) {
r0, _, _ := syscall.Syscall6(procDwmGetWindowAttribute.Addr(), 4, uintptr(hwnd), uintptr(attribute), uintptr(value), uintptr(size), 0, 0)
if r0 != 0 {
ret = syscall.Errno(r0)
}
return
}
func DwmSetWindowAttribute(hwnd HWND, attribute uint32, value unsafe.Pointer, size uint32) (ret error) {
r0, _, _ := syscall.Syscall6(procDwmSetWindowAttribute.Addr(), 4, uintptr(hwnd), uintptr(attribute), uintptr(value), uintptr(size), 0, 0)
if r0 != 0 {
ret = syscall.Errno(r0)
}
return
}
func GetAdaptersAddresses(family uint32, flags uint32, reserved uintptr, adapterAddresses *IpAdapterAddresses, sizePointer *uint32) (errcode error) { func GetAdaptersAddresses(family uint32, flags uint32, reserved uintptr, adapterAddresses *IpAdapterAddresses, sizePointer *uint32) (errcode error) {
r0, _, _ := syscall.Syscall6(procGetAdaptersAddresses.Addr(), 5, uintptr(family), uintptr(flags), uintptr(reserved), uintptr(unsafe.Pointer(adapterAddresses)), uintptr(unsafe.Pointer(sizePointer)), 0) r0, _, _ := syscall.Syscall6(procGetAdaptersAddresses.Addr(), 5, uintptr(family), uintptr(flags), uintptr(reserved), uintptr(unsafe.Pointer(adapterAddresses)), uintptr(unsafe.Pointer(sizePointer)), 0)
if r0 != 0 { if r0 != 0 {

@ -436,14 +436,25 @@ func (m Migrator) ColumnTypes(value interface{}) (columnTypes []gorm.ColumnType,
// check primary, unique field // check primary, unique field
{ {
columnTypeRows, err := m.DB.Raw("SELECT c.column_name, constraint_type FROM information_schema.table_constraints tc JOIN information_schema.constraint_column_usage AS ccu USING (constraint_schema, constraint_name) JOIN information_schema.columns AS c ON c.table_schema = tc.constraint_schema AND tc.table_name = c.table_name AND ccu.column_name = c.column_name WHERE constraint_type IN ('PRIMARY KEY', 'UNIQUE') AND c.table_catalog = ? AND c.table_schema = ? AND c.table_name = ?", currentDatabase, currentSchema, table).Rows() columnTypeRows, err := m.DB.Raw("SELECT constraint_name FROM information_schema.table_constraints tc JOIN information_schema.constraint_column_usage AS ccu USING (constraint_schema, constraint_name) JOIN information_schema.columns AS c ON c.table_schema = tc.constraint_schema AND tc.table_name = c.table_name AND ccu.column_name = c.column_name WHERE constraint_type IN ('PRIMARY KEY', 'UNIQUE') AND c.table_catalog = ? AND c.table_schema = ? AND c.table_name = ? AND constraint_type = ?", currentDatabase, currentSchema, table, "UNIQUE").Rows()
if err != nil { if err != nil {
return err return err
} }
uniqueContraints := map[string]int{}
for columnTypeRows.Next() {
var constraintName string
columnTypeRows.Scan(&constraintName)
uniqueContraints[constraintName]++
}
columnTypeRows.Close()
columnTypeRows, err = m.DB.Raw("SELECT c.column_name, constraint_name, constraint_type FROM information_schema.table_constraints tc JOIN information_schema.constraint_column_usage AS ccu USING (constraint_schema, constraint_name) JOIN information_schema.columns AS c ON c.table_schema = tc.constraint_schema AND tc.table_name = c.table_name AND ccu.column_name = c.column_name WHERE constraint_type IN ('PRIMARY KEY', 'UNIQUE') AND c.table_catalog = ? AND c.table_schema = ? AND c.table_name = ?", currentDatabase, currentSchema, table).Rows()
if err != nil {
return err
}
for columnTypeRows.Next() { for columnTypeRows.Next() {
var name, columnType string var name, constraintName, columnType string
columnTypeRows.Scan(&name, &columnType) columnTypeRows.Scan(&name, &constraintName, &columnType)
for _, c := range columnTypes { for _, c := range columnTypes {
mc := c.(*migrator.ColumnType) mc := c.(*migrator.ColumnType)
if mc.NameValue.String == name { if mc.NameValue.String == name {
@ -451,7 +462,9 @@ func (m Migrator) ColumnTypes(value interface{}) (columnTypes []gorm.ColumnType,
case "PRIMARY KEY": case "PRIMARY KEY":
mc.PrimaryKeyValue = sql.NullBool{Bool: true, Valid: true} mc.PrimaryKeyValue = sql.NullBool{Bool: true, Valid: true}
case "UNIQUE": case "UNIQUE":
mc.UniqueValue = sql.NullBool{Bool: true, Valid: true} if uniqueContraints[constraintName] == 1 {
mc.UniqueValue = sql.NullBool{Bool: true, Valid: true}
}
} }
break break
} }
@ -463,7 +476,7 @@ func (m Migrator) ColumnTypes(value interface{}) (columnTypes []gorm.ColumnType,
// check column type // check column type
{ {
dataTypeRows, err := m.DB.Raw(`SELECT a.attname as column_name, format_type(a.atttypid, a.atttypmod) AS data_type dataTypeRows, err := m.DB.Raw(`SELECT a.attname as column_name, format_type(a.atttypid, a.atttypmod) AS data_type
FROM pg_attribute a JOIN pg_class b ON a.attrelid = b.relfilenode AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname = ?) FROM pg_attribute a JOIN pg_class b ON a.attrelid = b.oid AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname = ?)
WHERE a.attnum > 0 -- hide internal columns WHERE a.attnum > 0 -- hide internal columns
AND NOT a.attisdropped -- hide deleted columns AND NOT a.attisdropped -- hide deleted columns
AND b.relname = ?`, currentSchema, table).Rows() AND b.relname = ?`, currentSchema, table).Rows()

3
vendor/gorm.io/gorm/.gitignore generated vendored

@ -3,4 +3,5 @@ documents
coverage.txt coverage.txt
_book _book
.idea .idea
vendor vendor
.vscode

@ -507,7 +507,9 @@ func (association *Association) buildCondition() *DB {
joinStmt.AddClause(queryClause) joinStmt.AddClause(queryClause)
} }
joinStmt.Build("WHERE") joinStmt.Build("WHERE")
tx.Clauses(clause.Expr{SQL: strings.Replace(joinStmt.SQL.String(), "WHERE ", "", 1), Vars: joinStmt.Vars}) if len(joinStmt.SQL.String()) > 0 {
tx.Clauses(clause.Expr{SQL: strings.Replace(joinStmt.SQL.String(), "WHERE ", "", 1), Vars: joinStmt.Vars})
}
} }
tx = tx.Session(&Session{QueryFields: true}).Clauses(clause.From{Joins: []clause.Join{{ tx = tx.Session(&Session{QueryFields: true}).Clauses(clause.From{Joins: []clause.Join{{

@ -206,7 +206,7 @@ func SaveAfterAssociations(create bool) func(db *gorm.DB) {
} }
} }
cacheKey := utils.ToStringKey(relPrimaryValues) cacheKey := utils.ToStringKey(relPrimaryValues...)
if len(relPrimaryValues) != len(rel.FieldSchema.PrimaryFields) || !identityMap[cacheKey] { if len(relPrimaryValues) != len(rel.FieldSchema.PrimaryFields) || !identityMap[cacheKey] {
identityMap[cacheKey] = true identityMap[cacheKey] = true
if isPtr { if isPtr {
@ -292,7 +292,7 @@ func SaveAfterAssociations(create bool) func(db *gorm.DB) {
} }
} }
cacheKey := utils.ToStringKey(relPrimaryValues) cacheKey := utils.ToStringKey(relPrimaryValues...)
if len(relPrimaryValues) != len(rel.FieldSchema.PrimaryFields) || !identityMap[cacheKey] { if len(relPrimaryValues) != len(rel.FieldSchema.PrimaryFields) || !identityMap[cacheKey] {
identityMap[cacheKey] = true identityMap[cacheKey] = true
distinctElems = reflect.Append(distinctElems, elem) distinctElems = reflect.Append(distinctElems, elem)

@ -70,10 +70,12 @@ func Update(config *Config) func(db *gorm.DB) {
if db.Statement.SQL.Len() == 0 { if db.Statement.SQL.Len() == 0 {
db.Statement.SQL.Grow(180) db.Statement.SQL.Grow(180)
db.Statement.AddClauseIfNotExists(clause.Update{}) db.Statement.AddClauseIfNotExists(clause.Update{})
if set := ConvertToAssignments(db.Statement); len(set) != 0 { if _, ok := db.Statement.Clauses["SET"]; !ok {
db.Statement.AddClause(set) if set := ConvertToAssignments(db.Statement); len(set) != 0 {
} else if _, ok := db.Statement.Clauses["SET"]; !ok { db.Statement.AddClause(set)
return } else {
return
}
} }
db.Statement.Build(db.Statement.BuildClauses...) db.Statement.Build(db.Statement.BuildClauses...)
@ -158,21 +160,21 @@ func ConvertToAssignments(stmt *gorm.Statement) (set clause.Set) {
switch stmt.ReflectValue.Kind() { switch stmt.ReflectValue.Kind() {
case reflect.Slice, reflect.Array: case reflect.Slice, reflect.Array:
if size := stmt.ReflectValue.Len(); size > 0 { if size := stmt.ReflectValue.Len(); size > 0 {
var primaryKeyExprs []clause.Expression var isZero bool
for i := 0; i < size; i++ { for i := 0; i < size; i++ {
exprs := make([]clause.Expression, len(stmt.Schema.PrimaryFields)) for _, field := range stmt.Schema.PrimaryFields {
var notZero bool _, isZero = field.ValueOf(stmt.Context, stmt.ReflectValue.Index(i))
for idx, field := range stmt.Schema.PrimaryFields { if !isZero {
value, isZero := field.ValueOf(stmt.Context, stmt.ReflectValue.Index(i)) break
exprs[idx] = clause.Eq{Column: field.DBName, Value: value} }
notZero = notZero || !isZero
}
if notZero {
primaryKeyExprs = append(primaryKeyExprs, clause.And(exprs...))
} }
} }
stmt.AddClause(clause.Where{Exprs: []clause.Expression{clause.And(clause.Or(primaryKeyExprs...))}}) if !isZero {
_, primaryValues := schema.GetIdentityFieldValuesMap(stmt.Context, stmt.ReflectValue, stmt.Schema.PrimaryFields)
column, values := schema.ToQueryValues("", stmt.Schema.PrimaryFieldDBNames, primaryValues)
stmt.AddClause(clause.Where{Exprs: []clause.Expression{clause.IN{Column: column, Values: values}}})
}
} }
case reflect.Struct: case reflect.Struct:
for _, field := range stmt.Schema.PrimaryFields { for _, field := range stmt.Schema.PrimaryFields {

@ -13,7 +13,7 @@ import (
"gorm.io/gorm/utils" "gorm.io/gorm/utils"
) )
// Create insert the value into database // Create inserts value, returning the inserted data's primary key in value's id
func (db *DB) Create(value interface{}) (tx *DB) { func (db *DB) Create(value interface{}) (tx *DB) {
if db.CreateBatchSize > 0 { if db.CreateBatchSize > 0 {
return db.CreateInBatches(value, db.CreateBatchSize) return db.CreateInBatches(value, db.CreateBatchSize)
@ -24,7 +24,7 @@ func (db *DB) Create(value interface{}) (tx *DB) {
return tx.callbacks.Create().Execute(tx) return tx.callbacks.Create().Execute(tx)
} }
// CreateInBatches insert the value in batches into database // CreateInBatches inserts value in batches of batchSize
func (db *DB) CreateInBatches(value interface{}, batchSize int) (tx *DB) { func (db *DB) CreateInBatches(value interface{}, batchSize int) (tx *DB) {
reflectValue := reflect.Indirect(reflect.ValueOf(value)) reflectValue := reflect.Indirect(reflect.ValueOf(value))
@ -68,7 +68,7 @@ func (db *DB) CreateInBatches(value interface{}, batchSize int) (tx *DB) {
return return
} }
// Save update value in database, if the value doesn't have primary key, will insert it // Save updates value in database. If value doesn't contain a matching primary key, value is inserted.
func (db *DB) Save(value interface{}) (tx *DB) { func (db *DB) Save(value interface{}) (tx *DB) {
tx = db.getInstance() tx = db.getInstance()
tx.Statement.Dest = value tx.Statement.Dest = value
@ -114,7 +114,7 @@ func (db *DB) Save(value interface{}) (tx *DB) {
return return
} }
// First find first record that match given conditions, order by primary key // First finds the first record ordered by primary key, matching given conditions conds
func (db *DB) First(dest interface{}, conds ...interface{}) (tx *DB) { func (db *DB) First(dest interface{}, conds ...interface{}) (tx *DB) {
tx = db.Limit(1).Order(clause.OrderByColumn{ tx = db.Limit(1).Order(clause.OrderByColumn{
Column: clause.Column{Table: clause.CurrentTable, Name: clause.PrimaryKey}, Column: clause.Column{Table: clause.CurrentTable, Name: clause.PrimaryKey},
@ -129,7 +129,7 @@ func (db *DB) First(dest interface{}, conds ...interface{}) (tx *DB) {
return tx.callbacks.Query().Execute(tx) return tx.callbacks.Query().Execute(tx)
} }
// Take return a record that match given conditions, the order will depend on the database implementation // Take finds the first record returned by the database in no specified order, matching given conditions conds
func (db *DB) Take(dest interface{}, conds ...interface{}) (tx *DB) { func (db *DB) Take(dest interface{}, conds ...interface{}) (tx *DB) {
tx = db.Limit(1) tx = db.Limit(1)
if len(conds) > 0 { if len(conds) > 0 {
@ -142,7 +142,7 @@ func (db *DB) Take(dest interface{}, conds ...interface{}) (tx *DB) {
return tx.callbacks.Query().Execute(tx) return tx.callbacks.Query().Execute(tx)
} }
// Last find last record that match given conditions, order by primary key // Last finds the last record ordered by primary key, matching given conditions conds
func (db *DB) Last(dest interface{}, conds ...interface{}) (tx *DB) { func (db *DB) Last(dest interface{}, conds ...interface{}) (tx *DB) {
tx = db.Limit(1).Order(clause.OrderByColumn{ tx = db.Limit(1).Order(clause.OrderByColumn{
Column: clause.Column{Table: clause.CurrentTable, Name: clause.PrimaryKey}, Column: clause.Column{Table: clause.CurrentTable, Name: clause.PrimaryKey},
@ -158,7 +158,7 @@ func (db *DB) Last(dest interface{}, conds ...interface{}) (tx *DB) {
return tx.callbacks.Query().Execute(tx) return tx.callbacks.Query().Execute(tx)
} }
// Find find records that match given conditions // Find finds all records matching given conditions conds
func (db *DB) Find(dest interface{}, conds ...interface{}) (tx *DB) { func (db *DB) Find(dest interface{}, conds ...interface{}) (tx *DB) {
tx = db.getInstance() tx = db.getInstance()
if len(conds) > 0 { if len(conds) > 0 {
@ -170,7 +170,7 @@ func (db *DB) Find(dest interface{}, conds ...interface{}) (tx *DB) {
return tx.callbacks.Query().Execute(tx) return tx.callbacks.Query().Execute(tx)
} }
// FindInBatches find records in batches // FindInBatches finds all records in batches of batchSize
func (db *DB) FindInBatches(dest interface{}, batchSize int, fc func(tx *DB, batch int) error) *DB { func (db *DB) FindInBatches(dest interface{}, batchSize int, fc func(tx *DB, batch int) error) *DB {
var ( var (
tx = db.Order(clause.OrderByColumn{ tx = db.Order(clause.OrderByColumn{
@ -202,7 +202,9 @@ func (db *DB) FindInBatches(dest interface{}, batchSize int, fc func(tx *DB, bat
batch++ batch++
if result.Error == nil && result.RowsAffected != 0 { if result.Error == nil && result.RowsAffected != 0 {
tx.AddError(fc(result, batch)) fcTx := result.Session(&Session{NewDB: true})
fcTx.RowsAffected = result.RowsAffected
tx.AddError(fc(fcTx, batch))
} else if result.Error != nil { } else if result.Error != nil {
tx.AddError(result.Error) tx.AddError(result.Error)
} }
@ -284,7 +286,8 @@ func (db *DB) assignInterfacesToValue(values ...interface{}) {
} }
} }
// FirstOrInit gets the first matched record or initialize a new instance with given conditions (only works with struct or map conditions) // FirstOrInit finds the first matching record, otherwise if not found initializes a new instance with given conds.
// Each conds must be a struct or map.
func (db *DB) FirstOrInit(dest interface{}, conds ...interface{}) (tx *DB) { func (db *DB) FirstOrInit(dest interface{}, conds ...interface{}) (tx *DB) {
queryTx := db.Limit(1).Order(clause.OrderByColumn{ queryTx := db.Limit(1).Order(clause.OrderByColumn{
Column: clause.Column{Table: clause.CurrentTable, Name: clause.PrimaryKey}, Column: clause.Column{Table: clause.CurrentTable, Name: clause.PrimaryKey},
@ -310,7 +313,8 @@ func (db *DB) FirstOrInit(dest interface{}, conds ...interface{}) (tx *DB) {
return return
} }
// FirstOrCreate gets the first matched record or create a new one with given conditions (only works with struct, map conditions) // FirstOrCreate finds the first matching record, otherwise if not found creates a new instance with given conds.
// Each conds must be a struct or map.
func (db *DB) FirstOrCreate(dest interface{}, conds ...interface{}) (tx *DB) { func (db *DB) FirstOrCreate(dest interface{}, conds ...interface{}) (tx *DB) {
tx = db.getInstance() tx = db.getInstance()
queryTx := db.Session(&Session{}).Limit(1).Order(clause.OrderByColumn{ queryTx := db.Session(&Session{}).Limit(1).Order(clause.OrderByColumn{
@ -358,14 +362,14 @@ func (db *DB) FirstOrCreate(dest interface{}, conds ...interface{}) (tx *DB) {
return tx return tx
} }
// Update update attributes with callbacks, refer: https://gorm.io/docs/update.html#Update-Changed-Fields // Update updates column with value using callbacks. Reference: https://gorm.io/docs/update.html#Update-Changed-Fields
func (db *DB) Update(column string, value interface{}) (tx *DB) { func (db *DB) Update(column string, value interface{}) (tx *DB) {
tx = db.getInstance() tx = db.getInstance()
tx.Statement.Dest = map[string]interface{}{column: value} tx.Statement.Dest = map[string]interface{}{column: value}
return tx.callbacks.Update().Execute(tx) return tx.callbacks.Update().Execute(tx)
} }
// Updates update attributes with callbacks, refer: https://gorm.io/docs/update.html#Update-Changed-Fields // Updates updates attributes using callbacks. values must be a struct or map. Reference: https://gorm.io/docs/update.html#Update-Changed-Fields
func (db *DB) Updates(values interface{}) (tx *DB) { func (db *DB) Updates(values interface{}) (tx *DB) {
tx = db.getInstance() tx = db.getInstance()
tx.Statement.Dest = values tx.Statement.Dest = values
@ -386,7 +390,9 @@ func (db *DB) UpdateColumns(values interface{}) (tx *DB) {
return tx.callbacks.Update().Execute(tx) return tx.callbacks.Update().Execute(tx)
} }
// Delete delete value match given conditions, if the value has primary key, then will including the primary key as condition // Delete deletes value matching given conditions. If value contains primary key it is included in the conditions. If
// value includes a deleted_at field, then Delete performs a soft delete instead by setting deleted_at with the current
// time if null.
func (db *DB) Delete(value interface{}, conds ...interface{}) (tx *DB) { func (db *DB) Delete(value interface{}, conds ...interface{}) (tx *DB) {
tx = db.getInstance() tx = db.getInstance()
if len(conds) > 0 { if len(conds) > 0 {
@ -480,7 +486,7 @@ func (db *DB) Rows() (*sql.Rows, error) {
return rows, tx.Error return rows, tx.Error
} }
// Scan scan value to a struct // Scan scans selected value to the struct dest
func (db *DB) Scan(dest interface{}) (tx *DB) { func (db *DB) Scan(dest interface{}) (tx *DB) {
config := *db.Config config := *db.Config
currentLogger, newLogger := config.Logger, logger.Recorder.New() currentLogger, newLogger := config.Logger, logger.Recorder.New()
@ -505,7 +511,7 @@ func (db *DB) Scan(dest interface{}) (tx *DB) {
return return
} }
// Pluck used to query single column from a model as a map // Pluck queries a single column from a model, returning in the slice dest. E.g.:
// var ages []int64 // var ages []int64
// db.Model(&users).Pluck("age", &ages) // db.Model(&users).Pluck("age", &ages)
func (db *DB) Pluck(column string, dest interface{}) (tx *DB) { func (db *DB) Pluck(column string, dest interface{}) (tx *DB) {
@ -548,7 +554,8 @@ func (db *DB) ScanRows(rows *sql.Rows, dest interface{}) error {
return tx.Error return tx.Error
} }
// Connection use a db conn to execute Multiple commands,this conn will put conn pool after it is executed. // Connection uses a db connection to execute an arbitrary number of commands in fc. When finished, the connection is
// returned to the connection pool.
func (db *DB) Connection(fc func(tx *DB) error) (err error) { func (db *DB) Connection(fc func(tx *DB) error) (err error) {
if db.Error != nil { if db.Error != nil {
return db.Error return db.Error
@ -570,7 +577,9 @@ func (db *DB) Connection(fc func(tx *DB) error) (err error) {
return fc(tx) return fc(tx)
} }
// Transaction start a transaction as a block, return error will rollback, otherwise to commit. // Transaction start a transaction as a block, return error will rollback, otherwise to commit. Transaction executes an
// arbitrary number of commands in fc within a transaction. On success the changes are committed; if an error occurs
// they are rolled back.
func (db *DB) Transaction(fc func(tx *DB) error, opts ...*sql.TxOptions) (err error) { func (db *DB) Transaction(fc func(tx *DB) error, opts ...*sql.TxOptions) (err error) {
panicked := true panicked := true
@ -613,7 +622,7 @@ func (db *DB) Transaction(fc func(tx *DB) error, opts ...*sql.TxOptions) (err er
return return
} }
// Begin begins a transaction // Begin begins a transaction with any transaction options opts
func (db *DB) Begin(opts ...*sql.TxOptions) *DB { func (db *DB) Begin(opts ...*sql.TxOptions) *DB {
var ( var (
// clone statement // clone statement
@ -642,7 +651,7 @@ func (db *DB) Begin(opts ...*sql.TxOptions) *DB {
return tx return tx
} }
// Commit commit a transaction // Commit commits the changes in a transaction
func (db *DB) Commit() *DB { func (db *DB) Commit() *DB {
if committer, ok := db.Statement.ConnPool.(TxCommitter); ok && committer != nil && !reflect.ValueOf(committer).IsNil() { if committer, ok := db.Statement.ConnPool.(TxCommitter); ok && committer != nil && !reflect.ValueOf(committer).IsNil() {
db.AddError(committer.Commit()) db.AddError(committer.Commit())
@ -652,7 +661,7 @@ func (db *DB) Commit() *DB {
return db return db
} }
// Rollback rollback a transaction // Rollback rollbacks the changes in a transaction
func (db *DB) Rollback() *DB { func (db *DB) Rollback() *DB {
if committer, ok := db.Statement.ConnPool.(TxCommitter); ok && committer != nil { if committer, ok := db.Statement.ConnPool.(TxCommitter); ok && committer != nil {
if !reflect.ValueOf(committer).IsNil() { if !reflect.ValueOf(committer).IsNil() {
@ -682,7 +691,7 @@ func (db *DB) RollbackTo(name string) *DB {
return db return db
} }
// Exec execute raw sql // Exec executes raw sql
func (db *DB) Exec(sql string, values ...interface{}) (tx *DB) { func (db *DB) Exec(sql string, values ...interface{}) (tx *DB) {
tx = db.getInstance() tx = db.getInstance()
tx.Statement.SQL = strings.Builder{} tx.Statement.SQL = strings.Builder{}

5
vendor/gorm.io/gorm/gorm.go generated vendored

@ -300,7 +300,8 @@ func (db *DB) WithContext(ctx context.Context) *DB {
// Debug start debug mode // Debug start debug mode
func (db *DB) Debug() (tx *DB) { func (db *DB) Debug() (tx *DB) {
return db.Session(&Session{ tx = db.getInstance()
return tx.Session(&Session{
Logger: db.Logger.LogMode(logger.Info), Logger: db.Logger.LogMode(logger.Info),
}) })
} }
@ -412,7 +413,7 @@ func (db *DB) SetupJoinTable(model interface{}, field string, joinTable interfac
relation, ok := modelSchema.Relationships.Relations[field] relation, ok := modelSchema.Relationships.Relations[field]
isRelation := ok && relation.JoinTable != nil isRelation := ok && relation.JoinTable != nil
if !isRelation { if !isRelation {
return fmt.Errorf("failed to found relation: %s", field) return fmt.Errorf("failed to find relation: %s", field)
} }
for _, ref := range relation.References { for _, ref := range relation.References {

@ -4,7 +4,7 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"io/ioutil" "io"
"log" "log"
"os" "os"
"time" "time"
@ -68,8 +68,8 @@ type Interface interface {
} }
var ( var (
// Discard Discard logger will print any log to ioutil.Discard // Discard Discard logger will print any log to io.Discard
Discard = New(log.New(ioutil.Discard, "", log.LstdFlags), Config{}) Discard = New(log.New(io.Discard, "", log.LstdFlags), Config{})
// Default Default logger // Default Default logger
Default = New(log.New(os.Stdout, "\r\n", log.LstdFlags), Config{ Default = New(log.New(os.Stdout, "\r\n", log.LstdFlags), Config{
SlowThreshold: 200 * time.Millisecond, SlowThreshold: 200 * time.Millisecond,

17
vendor/gorm.io/gorm/logger/sql.go generated vendored

@ -30,6 +30,8 @@ func isPrintable(s string) bool {
var convertibleTypes = []reflect.Type{reflect.TypeOf(time.Time{}), reflect.TypeOf(false), reflect.TypeOf([]byte{})} var convertibleTypes = []reflect.Type{reflect.TypeOf(time.Time{}), reflect.TypeOf(false), reflect.TypeOf([]byte{})}
var numericPlaceholderRe = regexp.MustCompile(`\$\d+\$`)
// ExplainSQL generate SQL string with given parameters, the generated SQL is expected to be used in logger, execute it might introduce a SQL injection vulnerability // ExplainSQL generate SQL string with given parameters, the generated SQL is expected to be used in logger, execute it might introduce a SQL injection vulnerability
func ExplainSQL(sql string, numericPlaceholder *regexp.Regexp, escaper string, avars ...interface{}) string { func ExplainSQL(sql string, numericPlaceholder *regexp.Regexp, escaper string, avars ...interface{}) string {
var ( var (
@ -138,9 +140,18 @@ func ExplainSQL(sql string, numericPlaceholder *regexp.Regexp, escaper string, a
sql = newSQL.String() sql = newSQL.String()
} else { } else {
sql = numericPlaceholder.ReplaceAllString(sql, "$$$1$$") sql = numericPlaceholder.ReplaceAllString(sql, "$$$1$$")
for idx, v := range vars {
sql = strings.Replace(sql, "$"+strconv.Itoa(idx+1)+"$", v, 1) sql = numericPlaceholderRe.ReplaceAllStringFunc(sql, func(v string) string {
} num := v[1 : len(v)-1]
n, _ := strconv.Atoi(num)
// position var start from 1 ($1, $2)
n -= 1
if n >= 0 && n <= len(vars)-1 {
return vars[n]
}
return v
})
} }
return sql return sql

@ -15,7 +15,7 @@ import (
) )
var ( var (
regFullDataType = regexp.MustCompile(`[^\d]*(\d+)[^\d]?`) regFullDataType = regexp.MustCompile(`\D*(\d+)\D?`)
) )
// Migrator m struct // Migrator m struct
@ -135,12 +135,12 @@ func (m Migrator) AutoMigrate(values ...interface{}) error {
} }
} }
} }
}
for _, chk := range stmt.Schema.ParseCheckConstraints() { for _, chk := range stmt.Schema.ParseCheckConstraints() {
if !tx.Migrator().HasConstraint(value, chk.Name) { if !tx.Migrator().HasConstraint(value, chk.Name) {
if err := tx.Migrator().CreateConstraint(value, chk.Name); err != nil { if err := tx.Migrator().CreateConstraint(value, chk.Name); err != nil {
return err return err
}
} }
} }
} }

38
vendor/gorm.io/gorm/scan.go generated vendored

@ -66,30 +66,32 @@ func (db *DB) scanIntoStruct(rows Rows, reflectValue reflect.Value, values []int
db.RowsAffected++ db.RowsAffected++
db.AddError(rows.Scan(values...)) db.AddError(rows.Scan(values...))
joinedSchemaMap := make(map[*schema.Field]interface{}, 0) joinedSchemaMap := make(map[*schema.Field]interface{})
for idx, field := range fields { for idx, field := range fields {
if field != nil { if field == nil {
if len(joinFields) == 0 || joinFields[idx][0] == nil { continue
db.AddError(field.Set(db.Statement.Context, reflectValue, values[idx])) }
} else {
joinSchema := joinFields[idx][0]
relValue := joinSchema.ReflectValueOf(db.Statement.Context, reflectValue)
if relValue.Kind() == reflect.Ptr {
if _, ok := joinedSchemaMap[joinSchema]; !ok {
if value := reflect.ValueOf(values[idx]).Elem(); value.Kind() == reflect.Ptr && value.IsNil() {
continue
}
relValue.Set(reflect.New(relValue.Type().Elem())) if len(joinFields) == 0 || joinFields[idx][0] == nil {
joinedSchemaMap[joinSchema] = nil db.AddError(field.Set(db.Statement.Context, reflectValue, values[idx]))
} else {
joinSchema := joinFields[idx][0]
relValue := joinSchema.ReflectValueOf(db.Statement.Context, reflectValue)
if relValue.Kind() == reflect.Ptr {
if _, ok := joinedSchemaMap[joinSchema]; !ok {
if value := reflect.ValueOf(values[idx]).Elem(); value.Kind() == reflect.Ptr && value.IsNil() {
continue
} }
relValue.Set(reflect.New(relValue.Type().Elem()))
joinedSchemaMap[joinSchema] = nil
} }
db.AddError(joinFields[idx][1].Set(db.Statement.Context, relValue, values[idx]))
} }
db.AddError(joinFields[idx][1].Set(db.Statement.Context, relValue, values[idx]))
// release data to pool
field.NewValuePool.Put(values[idx])
} }
// release data to pool
field.NewValuePool.Put(values[idx])
} }
} }

@ -403,18 +403,14 @@ func (schema *Schema) ParseField(fieldStruct reflect.StructField) *Field {
} }
if ef.PrimaryKey { if ef.PrimaryKey {
if val, ok := ef.TagSettings["PRIMARYKEY"]; ok && utils.CheckTruth(val) { if !utils.CheckTruth(ef.TagSettings["PRIMARYKEY"], ef.TagSettings["PRIMARY_KEY"]) {
ef.PrimaryKey = true
} else if val, ok := ef.TagSettings["PRIMARY_KEY"]; ok && utils.CheckTruth(val) {
ef.PrimaryKey = true
} else {
ef.PrimaryKey = false ef.PrimaryKey = false
if val, ok := ef.TagSettings["AUTOINCREMENT"]; !ok || !utils.CheckTruth(val) { if val, ok := ef.TagSettings["AUTOINCREMENT"]; !ok || !utils.CheckTruth(val) {
ef.AutoIncrement = false ef.AutoIncrement = false
} }
if ef.DefaultValue == "" { if !ef.AutoIncrement && ef.DefaultValue == "" {
ef.HasDefaultValue = false ef.HasDefaultValue = false
} }
} }
@ -472,9 +468,6 @@ func (field *Field) setupValuerAndSetter() {
oldValuerOf := field.ValueOf oldValuerOf := field.ValueOf
field.ValueOf = func(ctx context.Context, v reflect.Value) (interface{}, bool) { field.ValueOf = func(ctx context.Context, v reflect.Value) (interface{}, bool) {
value, zero := oldValuerOf(ctx, v) value, zero := oldValuerOf(ctx, v)
if zero {
return value, zero
}
s, ok := value.(SerializerValuerInterface) s, ok := value.(SerializerValuerInterface)
if !ok { if !ok {
@ -487,7 +480,7 @@ func (field *Field) setupValuerAndSetter() {
Destination: v, Destination: v,
Context: ctx, Context: ctx,
fieldValue: value, fieldValue: value,
}, false }, zero
} }
} }

@ -112,7 +112,7 @@ func ParseWithSpecialTableName(dest interface{}, cacheStore *sync.Map, namer Nam
schemaCacheKey = modelType schemaCacheKey = modelType
} }
// Load exist schmema cache, return if exists // Load exist schema cache, return if exists
if v, ok := cacheStore.Load(schemaCacheKey); ok { if v, ok := cacheStore.Load(schemaCacheKey); ok {
s := v.(*Schema) s := v.(*Schema)
// Wait for the initialization of other goroutines to complete // Wait for the initialization of other goroutines to complete
@ -146,7 +146,7 @@ func ParseWithSpecialTableName(dest interface{}, cacheStore *sync.Map, namer Nam
// When the schema initialization is completed, the channel will be closed // When the schema initialization is completed, the channel will be closed
defer close(schema.initialized) defer close(schema.initialized)
// Load exist schmema cache, return if exists // Load exist schema cache, return if exists
if v, ok := cacheStore.Load(schemaCacheKey); ok { if v, ok := cacheStore.Load(schemaCacheKey); ok {
s := v.(*Schema) s := v.(*Schema)
// Wait for the initialization of other goroutines to complete // Wait for the initialization of other goroutines to complete

@ -88,7 +88,9 @@ func (JSONSerializer) Scan(ctx context.Context, field *Field, dst reflect.Value,
return fmt.Errorf("failed to unmarshal JSONB value: %#v", dbValue) return fmt.Errorf("failed to unmarshal JSONB value: %#v", dbValue)
} }
err = json.Unmarshal(bytes, fieldValue.Interface()) if len(bytes) > 0 {
err = json.Unmarshal(bytes, fieldValue.Interface())
}
} }
field.ReflectValueOf(ctx, dst).Set(fieldValue.Elem()) field.ReflectValueOf(ctx, dst).Set(fieldValue.Elem())
@ -117,9 +119,15 @@ func (UnixSecondSerializer) Scan(ctx context.Context, field *Field, dst reflect.
// Value implements serializer interface // Value implements serializer interface
func (UnixSecondSerializer) Value(ctx context.Context, field *Field, dst reflect.Value, fieldValue interface{}) (result interface{}, err error) { func (UnixSecondSerializer) Value(ctx context.Context, field *Field, dst reflect.Value, fieldValue interface{}) (result interface{}, err error) {
rv := reflect.ValueOf(fieldValue)
switch v := fieldValue.(type) { switch v := fieldValue.(type) {
case int64, int, uint, uint64, int32, uint32, int16, uint16, *int64, *int, *uint, *uint64, *int32, *uint32, *int16, *uint16: case int64, int, uint, uint64, int32, uint32, int16, uint16:
result = time.Unix(reflect.Indirect(reflect.ValueOf(v)).Int(), 0) result = time.Unix(reflect.Indirect(rv).Int(), 0)
case *int64, *int, *uint, *uint64, *int32, *uint32, *int16, *uint16:
if rv.IsZero() {
return nil, nil
}
result = time.Unix(reflect.Indirect(rv).Int(), 0)
default: default:
err = fmt.Errorf("invalid field type %#v for UnixSecondSerializer, only int, uint supported", v) err = fmt.Errorf("invalid field type %#v for UnixSecondSerializer, only int, uint supported", v)
} }
@ -142,8 +150,10 @@ func (GobSerializer) Scan(ctx context.Context, field *Field, dst reflect.Value,
default: default:
return fmt.Errorf("failed to unmarshal gob value: %#v", dbValue) return fmt.Errorf("failed to unmarshal gob value: %#v", dbValue)
} }
decoder := gob.NewDecoder(bytes.NewBuffer(bytesValue)) if len(bytesValue) > 0 {
err = decoder.Decode(fieldValue.Interface()) decoder := gob.NewDecoder(bytes.NewBuffer(bytesValue))
err = decoder.Decode(fieldValue.Interface())
}
} }
field.ReflectValueOf(ctx, dst).Set(fieldValue.Elem()) field.ReflectValueOf(ctx, dst).Set(fieldValue.Elem())
return return

6
vendor/gorm.io/gorm/statement.go generated vendored

@ -650,7 +650,7 @@ func (stmt *Statement) Changed(fields ...string) bool {
return false return false
} }
var nameMatcher = regexp.MustCompile(`^[\W]?(?:[a-z_0-9]+?)[\W]?\.[\W]?([a-z_0-9]+?)[\W]?$`) var nameMatcher = regexp.MustCompile(`^(?:\W?(\w+?)\W?\.)?\W?(\w+?)\W?$`)
// SelectAndOmitColumns get select and omit columns, select -> true, omit -> false // SelectAndOmitColumns get select and omit columns, select -> true, omit -> false
func (stmt *Statement) SelectAndOmitColumns(requireCreate, requireUpdate bool) (map[string]bool, bool) { func (stmt *Statement) SelectAndOmitColumns(requireCreate, requireUpdate bool) (map[string]bool, bool) {
@ -672,8 +672,8 @@ func (stmt *Statement) SelectAndOmitColumns(requireCreate, requireUpdate bool) (
} }
} else if field := stmt.Schema.LookUpField(column); field != nil && field.DBName != "" { } else if field := stmt.Schema.LookUpField(column); field != nil && field.DBName != "" {
results[field.DBName] = true results[field.DBName] = true
} else if matches := nameMatcher.FindStringSubmatch(column); len(matches) == 2 { } else if matches := nameMatcher.FindStringSubmatch(column); len(matches) == 3 && (matches[1] == stmt.Table || matches[1] == "") {
results[matches[1]] = true results[matches[2]] = true
} else { } else {
results[column] = true results[column] = true
} }

42
vendor/modules.txt vendored

@ -32,7 +32,7 @@ github.com/go-playground/locales/currency
# github.com/go-playground/universal-translator v0.18.0 # github.com/go-playground/universal-translator v0.18.0
## explicit; go 1.13 ## explicit; go 1.13
github.com/go-playground/universal-translator github.com/go-playground/universal-translator
# github.com/go-playground/validator/v10 v10.11.0 # github.com/go-playground/validator/v10 v10.11.1
## explicit; go 1.13 ## explicit; go 1.13
github.com/go-playground/validator/v10 github.com/go-playground/validator/v10
# github.com/go-redis/redis/v9 v9.0.0-beta.2 # github.com/go-redis/redis/v9 v9.0.0-beta.2
@ -59,8 +59,6 @@ github.com/goccy/go-json/internal/encoder/vm_color_indent
github.com/goccy/go-json/internal/encoder/vm_indent github.com/goccy/go-json/internal/encoder/vm_indent
github.com/goccy/go-json/internal/errors github.com/goccy/go-json/internal/errors
github.com/goccy/go-json/internal/runtime github.com/goccy/go-json/internal/runtime
# github.com/golang-sql/sqlexp v0.1.0
## explicit; go 1.16
# github.com/golang/snappy v0.0.4 # github.com/golang/snappy v0.0.4
## explicit ## explicit
github.com/golang/snappy github.com/golang/snappy
@ -105,8 +103,8 @@ github.com/jinzhu/now
# github.com/json-iterator/go v1.1.12 # github.com/json-iterator/go v1.1.12
## explicit; go 1.12 ## explicit; go 1.12
github.com/json-iterator/go github.com/json-iterator/go
# github.com/klauspost/compress v1.15.9 # github.com/klauspost/compress v1.15.10
## explicit; go 1.16 ## explicit; go 1.17
github.com/klauspost/compress github.com/klauspost/compress
github.com/klauspost/compress/fse github.com/klauspost/compress/fse
github.com/klauspost/compress/huff0 github.com/klauspost/compress/huff0
@ -137,6 +135,12 @@ github.com/montanaflynn/stats
# github.com/natefinch/lumberjack v2.0.0+incompatible # github.com/natefinch/lumberjack v2.0.0+incompatible
## explicit ## explicit
github.com/natefinch/lumberjack github.com/natefinch/lumberjack
# github.com/oschwald/geoip2-golang v1.8.0
## explicit; go 1.18
github.com/oschwald/geoip2-golang
# github.com/oschwald/maxminddb-golang v1.10.0
## explicit; go 1.18
github.com/oschwald/maxminddb-golang
# github.com/pelletier/go-toml/v2 v2.0.5 # github.com/pelletier/go-toml/v2 v2.0.5
## explicit; go 1.16 ## explicit; go 1.16
github.com/pelletier/go-toml/v2 github.com/pelletier/go-toml/v2
@ -242,16 +246,18 @@ github.com/xdg-go/stringprep
# github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a # github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a
## explicit; go 1.12 ## explicit; go 1.12
github.com/youmark/pkcs8 github.com/youmark/pkcs8
# go.dtapp.net/dorm v1.0.33 # go.dtapp.net/dorm v1.0.38
## explicit; go 1.19 ## explicit; go 1.19
go.dtapp.net/dorm go.dtapp.net/dorm
# go.dtapp.net/goip v1.0.30 # go.dtapp.net/goip v1.0.36
## explicit; go 1.19 ## explicit; go 1.19
go.dtapp.net/goip go.dtapp.net/goip
go.dtapp.net/goip/geoip
go.dtapp.net/goip/ip2region go.dtapp.net/goip/ip2region
go.dtapp.net/goip/v4 go.dtapp.net/goip/ip2region_v2
go.dtapp.net/goip/v6 go.dtapp.net/goip/ipv6wry
# go.dtapp.net/golog v1.0.73 go.dtapp.net/goip/qqwry
# go.dtapp.net/golog v1.0.87
## explicit; go 1.19 ## explicit; go 1.19
go.dtapp.net/golog go.dtapp.net/golog
# go.dtapp.net/gorandom v1.0.1 # go.dtapp.net/gorandom v1.0.1
@ -272,8 +278,6 @@ go.dtapp.net/gotrace_id
# go.dtapp.net/gourl v1.0.0 # go.dtapp.net/gourl v1.0.0
## explicit; go 1.19 ## explicit; go 1.19
go.dtapp.net/gourl go.dtapp.net/gourl
# go.dtapp.net/goxml v1.0.1
## explicit; go 1.18
# go.mongodb.org/mongo-driver v1.10.2 # go.mongodb.org/mongo-driver v1.10.2
## explicit; go 1.10 ## explicit; go 1.10
go.mongodb.org/mongo-driver/bson go.mongodb.org/mongo-driver/bson
@ -326,7 +330,7 @@ go.uber.org/zap/internal/bufferpool
go.uber.org/zap/internal/color go.uber.org/zap/internal/color
go.uber.org/zap/internal/exit go.uber.org/zap/internal/exit
go.uber.org/zap/zapcore go.uber.org/zap/zapcore
# golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 # golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0
## explicit; go 1.17 ## explicit; go 1.17
golang.org/x/crypto/ocsp golang.org/x/crypto/ocsp
golang.org/x/crypto/pbkdf2 golang.org/x/crypto/pbkdf2
@ -335,7 +339,7 @@ golang.org/x/crypto/sha3
# golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 # golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4
## explicit; go 1.17 ## explicit; go 1.17
golang.org/x/mod/semver golang.org/x/mod/semver
# golang.org/x/net v0.0.0-20220909164309-bea034e7d591 # golang.org/x/net v0.0.0-20220920152717-4a395b0a80a1
## explicit; go 1.17 ## explicit; go 1.17
golang.org/x/net/html golang.org/x/net/html
golang.org/x/net/html/atom golang.org/x/net/html/atom
@ -348,7 +352,7 @@ golang.org/x/net/idna
# golang.org/x/sync v0.0.0-20220907140024-f12130a52804 # golang.org/x/sync v0.0.0-20220907140024-f12130a52804
## explicit ## explicit
golang.org/x/sync/errgroup golang.org/x/sync/errgroup
# golang.org/x/sys v0.0.0-20220913175220-63ea55921009 # golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8
## explicit; go 1.17 ## explicit; go 1.17
golang.org/x/sys/cpu golang.org/x/sys/cpu
golang.org/x/sys/internal/unsafeheader golang.org/x/sys/internal/unsafeheader
@ -398,16 +402,14 @@ google.golang.org/protobuf/runtime/protoiface
# gopkg.in/yaml.v2 v2.4.0 # gopkg.in/yaml.v2 v2.4.0
## explicit; go 1.15 ## explicit; go 1.15
gopkg.in/yaml.v2 gopkg.in/yaml.v2
# gorm.io/datatypes v1.0.7
## explicit; go 1.14
# gorm.io/driver/mysql v1.3.6 # gorm.io/driver/mysql v1.3.6
## explicit; go 1.14 ## explicit; go 1.14
gorm.io/driver/mysql gorm.io/driver/mysql
# gorm.io/driver/postgres v1.3.9 # gorm.io/driver/postgres v1.3.10
## explicit; go 1.14 ## explicit; go 1.14
gorm.io/driver/postgres gorm.io/driver/postgres
# gorm.io/gorm v1.23.8 # gorm.io/gorm v1.23.9
## explicit; go 1.14 ## explicit; go 1.16
gorm.io/gorm gorm.io/gorm
gorm.io/gorm/callbacks gorm.io/gorm/callbacks
gorm.io/gorm/clause gorm.io/gorm/clause

Loading…
Cancel
Save