Compare commits

...

105 Commits

Author SHA1 Message Date
dtapps 7b6e0fc4dc - optimization gorm
4 months ago
dtapps f0ebc78416 - update
5 months ago
dtapps 28f62d88e6 - add `DeleteData` function
5 months ago
dtapps c3f557e7f1 - update ip
5 months ago
dtapps 78c77ff6ad - update gin_gorm
5 months ago
dtapps d63a3e16d1 - update gin_slog
5 months ago
dtapps 0b68d2d075 - update mod
5 months ago
dtapps 0e2a64323d add slog
5 months ago
李光春 dffb6465a9 - update
1 year ago
李光春 9393e23930 - update mongo
2 years ago
李光春 8e0ffea774 - update time
2 years ago
李光春 bd3b2c9ba0 - update ip
2 years ago
李光春 0c597201ff - update
2 years ago
李光春 5e72ef4cd4 - update
2 years ago
李光春 a2030c149f - update system
2 years ago
李光春 359d8bf17b - update
2 years ago
李光春 b4009b5862 - update
2 years ago
李光春 f8a9284813 - update mongo
2 years ago
李光春 54b64a6175 - update
2 years ago
李光春 699b1a6e5f - update
2 years ago
李光春 a7e10e9661 - update db
2 years ago
李光春 592b43fc26 - update db
2 years ago
李光春 e05fcc3f4e - update mongo
2 years ago
李光春 4df2eab876 - update mongo
2 years ago
李光春 f38cafd8f7 - update mongo
2 years ago
李光春 694b738017 - update mongo
2 years ago
李光春 14f6ae68fe - update
2 years ago
李光春 7dead7925c - update ip
2 years ago
李光春 0fb387879e - update config
2 years ago
李光春 729bbf76c6 - update fun
2 years ago
李光春 5b328feee9 - update fun
2 years ago
李光春 10e31bb497 - add fun
2 years ago
李光春 3f0ab29c10 - update gorm
2 years ago
李光春 c4d22be7c9 - update mongo
2 years ago
李光春 0c37436a40 - update mongo
2 years ago
李光春 9027f79406 - update mongo
2 years ago
李光春 d34e9f1566 - update mongo
2 years ago
李光春 c8a251ca62 - update ip
2 years ago
李光春 b664b74537 - add delete
2 years ago
李光春 f900440188 - add vendor
2 years ago
李光春 f6ff746740 - update model
2 years ago
李光春 b2786dcae5 - update api ip
2 years ago
李光春 a9ae30ac48 - add api ip
2 years ago
李光春 105552f60e - update gorm
2 years ago
李光春 09d1f5322c - update
2 years ago
李光春 9ae52fb881 - update gorm
2 years ago
李光春 b491020dfb - update gin
2 years ago
李光春 ee6dd7c3ba - update gin
2 years ago
李光春 6d83b387ca - update
2 years ago
李光春 fabc48f5e5 - update
2 years ago
李光春 99d2f42cda - update json to string
2 years ago
李光春 61c3b48176 - update
2 years ago
李光春 5ad38c7138 - update debug
2 years ago
李光春 ab40cf6a20 - update
2 years ago
李光春 2556f543eb - update gorm
2 years ago
李光春 7e275ff8c6 - update gorm
2 years ago
李光春 f14ff39dde - update
2 years ago
李光春 cca4e98da6 - update gorm
2 years ago
李光春 6084e24c15 - update zap
2 years ago
李光春 621a70a569 - update
2 years ago
李光春 0e6ae9bd78 - update
2 years ago
李光春 0cbf7efddc - update zap
2 years ago
李光春 5eaaf329a8 - update
2 years ago
李光春 acbcfac098 - update
2 years ago
李光春 bfdf600dd3 - update api mongo
2 years ago
李光春 216598bc9d - update gorm
2 years ago
李光春 e6c2c7668b - update mongo
2 years ago
李光春 242112f779 - update mongo
2 years ago
李光春 04f079daf9 - update
2 years ago
李光春 138cb2a350 - update mongo time
2 years ago
李光春 6665e8597c - update mongo
2 years ago
李光春 e27626dd15 - update type
2 years ago
李光春 b615f8f1b1 - update gin
2 years ago
李光春 101ea45f6c - update gin
2 years ago
李光春 b7c4b649c6 - update gin mongo
2 years ago
李光春 5c291f12b8 - add mongo
2 years ago
李光春 d94deede67 - update log
2 years ago
李光春 90184bd169 - update log
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2 years ago
李光春 8707ba3981 - update log
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2 years ago
李光春 09a09e3888 - update
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2 years ago
李光春 510aa3e051 - update
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2 years ago
李光春 1d673bd7a2 - update gin
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2 years ago
李光春 b6ebc957a4 - update gin
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2 years ago
李光春 65ec9457b0 - update logrus.console
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2 years ago
李光春 54da153cab - update log
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2 years ago
李光春 5d338d96aa - update log
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2 years ago
李光春 8c6cc53950 - 删除mongo
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2 years ago
李光春 9d616ddad0 - 删除mongo
continuous-integration/drone/push Build is passing Details
2 years ago
李光春 ec422e395e - add error log
continuous-integration/drone/tag Build is passing Details
continuous-integration/drone/push Build was killed Details
2 years ago
李光春 ab7f32f5a6 - update
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2 years ago
李光春 ca988d4afd - update
continuous-integration/drone/push Build is failing Details
continuous-integration/drone/tag Build is failing Details
2 years ago
李光春 81d369d2aa - update
continuous-integration/drone/push Build is failing Details
2 years ago
李光春 73a2861132 - update middleware
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2 years ago
李光春 c0fde6aa14 - update api
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2 years ago
李光春 0105a46f00 - update api
2 years ago
李光春 54e83ece3b - update gorm
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2 years ago
李光春 55ce9d44a9 - update
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2 years ago
李光春 b2d2318593 - update
continuous-integration/drone/tag Build is passing Details
2 years ago
李光春 df4e46c01c - update gorm
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2 years ago
李光春 7b4d7fdf7f - update gorm
continuous-integration/drone/push Build is passing Details
2 years ago
李光春 872ec4ca88 - add gin_middleware
continuous-integration/drone/tag Build is passing Details
2 years ago
李光春 4e1213b336 - update
continuous-integration/drone/push Build is failing Details
continuous-integration/drone/tag Build is failing Details
2 years ago
李光春 7e4b091923 - update
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2 years ago
李光春 6143a4a7da - update vendor
2 years ago
李光春 3c5f96faab - update
2 years ago

3
.gitignore vendored

@ -5,5 +5,6 @@
.vscode
*.log
gomod.sh
*_test.go
mod.sh
/vendor/
time_test.go

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 李光春
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

@ -1,25 +1,17 @@
<h1>
<a href="https://www.dtapp.net/">Golang Log</a>
<a href="https://www.dtapp.net/">Golang</a>
</h1>
📦 Golang 日志组件
📦 Golang 日志
[comment]: <> (go)
[![godoc](https://pkg.go.dev/badge/go.dtapp.net/golog?status.svg)](https://pkg.go.dev/go.dtapp.net/golog)
[![goproxy.cn](https://goproxy.cn/stats/go.dtapp.net/golog/badges/download-count.svg)](https://goproxy.cn/stats/go.dtapp.net/golog)
[![goreportcard.com](https://goreportcard.com/badge/go.dtapp.net/golog )](https://goreportcard.com/report/go.dtapp.net/golog)
[![deps.dev](https://img.shields.io/badge/deps-go-red.svg)](https://deps.dev/go/go.dtapp.net/golog)
[![deps.dev](https://img.shields.io/badge/deps-go-red.svg)](https://deps.dev/go/go.dtapp.net%2Fgolog)
#### 安装使用
#### 安装
```go
go get -v -u go.dtapp.net/golog
```shell
go get -v -u go.dtapp.net/golog@v1.0.109
```
#### 导入
```go
import (
"go.dtapp.net/golog"
)
```

@ -1,93 +0,0 @@
package golog
import (
"go.dtapp.net/goip"
"gorm.io/datatypes"
"gorm.io/gorm"
"log"
"os"
"runtime"
"strings"
)
type ApiConfig struct {
Db *gorm.DB // 驱动
TableName string // 表名
}
// Api 接口
type Api struct {
db *gorm.DB // pgsql数据库
tableName string // 日志表名
insideIp string // 内网ip
hostname string // 主机名
goVersion string // go版本
}
// NewApi 创建接口实例化
func NewApi(config *ApiConfig) *Api {
app := &Api{}
if config.Db == nil {
panic("驱动不正常")
}
if config.TableName == "" {
panic("表名不能为空")
}
hostname, _ := os.Hostname()
app.db = config.Db
app.tableName = config.TableName
app.hostname = hostname
app.insideIp = goip.GetInsideIp()
app.goVersion = strings.TrimPrefix(runtime.Version(), "go")
app.AutoMigrate()
return app
}
// ApiPostgresqlLog 结构体
type ApiPostgresqlLog struct {
LogId uint `gorm:"primaryKey" json:"log_id"` //【记录】编号
RequestTime TimeString `gorm:"index" json:"request_time"` //【请求】时间
RequestUri string `gorm:"type:text" json:"request_uri"` //【请求】链接
RequestUrl string `gorm:"type:text" json:"request_url"` //【请求】链接
RequestApi string `gorm:"type:text;index" json:"request_api"` //【请求】接口
RequestMethod string `gorm:"type:text;index" json:"request_method"` //【请求】方式
RequestParams datatypes.JSON `gorm:"type:jsonb" json:"request_params"` //【请求】参数
RequestHeader datatypes.JSON `gorm:"type:jsonb" json:"request_header"` //【请求】头部
ResponseHeader datatypes.JSON `gorm:"type:jsonb" json:"response_header"` //【返回】头部
ResponseStatusCode int `gorm:"type:bigint" json:"response_status_code"` //【返回】状态码
ResponseBody datatypes.JSON `gorm:"type:jsonb" json:"response_body"` //【返回】内容
ResponseContentLength int64 `gorm:"type:bigint" json:"response_content_length"` //【返回】大小
ResponseTime TimeString `gorm:"index" json:"response_time"` //【返回】时间
SystemHostName string `gorm:"type:text" json:"system_host_name"` //【系统】主机名
SystemInsideIp string `gorm:"type:text" json:"system_inside_ip"` //【系统】内网ip
GoVersion string `gorm:"type:text" json:"go_version"` //【程序】Go版本
}
// AutoMigrate 自动迁移
func (p *Api) AutoMigrate() {
err := p.db.Table(p.tableName).AutoMigrate(&ApiPostgresqlLog{})
if err != nil {
panic("创建表失败:" + err.Error())
}
}
// Record 记录日志
func (p *Api) Record(content ApiPostgresqlLog) *gorm.DB {
content.SystemHostName = p.hostname
if content.SystemInsideIp == "" {
content.SystemInsideIp = p.insideIp
}
content.GoVersion = p.goVersion
resp := p.db.Table(p.tableName).Create(&content)
if resp.RowsAffected == 0 {
log.Println("Api", resp.Error)
}
return resp
}
// Query 查询
func (p *Api) Query() *gorm.DB {
return p.db.Table(p.tableName)
}

@ -0,0 +1,89 @@
package golog
import (
"context"
"errors"
"go.dtapp.net/gorequest"
"gorm.io/gorm"
)
// ApiGorm 接口日志
type ApiGorm struct {
gormClient *gorm.DB // 数据库驱动
config struct {
systemHostname string // 主机名
systemOs string // 系统类型
systemVersion string // 系统版本
systemKernel string // 系统内核
systemKernelVersion string // 系统内核版本
systemBootTime uint64 // 系统开机时间
cpuCores int // CPU核数
cpuModelName string // CPU型号名称
cpuMhz float64 // CPU兆赫
systemInsideIp string // 内网ip
systemOutsideIp string // 外网ip
goVersion string // go版本
sdkVersion string // sdk版本
}
gormConfig struct {
stats bool // 状态
tableName string // 表名
}
}
// ApiGormFun 接口日志驱动
type ApiGormFun func() *ApiGorm
// NewApiGorm 创建接口实例化
func NewApiGorm(ctx context.Context, systemOutsideIp string, gormClient *gorm.DB, gormTableName string) (*ApiGorm, error) {
gl := &ApiGorm{}
// 配置信息
if systemOutsideIp == "" {
return nil, errors.New("没有设置外网IP")
}
gl.setConfig(ctx, systemOutsideIp)
if gormClient == nil {
gl.gormConfig.stats = false
} else {
gl.gormClient = gormClient
if gormTableName == "" {
return nil, errors.New("没有设置表名")
} else {
gl.gormConfig.tableName = gormTableName
}
// 创建模型
gl.gormAutoMigrate(ctx)
gl.gormConfig.stats = true
}
return gl, nil
}
// Middleware 中间件
func (ag *ApiGorm) Middleware(ctx context.Context, request gorequest.Response) {
if ag.gormConfig.stats {
ag.gormMiddleware(ctx, request)
}
}
// MiddlewareXml 中间件
func (ag *ApiGorm) MiddlewareXml(ctx context.Context, request gorequest.Response) {
if ag.gormConfig.stats {
ag.gormMiddlewareXml(ctx, request)
}
}
// MiddlewareCustom 中间件
func (ag *ApiGorm) MiddlewareCustom(ctx context.Context, api string, request gorequest.Response) {
if ag.gormConfig.stats {
ag.gormMiddlewareCustom(ctx, api, request)
}
}

@ -0,0 +1,29 @@
package golog
import (
"context"
"go.dtapp.net/gorequest"
"runtime"
)
func (ag *ApiGorm) setConfig(ctx context.Context, systemOutsideIp string) {
info := getSystem()
ag.config.systemHostname = info.SystemHostname
ag.config.systemOs = info.SystemOs
ag.config.systemVersion = info.SystemVersion
ag.config.systemKernel = info.SystemKernel
ag.config.systemKernelVersion = info.SystemKernelVersion
ag.config.systemBootTime = info.SystemBootTime
ag.config.cpuCores = info.CpuCores
ag.config.cpuModelName = info.CpuModelName
ag.config.cpuMhz = info.CpuMhz
ag.config.systemInsideIp = gorequest.GetInsideIp(ctx)
ag.config.systemOutsideIp = systemOutsideIp
ag.config.goVersion = runtime.Version()
ag.config.sdkVersion = Version
}

@ -0,0 +1,24 @@
package golog
import (
"context"
"errors"
"go.dtapp.net/gotime"
)
// GormDeleteData 删除N天前数据
func (ag *ApiGorm) GormDeleteData(ctx context.Context, day int) error {
return ag.GormDeleteDataCustom(ctx, ag.gormConfig.tableName, day)
}
// GormDeleteDataCustom 删除N天前数据
func (ag *ApiGorm) GormDeleteDataCustom(ctx context.Context, tableName string, day int) error {
if ag.gormConfig.stats == false {
return nil
}
if tableName == "" {
return errors.New("没有设置表名")
}
return ag.gormClient.Table(tableName).Where("request_time < ?", gotime.Current().BeforeDay(day).Format()).Delete(&apiPostgresqlLog{}).Error
}

@ -0,0 +1,143 @@
package golog
import (
"context"
"go.dtapp.net/gojson"
"go.dtapp.net/gorequest"
"go.dtapp.net/gotrace_id"
"go.dtapp.net/gourl"
"log"
"time"
"unicode/utf8"
)
// 模型
type apiGormLog struct {
LogID int64 `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:"default:0.0.0.0;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_body,omitempty"` //【返回】数据
ResponseTime time.Time `gorm:"index;comment:【返回】时间" json:"response_time,omitempty"` //【返回】时间
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
SystemOs string `gorm:"index;comment:【系统】系统类型" json:"system_os,omitempty"` //【系统】系统类型
SystemArch string `gorm:"index;comment:【系统】系统架构" json:"system_arch,omitempty"` //【系统】系统架构
GoVersion string `gorm:"comment:【程序】Go版本" json:"go_version,omitempty"` //【程序】Go版本
SdkVersion string `gorm:"comment:【程序】Sdk版本" json:"sdk_version,omitempty"` //【程序】Sdk版本
}
// 创建模型
func (ag *ApiGorm) gormAutoMigrate(ctx context.Context) {
if ag.gormConfig.stats == false {
return
}
err := ag.gormClient.Table(ag.gormConfig.tableName).AutoMigrate(&apiGormLog{})
if err != nil {
log.Printf("创建模型:%s", err)
}
}
// 记录日志
func (ag *ApiGorm) gormRecord(ctx context.Context, data apiGormLog) {
if ag.gormConfig.stats == false {
return
}
if utf8.ValidString(data.ResponseBody) == false {
data.ResponseBody = ""
}
data.SystemHostName = ag.config.systemHostname //【系统】主机名
data.SystemInsideIp = ag.config.systemInsideIp //【系统】内网ip
data.GoVersion = ag.config.goVersion //【程序】Go版本
data.SdkVersion = ag.config.systemVersion //【程序】Sdk版本
data.TraceID = gotrace_id.GetTraceIdContext(ctx) //【记录】跟踪编号
data.RequestIp = ag.config.systemOutsideIp //【请求】请求Ip
data.SystemOs = ag.config.systemOs //【系统】系统类型
data.SystemArch = ag.config.systemKernel //【系统】系统架构
err := ag.gormClient.Table(ag.gormConfig.tableName).Create(&data).Error
if err != nil {
log.Printf("记录接口日志错误:%s", err)
log.Printf("记录接口日志数据:%+v", data)
}
}
// 中间件
func (ag *ApiGorm) gormMiddleware(ctx context.Context, request gorequest.Response) {
data := apiGormLog{
RequestTime: request.RequestTime, //【请求】时间
RequestUri: request.RequestUri, //【请求】链接
RequestUrl: gourl.UriParse(request.RequestUri).Url, //【请求】链接
RequestApi: gourl.UriParse(request.RequestUri).Path, //【请求】接口
RequestMethod: request.RequestMethod, //【请求】方式
RequestParams: gojson.JsonEncodeNoError(request.RequestParams), //【请求】参数
RequestHeader: gojson.JsonEncodeNoError(request.RequestHeader), //【请求】头部
ResponseHeader: gojson.JsonEncodeNoError(request.ResponseHeader), //【返回】头部
ResponseStatusCode: request.ResponseStatusCode, //【返回】状态码
ResponseTime: request.ResponseTime, //【返回】时间
}
if !request.HeaderIsImg() {
if len(request.ResponseBody) > 0 {
data.ResponseBody = gojson.JsonEncodeNoError(gojson.JsonDecodeNoError(string(request.ResponseBody))) //【返回】数据
}
}
ag.gormRecord(ctx, data)
}
// 中间件
func (ag *ApiGorm) gormMiddlewareXml(ctx context.Context, request gorequest.Response) {
data := apiGormLog{
RequestTime: request.RequestTime, //【请求】时间
RequestUri: request.RequestUri, //【请求】链接
RequestUrl: gourl.UriParse(request.RequestUri).Url, //【请求】链接
RequestApi: gourl.UriParse(request.RequestUri).Path, //【请求】接口
RequestMethod: request.RequestMethod, //【请求】方式
RequestParams: gojson.JsonEncodeNoError(request.RequestParams), //【请求】参数
RequestHeader: gojson.JsonEncodeNoError(request.RequestHeader), //【请求】头部
ResponseHeader: gojson.JsonEncodeNoError(request.ResponseHeader), //【返回】头部
ResponseStatusCode: request.ResponseStatusCode, //【返回】状态码
ResponseTime: request.ResponseTime, //【返回】时间
}
if !request.HeaderIsImg() {
if len(request.ResponseBody) > 0 {
data.ResponseBody = gojson.XmlEncodeNoError(gojson.XmlDecodeNoError(request.ResponseBody)) //【返回】内容
}
}
ag.gormRecord(ctx, data)
}
// 中间件
func (ag *ApiGorm) gormMiddlewareCustom(ctx context.Context, api string, request gorequest.Response) {
data := apiGormLog{
RequestTime: request.RequestTime, //【请求】时间
RequestUri: request.RequestUri, //【请求】链接
RequestUrl: gourl.UriParse(request.RequestUri).Url, //【请求】链接
RequestApi: api, //【请求】接口
RequestMethod: request.RequestMethod, //【请求】方式
RequestParams: gojson.JsonEncodeNoError(request.RequestParams), //【请求】参数
RequestHeader: gojson.JsonEncodeNoError(request.RequestHeader), //【请求】头部
ResponseHeader: gojson.JsonEncodeNoError(request.ResponseHeader), //【返回】头部
ResponseStatusCode: request.ResponseStatusCode, //【返回】状态码
ResponseTime: request.ResponseTime, //【返回】时间
}
if !request.HeaderIsImg() {
if len(request.ResponseBody) > 0 {
data.ResponseBody = gojson.JsonEncodeNoError(gojson.JsonDecodeNoError(string(request.ResponseBody))) //【返回】数据
}
}
ag.gormRecord(ctx, data)
}

@ -0,0 +1,21 @@
package golog
import (
"context"
)
// ApiSLog 接口日志
type ApiSLog struct {
slog struct {
status bool // 状态
client *SLog // 日志服务
}
}
// ApiSLogFun 接口日志驱动
type ApiSLogFun func() *ApiSLog
func NewApiSlog(ctx context.Context) *ApiSLog {
sl := &ApiSLog{}
return sl
}

@ -0,0 +1,20 @@
package golog
// ConfigSLogClientFun 日志配置
func (al *ApiSLog) ConfigSLogClientFun(sLogFun SLogFun) {
sLog := sLogFun()
if sLog != nil {
al.slog.client = sLog
al.slog.status = true
}
}
// ConfigSLogResultClientFun 日志配置然后返回
func (al *ApiSLog) ConfigSLogResultClientFun(sLogFun SLogFun) *ApiSLog {
sLog := sLogFun()
if sLog != nil {
al.slog.client = sLog
al.slog.status = true
}
return al
}

@ -0,0 +1,125 @@
package golog
import (
"context"
"go.dtapp.net/gojson"
"go.dtapp.net/gorequest"
"go.dtapp.net/gotrace_id"
"go.dtapp.net/gourl"
"time"
)
// 结构体
type apiSLog struct {
TraceID string `json:"trace_id,omitempty"`
RequestTime time.Time `json:"request_time,omitempty"`
RequestUri string `json:"request_uri,omitempty"`
RequestUrl string `json:"request_url,omitempty"`
RequestApi string `json:"request_api,omitempty"`
RequestMethod string `json:"request_method,omitempty"`
RequestParams map[string]interface{} `json:"request_params,omitempty"`
RequestHeader map[string]string `json:"request_header,omitempty"`
ResponseHeader map[string][]string `json:"response_header,omitempty"`
ResponseStatusCode int `json:"response_status_code,omitempty"`
ResponseBody map[string]interface{} `json:"response_body,omitempty"`
ResponseTime time.Time `json:"response_time,omitempty,omitempty"`
}
// Middleware 中间件
func (al *ApiSLog) Middleware(ctx context.Context, request gorequest.Response) {
data := apiSLog{
TraceID: gotrace_id.GetTraceIdContext(ctx),
RequestTime: request.RequestTime,
RequestUri: request.RequestUri,
RequestUrl: gourl.UriParse(request.RequestUri).Url,
RequestApi: gourl.UriParse(request.RequestUri).Path,
RequestMethod: request.RequestMethod,
RequestParams: request.RequestParams,
RequestHeader: request.RequestHeader,
ResponseHeader: request.ResponseHeader,
ResponseStatusCode: request.ResponseStatusCode,
ResponseBody: gojson.JsonDecodeNoError(string(request.ResponseBody)),
ResponseTime: request.ResponseTime,
}
if al.slog.status {
al.slog.client.WithTraceId(ctx).Info("Middleware",
"request_time", data.RequestTime,
"request_uri", data.RequestUri,
"request_url", data.RequestUrl,
"request_api", data.RequestApi,
"request_method", data.RequestMethod,
"request_params", data.RequestParams,
"request_header", data.RequestHeader,
"response_header", data.ResponseHeader,
"response_status_code", data.ResponseStatusCode,
"response_body", data.ResponseBody,
"response_time", data.ResponseTime,
)
}
}
// MiddlewareXml 中间件
func (al *ApiSLog) MiddlewareXml(ctx context.Context, request gorequest.Response) {
data := apiSLog{
TraceID: gotrace_id.GetTraceIdContext(ctx),
RequestTime: request.RequestTime,
RequestUri: request.RequestUri,
RequestUrl: gourl.UriParse(request.RequestUri).Url,
RequestApi: gourl.UriParse(request.RequestUri).Path,
RequestMethod: request.RequestMethod,
RequestParams: request.RequestParams,
RequestHeader: request.RequestHeader,
ResponseHeader: request.ResponseHeader,
ResponseStatusCode: request.ResponseStatusCode,
ResponseBody: gojson.XmlDecodeNoError(request.ResponseBody),
ResponseTime: request.ResponseTime,
}
if al.slog.status {
al.slog.client.WithTraceId(ctx).Info("MiddlewareXml",
"request_time", data.RequestTime,
"request_uri", data.RequestUri,
"request_url", data.RequestUrl,
"request_api", data.RequestApi,
"request_method", data.RequestMethod,
"request_params", data.RequestParams,
"request_header", data.RequestHeader,
"response_header", data.ResponseHeader,
"response_status_code", data.ResponseStatusCode,
"response_body", data.ResponseBody,
"response_time", data.ResponseTime,
)
}
}
// MiddlewareCustom 中间件
func (al *ApiSLog) MiddlewareCustom(ctx context.Context, api string, request gorequest.Response) {
data := apiSLog{
TraceID: gotrace_id.GetTraceIdContext(ctx),
RequestTime: request.RequestTime,
RequestUri: request.RequestUri,
RequestUrl: gourl.UriParse(request.RequestUri).Url,
RequestApi: api,
RequestMethod: request.RequestMethod,
RequestParams: request.RequestParams,
RequestHeader: request.RequestHeader,
ResponseHeader: request.ResponseHeader,
ResponseStatusCode: request.ResponseStatusCode,
ResponseBody: gojson.JsonDecodeNoError(string(request.ResponseBody)),
ResponseTime: request.ResponseTime,
}
if al.slog.status {
al.slog.client.WithTraceId(ctx).Info("MiddlewareCustom",
"request_time", data.RequestTime,
"request_uri", data.RequestUri,
"request_url", data.RequestUrl,
"request_api", data.RequestApi,
"request_method", data.RequestMethod,
"request_params", data.RequestParams,
"request_header", data.RequestHeader,
"response_header", data.ResponseHeader,
"response_status_code", data.ResponseStatusCode,
"response_body", data.ResponseBody,
"response_time", data.ResponseTime,
)
}
}

@ -0,0 +1,47 @@
package golog
import (
"github.com/shirou/gopsutil/cpu"
"github.com/shirou/gopsutil/host"
)
type systemResult struct {
SystemHostname string // 主机名
SystemOs string // 系统类型
SystemVersion string // 系统版本
SystemKernel string // 系统内核
SystemKernelVersion string // 系统内核版本
SystemUpTime uint64 // 系统运行时间
SystemBootTime uint64 // 系统开机时间
CpuCores int // CPU核数
CpuModelName string // CPU型号名称
CpuMhz float64 // CPU兆赫
}
func getSystem() (result systemResult) {
hInfo, _ := host.Info()
result.SystemHostname = hInfo.Hostname
result.SystemOs = hInfo.OS
result.SystemVersion = hInfo.PlatformVersion
result.SystemKernel = hInfo.KernelArch
result.SystemKernelVersion = hInfo.KernelVersion
result.SystemUpTime = hInfo.Uptime
if hInfo.BootTime != 0 {
result.SystemBootTime = hInfo.BootTime
}
hCpu, _ := cpu.Times(true)
result.CpuCores = len(hCpu)
cInfo, _ := cpu.Info()
if len(cInfo) > 0 {
result.CpuModelName = cInfo[0].ModelName
result.CpuMhz = cInfo[0].Mhz
}
return result
}

104
gin.go

@ -1,104 +0,0 @@
package golog
import (
"go.dtapp.net/goip"
"gorm.io/datatypes"
"gorm.io/gorm"
"log"
"os"
"runtime"
"strings"
)
type GinConfig struct {
Db *gorm.DB // 驱动
TableName string // 表名
}
// Gin 框架
type Gin struct {
db *gorm.DB // pgsql数据库
tableName string // 日志表名
insideIp string // 内网ip
hostname string // 主机名
goVersion string // go版本
}
// NewGin 创建框架实例化
func NewGin(config *GinConfig) *Gin {
app := &Gin{}
if config.Db == nil {
panic("驱动不正常")
}
if config.TableName == "" {
panic("表名不能为空")
}
hostname, _ := os.Hostname()
app.db = config.Db
app.tableName = config.TableName
app.hostname = hostname
app.insideIp = goip.GetInsideIp()
app.goVersion = strings.TrimPrefix(runtime.Version(), "go")
app.AutoMigrate()
return app
}
// GinPostgresqlLog 结构体
type GinPostgresqlLog struct {
LogId uint `gorm:"primaryKey" json:"log_id"` //【记录】编号
TraceId string `gorm:"type:text" json:"trace_id"` //【系统】链编号
RequestTime TimeString `gorm:"index" json:"request_time"` //【请求】时间
RequestUri string `gorm:"type:text" json:"request_uri"` //【请求】请求链接 域名+路径+参数
RequestUrl string `gorm:"type:text" json:"request_url"` //【请求】请求链接 域名+路径
RequestApi string `gorm:"type:text;index" json:"request_api"` //【请求】请求接口 路径
RequestMethod string `gorm:"type:text;index" json:"request_method"` //【请求】请求方式
RequestProto string `gorm:"type:text" json:"request_proto"` //【请求】请求协议
RequestUa string `gorm:"type:text" json:"request_ua"` //【请求】请求UA
RequestReferer string `gorm:"type:text" json:"request_referer"` //【请求】请求referer
RequestBody datatypes.JSON `gorm:"type:jsonb" json:"request_body"` //【请求】请求主体
RequestUrlQuery datatypes.JSON `gorm:"type:jsonb" json:"request_url_query"` //【请求】请求URL参数
RequestIp string `gorm:"type:text" json:"request_ip"` //【请求】请求客户端Ip
RequestIpCountry string `gorm:"type:text" json:"request_ip_country"` //【请求】请求客户端城市
RequestIpRegion string `gorm:"type:text" json:"request_ip_region"` //【请求】请求客户端区域
RequestIpProvince string `gorm:"type:text" json:"request_ip_province"` //【请求】请求客户端省份
RequestIpCity string `gorm:"type:text" json:"request_ip_city"` //【请求】请求客户端城市
RequestIpIsp string `gorm:"type:text" json:"request_ip_isp"` //【请求】请求客户端运营商
RequestHeader datatypes.JSON `gorm:"type:jsonb" json:"request_header"` //【请求】请求头
ResponseTime TimeString `gorm:"index" json:"response_time"` //【返回】时间
ResponseCode int `gorm:"type:bigint" json:"response_code"` //【返回】状态码
ResponseMsg string `gorm:"type:text" json:"response_msg"` //【返回】描述
ResponseData datatypes.JSON `gorm:"type:jsonb" json:"response_data"` //【返回】数据
CostTime int64 `gorm:"type:bigint" json:"cost_time"` //【系统】花费时间
SystemHostName string `gorm:"type:text" json:"system_host_name"` //【系统】主机名
SystemInsideIp string `gorm:"type:text" json:"system_inside_ip"` //【系统】内网ip
GoVersion string `gorm:"type:text" json:"go_version"` //【程序】Go版本
}
// AutoMigrate 自动迁移
func (p *Gin) AutoMigrate() {
err := p.db.Table(p.tableName).AutoMigrate(&GinPostgresqlLog{})
if err != nil {
panic("创建表失败:" + err.Error())
}
}
// Record 记录日志
func (p *Gin) Record(content GinPostgresqlLog) *gorm.DB {
content.SystemHostName = p.hostname
if content.SystemInsideIp == "" {
content.SystemInsideIp = p.insideIp
}
content.GoVersion = p.goVersion
resp := p.db.Table(p.tableName).Create(&content)
if resp.RowsAffected == 0 {
log.Println("Gin", resp.Error)
}
return resp
}
// Query 查询
func (p *Gin) Query() *gorm.DB {
return p.db.Table(p.tableName)
}

@ -0,0 +1,145 @@
package golog
import (
"bytes"
"context"
"errors"
"github.com/gin-gonic/gin"
"go.dtapp.net/gojson"
"go.dtapp.net/gorequest"
"go.dtapp.net/gotime"
"gorm.io/gorm"
"io/ioutil"
)
// GinGorm 框架日志
type GinGorm struct {
gormClient *gorm.DB // 数据库驱动
config struct {
systemHostname string // 主机名
systemOs string // 系统类型
systemVersion string // 系统版本
systemKernel string // 系统内核
systemKernelVersion string // 系统内核版本
systemBootTime uint64 // 系统开机时间
cpuCores int // CPU核数
cpuModelName string // CPU型号名称
cpuMhz float64 // CPU兆赫
systemInsideIp string // 内网ip
systemOutsideIp string // 外网ip
goVersion string // go版本
sdkVersion string // sdk版本
}
gormConfig struct {
stats bool // 状态
tableName string // 表名
}
}
// GinGormFun *GinGorm 框架日志驱动
type GinGormFun func() *GinGorm
// NewGinGorm 创建框架实例化
func NewGinGorm(ctx context.Context, systemOutsideIp string, gormClient *gorm.DB, gormTableName string) (*GinGorm, error) {
gg := &GinGorm{}
// 配置信息
if systemOutsideIp == "" {
return nil, errors.New("没有设置外网IP")
}
gg.setConfig(ctx, systemOutsideIp)
if gormClient == nil {
gg.gormConfig.stats = false
} else {
gg.gormClient = gormClient
if gormTableName == "" {
return nil, errors.New("没有设置表名")
} else {
gg.gormConfig.tableName = gormTableName
}
// 创建模型
gg.gormAutoMigrate(ctx)
gg.gormConfig.stats = true
}
return gg, nil
}
type bodyGormWriter struct {
gin.ResponseWriter
body *bytes.Buffer
}
func (w bodyGormWriter) Write(b []byte) (int, error) {
w.body.Write(b)
return w.ResponseWriter.Write(b)
}
func (w bodyGormWriter) WriteString(s string) (int, error) {
w.body.WriteString(s)
return w.ResponseWriter.WriteString(s)
}
func (gg *GinGorm) jsonUnmarshal(data string) (result interface{}) {
_ = gojson.Unmarshal([]byte(data), &result)
return
}
// Middleware 中间件
func (gg *GinGorm) Middleware() gin.HandlerFunc {
return func(ginCtx *gin.Context) {
// 开始时间
startTime := gotime.Current().TimestampWithMillisecond()
requestTime := gotime.Current().Time
// 获取全部内容
requestBody := gorequest.NewParams()
queryParams := ginCtx.Request.URL.Query() // 请求URL参数
for key, values := range queryParams {
for _, value := range values {
requestBody.Set(key, value)
}
}
var dataMap map[string]interface{}
rawData, _ := ginCtx.GetRawData() // 请求内容参数
if gojson.IsValidJSON(string(rawData)) {
dataMap = gojson.JsonDecodeNoError(string(rawData))
} else {
dataMap = gojson.ParseQueryString(string(rawData))
}
for key, value := range dataMap {
requestBody.Set(key, value)
}
// 重新赋值
ginCtx.Request.Body = ioutil.NopCloser(bytes.NewBuffer(rawData))
blw := &bodyGormWriter{body: bytes.NewBufferString(""), ResponseWriter: ginCtx.Writer}
ginCtx.Writer = blw
// 处理请求
ginCtx.Next()
// 响应
responseCode := ginCtx.Writer.Status()
responseBody := blw.body.String()
// 结束时间
endTime := gotime.Current().TimestampWithMillisecond()
responseTime := gotime.Current().Time
go func() {
// 记录
gg.recordJson(ginCtx, requestTime, requestBody, responseTime, responseCode, responseBody, endTime-startTime, gorequest.ClientIp(ginCtx.Request))
}()
}
}

@ -0,0 +1,29 @@
package golog
import (
"context"
"go.dtapp.net/gorequest"
"runtime"
)
func (gg *GinGorm) setConfig(ctx context.Context, systemOutsideIp string) {
info := getSystem()
gg.config.systemHostname = info.SystemHostname
gg.config.systemOs = info.SystemOs
gg.config.systemVersion = info.SystemVersion
gg.config.systemKernel = info.SystemKernel
gg.config.systemKernelVersion = info.SystemKernelVersion
gg.config.systemBootTime = info.SystemBootTime
gg.config.cpuCores = info.CpuCores
gg.config.cpuModelName = info.CpuModelName
gg.config.cpuMhz = info.CpuMhz
gg.config.systemInsideIp = gorequest.GetInsideIp(ctx)
gg.config.systemOutsideIp = systemOutsideIp
gg.config.sdkVersion = Version
gg.config.goVersion = runtime.Version()
}

@ -0,0 +1,24 @@
package golog
import (
"context"
"errors"
"go.dtapp.net/gotime"
)
// GormDeleteData 删除N天前数据
func (gg *GinGorm) GormDeleteData(ctx context.Context, day int) error {
return gg.GormDeleteDataCustom(ctx, gg.gormConfig.tableName, day)
}
// GormDeleteDataCustom 删除N天前数据
func (gg *GinGorm) GormDeleteDataCustom(ctx context.Context, tableName string, day int) error {
if gg.gormConfig.stats == false {
return nil
}
if tableName == "" {
return errors.New("没有设置表名")
}
return gg.gormClient.Table(tableName).Where("request_time < ?", gotime.Current().BeforeDay(day).Format()).Delete(&ginPostgresqlLog{}).Error
}

@ -0,0 +1,95 @@
package golog
import (
"context"
"github.com/gin-gonic/gin"
"go.dtapp.net/gojson"
"go.dtapp.net/gorequest"
"go.dtapp.net/gotrace_id"
"go.dtapp.net/gourl"
"log"
"time"
)
// 模型
type ginGormLog struct {
LogID int64 `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"` //【请求】协议
RequestBody string `gorm:"comment:【请求】参数" json:"request_body,omitempty"` //【请求】参数
RequestIP string `gorm:"index;comment:【请求】客户端IP" json:"request_ip,omitempty"` //【请求】客户端IP
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"` //【返回】状态码
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:"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"` //【系统】系统架构
GoVersion string `gorm:"comment:【程序】Go版本" json:"go_version,omitempty"` //【程序】Go版本
SdkVersion string `gorm:"comment:【程序】Sdk版本" json:"sdk_version,omitempty"` //【程序】Sdk版本
}
// 创建模型
func (gg *GinGorm) gormAutoMigrate(ctx context.Context) {
if gg.gormConfig.stats == false {
return
}
err := gg.gormClient.Table(gg.gormConfig.tableName).AutoMigrate(&ginGormLog{})
if err != nil {
log.Printf("创建模型:%s\n", err)
}
}
// gormRecord 记录日志
func (gg *GinGorm) gormRecord(data ginGormLog) {
if gg.gormConfig.stats == false {
return
}
data.SystemHostName = gg.config.systemHostname //【系统】主机名
data.SystemInsideIP = gg.config.systemInsideIp //【系统】内网ip
data.GoVersion = gg.config.goVersion //【程序】Go版本
data.SdkVersion = gg.config.sdkVersion //【程序】Sdk版本
data.SystemOs = gg.config.systemOs //【系统】系统类型
data.SystemArch = gg.config.systemKernel //【系统】系统架构
err := gg.gormClient.Table(gg.gormConfig.tableName).Create(&data).Error
if err != nil {
log.Printf("记录框架日志错误:%s\n", err)
log.Printf("记录框架日志数据:%+v\n", data)
}
}
func (gg *GinGorm) recordJson(ginCtx *gin.Context, requestTime time.Time, requestBody gorequest.Params, responseTime time.Time, responseCode int, responseBody string, costTime int64, requestIp string) {
data := ginGormLog{
TraceID: gotrace_id.GetGinTraceId(ginCtx), //【系统】跟踪编号
RequestTime: requestTime, //【请求】时间
RequestURL: ginCtx.Request.RequestURI, //【请求】链接
RequestApi: gourl.UriFilterExcludeQueryString(ginCtx.Request.RequestURI), //【请求】接口
RequestMethod: ginCtx.Request.Method, //【请求】方式
RequestProto: ginCtx.Request.Proto, //【请求】协议
RequestBody: gojson.JsonEncodeNoError(requestBody), //【请求】参数
RequestIP: requestIp, //【请求】客户端IP
RequestHeader: gojson.JsonEncodeNoError(ginCtx.Request.Header), //【请求】头部
ResponseTime: responseTime, //【返回】时间
ResponseCode: responseCode, //【返回】状态码
ResponseData: responseBody, //【返回】数据
CostTime: costTime, //【系统】花费时间
}
if ginCtx.Request.TLS == nil {
data.RequestUri = "http://" + ginCtx.Request.Host + ginCtx.Request.RequestURI //【请求】链接
} else {
data.RequestUri = "https://" + ginCtx.Request.Host + ginCtx.Request.RequestURI //【请求】链接
}
gg.gormRecord(data)
}

@ -0,0 +1,101 @@
package golog
import (
"bytes"
"context"
"github.com/gin-gonic/gin"
"go.dtapp.net/gojson"
"go.dtapp.net/gorequest"
"go.dtapp.net/gotime"
"io/ioutil"
)
// GinSLog 框架日志
type GinSLog struct {
slog struct {
status bool // 状态
client *SLog // 日志服务
}
}
// GinSLogFun 框架日志驱动
type GinSLogFun func() *GinSLog
// NewGinSLog 创建框架实例化
func NewGinSLog(ctx context.Context) (*GinSLog, error) {
c := &GinSLog{}
return c, nil
}
type bodySLogWriter struct {
gin.ResponseWriter
body *bytes.Buffer
}
func (w bodySLogWriter) Write(b []byte) (int, error) {
w.body.Write(b)
return w.ResponseWriter.Write(b)
}
func (w bodySLogWriter) WriteString(s string) (int, error) {
w.body.WriteString(s)
return w.ResponseWriter.WriteString(s)
}
func (gl *GinSLog) jsonUnmarshal(data string) (result interface{}) {
_ = gojson.Unmarshal([]byte(data), &result)
return
}
// Middleware 中间件
func (gl *GinSLog) Middleware() gin.HandlerFunc {
return func(ginCtx *gin.Context) {
// 开始时间
startTime := gotime.Current().TimestampWithMillisecond()
requestTime := gotime.Current().Time
// 获取全部内容
requestAllContent := gorequest.NewParams()
queryParams := ginCtx.Request.URL.Query() // 请求URL参数
for key, values := range queryParams {
for _, value := range values {
requestAllContent.Set(key, value)
}
}
var dataMap map[string]interface{}
rawData, _ := ginCtx.GetRawData() // 请求内容参数
if gojson.IsValidJSON(string(rawData)) {
dataMap = gojson.JsonDecodeNoError(string(rawData))
} else {
dataMap = gojson.ParseQueryString(string(rawData))
}
for key, value := range dataMap {
requestAllContent.Set(key, value)
}
// 重新赋值
ginCtx.Request.Body = ioutil.NopCloser(bytes.NewBuffer(rawData))
blw := &bodySLogWriter{body: bytes.NewBufferString(""), ResponseWriter: ginCtx.Writer}
ginCtx.Writer = blw
// 处理请求
ginCtx.Next()
// 响应
responseCode := ginCtx.Writer.Status()
responseBody := blw.body.String()
// 结束时间
endTime := gotime.Current().TimestampWithMillisecond()
responseTime := gotime.Current().Time
go func() {
// 记录
gl.recordJson(ginCtx, requestTime, requestAllContent, responseTime, responseCode, responseBody, endTime-startTime, gorequest.ClientIp(ginCtx.Request))
}()
}
}

@ -0,0 +1,20 @@
package golog
// ConfigSLogClientFun 日志配置
func (gl *GinSLog) ConfigSLogClientFun(sLogFun SLogFun) {
sLog := sLogFun()
if sLog != nil {
gl.slog.client = sLog
gl.slog.status = true
}
}
// ConfigSLogResultClientFun 日志配置然后返回
func (gl *GinSLog) ConfigSLogResultClientFun(sLogFun SLogFun) *GinSLog {
sLog := sLogFun()
if sLog != nil {
gl.slog.client = sLog
gl.slog.status = true
}
return gl
}

@ -0,0 +1,22 @@
package golog
import (
"context"
)
// GinSLogCustom 框架自定义日志
type GinSLogCustom struct {
slog struct {
status bool // 状态
client *SLog // 日志服务
}
}
// GinSLogCustomFun 框架自定义日志驱动
type GinSLogCustomFun func() *GinSLogCustom
// NewGinSLogCustom 创建框架实例化
func NewGinSLogCustom(ctx context.Context) (*GinSLogCustom, error) {
c := &GinSLogCustom{}
return c, nil
}

@ -0,0 +1,20 @@
package golog
// ConfigSLogClientFun 日志配置
func (c *GinSLogCustom) ConfigSLogClientFun(sLogFun SLogFun) {
sLog := sLogFun()
if sLog != nil {
c.slog.client = sLog
c.slog.status = true
}
}
// ConfigSLogResultClientFun 日志配置然后返回
func (c *GinSLogCustom) ConfigSLogResultClientFun(sLogFun SLogFun) *GinSLogCustom {
sLog := sLogFun()
if sLog != nil {
c.slog.client = sLog
c.slog.status = true
}
return c
}

@ -0,0 +1,100 @@
package golog
import (
"fmt"
"github.com/gin-gonic/gin"
"go.dtapp.net/gojson"
"go.dtapp.net/gorequest"
"go.dtapp.net/gotrace_id"
"go.dtapp.net/gourl"
)
// 结构体
type ginSLogCustom struct {
TraceID string `json:"trace_id,omitempty"` //【系统】跟踪编号
RequestUri string `json:"request_uri,omitempty"` //【请求】请求链接 域名+路径+参数
RequestUrl string `json:"request_url,omitempty"` //【请求】请求链接 域名+路径
RequestApi string `json:"request_api,omitempty"` //【请求】请求接口 路径
RequestMethod string `json:"request_method,omitempty"` //【请求】请求方式
RequestProto string `json:"request_proto,omitempty"` //【请求】请求协议
RequestUa string `json:"request_ua,omitempty"` //【请求】请求UA
RequestReferer string `json:"request_referer,omitempty"` //【请求】请求referer
RequestUrlQuery string `json:"request_url_query,omitempty"` //【请求】请求URL参数
RequestHeader string `json:"request_header,omitempty"` //【请求】请求头
RequestIP string `json:"request_ip,omitempty"` //【请求】请求客户端IP
CustomID string `json:"custom_id,omitempty"` //【日志】自定义编号
CustomType string `json:"custom_type,omitempty"` //【日志】自定义类型
CustomContent string `json:"custom_content,omitempty"` //【日志】自定义内容
}
type GinCustomClientGinRecordOperation struct {
slogClient *SLog // 日志服务
data *ginSLogCustom // 数据
}
// GinRecord 记录日志
func (c *GinSLogCustom) GinRecord(ginCtx *gin.Context) *GinCustomClientGinRecordOperation {
operation := &GinCustomClientGinRecordOperation{
slogClient: c.slog.client,
}
operation.data = new(ginSLogCustom)
operation.data.TraceID = gotrace_id.GetGinTraceId(ginCtx) // 【系统】跟踪编号
if ginCtx.Request.TLS == nil {
operation.data.RequestUri = "http://" + ginCtx.Request.Host + ginCtx.Request.RequestURI //【请求】请求链接
} else {
operation.data.RequestUri = "https://" + ginCtx.Request.Host + ginCtx.Request.RequestURI //【请求】请求链接
}
operation.data.RequestUrl = ginCtx.Request.RequestURI //【请求】请求链接 域名+路径
operation.data.RequestApi = gourl.UriFilterExcludeQueryString(ginCtx.Request.RequestURI) //【请求】请求接口 路径
operation.data.RequestMethod = ginCtx.Request.Method //【请求】请求方式
operation.data.RequestProto = ginCtx.Request.Proto //【请求】请求协议
operation.data.RequestUa = ginCtx.Request.UserAgent() //【请求】请求UA
operation.data.RequestReferer = ginCtx.Request.Referer() //【请求】请求referer
operation.data.RequestUrlQuery = gojson.JsonEncodeNoError(ginCtx.Request.URL.Query()) //【请求】请求URL参数
operation.data.RequestHeader = gojson.JsonEncodeNoError(ginCtx.Request.Header) //【请求】请求头
operation.data.RequestIP = gorequest.ClientIp(ginCtx.Request) //【请求】请求客户端Ip
return operation
}
func (o *GinCustomClientGinRecordOperation) CustomInfo(customId any, customType any, customContent any) *GinCustomClientGinRecordOperation {
o.data.CustomID = fmt.Sprintf("%s", customId) //【日志】自定义编号
o.data.CustomType = fmt.Sprintf("%s", customType) //【日志】自定义类型
o.data.CustomContent = fmt.Sprintf("%s", customContent) //【日志】自定义内容
return o
}
func (o *GinCustomClientGinRecordOperation) CreateData() {
o.slogClient.WithTraceIdStr(o.data.TraceID).Info("custom",
"request_uri", o.data.RequestUri,
"request_url", o.data.RequestUrl,
"request_api", o.data.RequestApi,
"request_method", o.data.RequestMethod,
"request_proto", o.data.RequestProto,
"request_ua", o.data.RequestUa,
"request_referer", o.data.RequestReferer,
"request_url_query", o.data.RequestUrlQuery,
"request_header", o.data.RequestHeader,
"request_ip", o.data.RequestIP,
"custom_id", o.data.CustomID,
"custom_type", o.data.CustomType,
"custom_content", o.data.CustomContent,
)
}
func (o *GinCustomClientGinRecordOperation) CreateDataNoError() {
o.slogClient.WithTraceIdStr(o.data.TraceID).Info("custom",
"request_uri", o.data.RequestUri,
"request_url", o.data.RequestUrl,
"request_api", o.data.RequestApi,
"request_method", o.data.RequestMethod,
"request_proto", o.data.RequestProto,
"request_ua", o.data.RequestUa,
"request_referer", o.data.RequestReferer,
"request_url_query", o.data.RequestUrlQuery,
"request_header", o.data.RequestHeader,
"request_ip", o.data.RequestIP,
"custom_id", o.data.CustomID,
"custom_type", o.data.CustomType,
"custom_content", o.data.CustomContent,
)
}

@ -0,0 +1,70 @@
package golog
import (
"github.com/gin-gonic/gin"
"go.dtapp.net/gorequest"
"go.dtapp.net/gotrace_id"
"go.dtapp.net/gourl"
"time"
)
// 结构体
type ginSLog struct {
TraceID string `json:"trace_id,omitempty"` //【系统】跟踪编号
RequestTime time.Time `json:"request_time,omitempty"` //【请求】时间
RequestUri string `json:"request_uri,omitempty"` //【请求】请求链接 域名+路径+参数
RequestUrl string `json:"request_url,omitempty"` //【请求】请求链接 域名+路径
RequestApi string `json:"request_api,omitempty"` //【请求】请求接口 路径
RequestMethod string `json:"request_method,omitempty"` //【请求】请求方式
RequestProto string `json:"request_proto,omitempty"` //【请求】请求协议
RequestBody map[string]interface{} `json:"request_body,omitempty"` //【请求】请求参数
RequestIP string `json:"request_ip,omitempty"` //【请求】请求客户端IP
RequestHeader map[string][]string `json:"request_header,omitempty"` //【请求】请求头
ResponseTime time.Time `json:"response_time,omitempty"` //【返回】时间
ResponseCode int `json:"response_code,omitempty"` //【返回】状态码
ResponseData string `json:"response_data,omitempty"` //【返回】数据
CostTime int64 `json:"cost_time,omitempty"` //【系统】花费时间
}
// record 记录日志
func (gl *GinSLog) record(msg string, data ginSLog) {
gl.slog.client.WithTraceIdStr(data.TraceID).Info(msg,
"request_time", data.RequestTime,
"request_uri", data.RequestUri,
"request_url", data.RequestUrl,
"request_api", data.RequestApi,
"request_method", data.RequestMethod,
"request_proto", data.RequestProto,
"request_body", data.RequestBody,
"request_ip", data.RequestIP,
"request_header", data.RequestHeader,
"response_time", data.ResponseTime,
"response_code", data.ResponseCode,
"response_data", data.ResponseData,
"cost_time", data.CostTime,
)
}
func (gl *GinSLog) recordJson(ginCtx *gin.Context, requestTime time.Time, request_body gorequest.Params, responseTime time.Time, responseCode int, responseBody string, costTime int64, requestIp string) {
data := ginSLog{
TraceID: gotrace_id.GetGinTraceId(ginCtx), //【系统】跟踪编号
RequestTime: requestTime, //【请求】时间
RequestUrl: ginCtx.Request.RequestURI, //【请求】请求链接
RequestApi: gourl.UriFilterExcludeQueryString(ginCtx.Request.RequestURI), //【请求】请求接口
RequestMethod: ginCtx.Request.Method, //【请求】请求方式
RequestProto: ginCtx.Request.Proto, //【请求】请求协议
RequestBody: request_body, //【请求】请求参数
RequestIP: requestIp, //【请求】请求客户端IP
RequestHeader: ginCtx.Request.Header, //【请求】请求头
ResponseTime: responseTime, //【返回】时间
ResponseCode: responseCode, //【返回】状态码
ResponseData: responseBody, //【返回】数据
CostTime: costTime, //【系统】花费时间
}
if ginCtx.Request.TLS == nil {
data.RequestUri = "http://" + ginCtx.Request.Host + ginCtx.Request.RequestURI //【请求】请求链接
} else {
data.RequestUri = "https://" + ginCtx.Request.Host + ginCtx.Request.RequestURI //【请求】请求链接
}
gl.record("json", data)
}

@ -1,39 +1,57 @@
module go.dtapp.net/golog
go 1.18
go 1.21.5
require (
github.com/gin-gonic/gin v1.9.1
github.com/natefinch/lumberjack v2.0.0+incompatible
go.dtapp.net/goip v1.0.17
go.dtapp.net/gotime v1.0.2
go.uber.org/zap v1.21.0
gorm.io/datatypes v1.0.6
gorm.io/driver/postgres v1.3.7
gorm.io/gorm v1.23.6
github.com/shirou/gopsutil v3.21.11+incompatible
go.dtapp.net/gojson v1.0.4
go.dtapp.net/gorequest v1.0.46
go.dtapp.net/gotime v1.0.6
go.dtapp.net/gotrace_id v1.0.8
go.dtapp.net/gourl v1.0.0
gorm.io/gorm v1.25.5
)
require (
github.com/go-sql-driver/mysql v1.6.0 // indirect
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
github.com/jackc/pgconn v1.12.1 // indirect
github.com/jackc/pgio v1.0.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgproto3/v2 v2.3.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
github.com/jackc/pgtype v1.11.0 // indirect
github.com/jackc/pgx/v4 v4.16.1 // indirect
github.com/BurntSushi/toml v1.3.2 // indirect
github.com/MercuryEngineering/CookieMonster v0.0.0-20180304172713-1584578b3403 // indirect
github.com/basgys/goxml2json v1.1.0 // indirect
github.com/bytedance/sonic v1.10.2 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
github.com/chenzhuoyu/iasm v0.9.1 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.16.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/saracen/go7z v0.0.0-20191010121135-9c09b6bd7fda // indirect
github.com/saracen/solidblock v0.0.0-20190426153529-45df20abab6f // indirect
github.com/ulikunitz/xz v0.5.10 // indirect
go.dtapp.net/gorequest v1.0.19 // indirect
go.dtapp.net/gostring v1.0.3 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.8.0 // indirect
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/tools v0.1.11 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
gorm.io/driver/mysql v1.3.4 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
github.com/tklauser/go-sysconf v0.3.13 // indirect
github.com/tklauser/numcpus v0.7.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect
github.com/yusufpapurcu/wmi v1.2.3 // indirect
go.dtapp.net/gorandom v1.0.2 // indirect
go.dtapp.net/gostring v1.0.13 // indirect
golang.org/x/arch v0.6.0 // indirect
golang.org/x/crypto v0.17.0 // indirect
golang.org/x/net v0.19.0 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/text v0.14.0 // indirect
google.golang.org/protobuf v1.32.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

392
go.sum

@ -1,286 +1,142 @@
github.com/Azure/azure-sdk-for-go/sdk/azcore v0.19.0/go.mod h1:h6H6c8enJmmocHUbLiiGY6sx7f9i+X3m1CHdd5c6Rdw=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.11.0/go.mod h1:HcM1YX14R7CJcghJGOYCgdezslRSVzqwLf/q+4Y2r/0=
github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2TzfVZ1pCb5vxm4BtZPUdYWe/Xo8=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/MercuryEngineering/CookieMonster v0.0.0-20180304172713-1584578b3403 h1:EtZwYyLbkEcIt+B//6sujwRCnHuTEK3qiSypAX5aJeM=
github.com/MercuryEngineering/CookieMonster v0.0.0-20180304172713-1584578b3403/go.mod h1:mM6WvakkX2m+NgMiPCfFFjwfH4KzENC07zeGEqq9U7s=
github.com/basgys/goxml2json v1.1.0 h1:4ln5i4rseYfXNd86lGEB+Vi652IsIXIvggKM/BhUKVw=
github.com/basgys/goxml2json v1.1.0/go.mod h1:wH7a5Np/Q4QoECFIU8zTQlZwZkrilY0itPfecMw41Dw=
github.com/bitly/go-simplejson v0.5.1 h1:xgwPbetQScXt1gh9BmoJ6j9JMr3TElvuIyjR8pgdoow=
github.com/bitly/go-simplejson v0.5.1/go.mod h1:YOPVLzCfwK14b4Sff3oP1AmGhI9T9Vsg84etUnlyp+Q=
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
github.com/bytedance/sonic v1.10.2 h1:GQebETVBxYB7JGWJtLBi07OVzWwt+8dWA00gEVW2ZFE=
github.com/bytedance/sonic v1.10.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0=
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA=
github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0=
github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/denisenkom/go-mssqldb v0.12.0 h1:VtrkII767ttSPNRfFekePK3sctr+joXgO58stqQbtUA=
github.com/denisenkom/go-mssqldb v0.12.0/go.mod h1:iiK0YP1ZeepvmBQk/QpLEhhTNJgfzrpArPY/aFvc9yU=
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188 h1:+eHOFJl1BaXrQxKX+T06f78590z4qA2ZzBTqahsKSE4=
github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188/go.mod h1:vXjM/+wXQnTPR4KqTKDgJukSZ6amVRtWMPEjE6sQoK8=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0=
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=
github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=
github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=
github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
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.10.1/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
github.com/jackc/pgconn v1.11.0/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
github.com/jackc/pgconn v1.12.1 h1:rsDFzIpRk7xT4B8FufgpCCeyjdNpKyghZeSefViE5W8=
github.com/jackc/pgconn v1.12.1/go.mod h1:ZkhRC59Llhrq3oSfrikvwQ5NaxYExr6twkdkMLaKono=
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c=
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc=
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A=
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
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.2.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.3.0 h1:brH0pCGBDkBW07HWlN/oSBXrmo3WB0UvZd1pIuDcL8Y=
github.com/jackc/pgproto3/v2 v2.3.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=
github.com/jackc/pgtype v1.9.1/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
github.com/jackc/pgtype v1.10.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
github.com/jackc/pgtype v1.11.0 h1:u4uiGPz/1hryuXzyaBhSk6dnIyyG2683olG2OV+UUgs=
github.com/jackc/pgtype v1.11.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-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
github.com/jackc/pgx/v4 v4.14.1/go.mod h1:RgDuE4Z34o7XE92RpLsvFiOEfrAUT0Xt2KxvX73W06M=
github.com/jackc/pgx/v4 v4.15.0/go.mod h1:D/zyOyXiaM1TmVWnOM18p0xdDtdakRBa0RsVGI3U3bw=
github.com/jackc/pgx/v4 v4.16.1 h1:JzTglcal01DrghUqt+PmzWsZx/Yh7SC/CTQmSBMTd0Y=
github.com/jackc/pgx/v4 v4.16.1/go.mod h1:SIhx0D5hoADaiXZVyv+3gSm3LCIIINTVO0PficsvWGQ=
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.2.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.2.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.16.0 h1:x+plE831WK4vaKHO/jpgUGsvLKIqRRkz6M78GuJAfGE=
github.com/go-playground/validator/v10 v10.16.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8=
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.12 h1:TJ1bhYJPV44phC+IMu1u2K/i5RriLTPe+yc68XDJ1Z0=
github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM=
github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk=
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
github.com/saracen/go7z v0.0.0-20191010121135-9c09b6bd7fda h1:h+YpzUB/bGVJcLqW+d5GghcCmE/A25KbzjXvWJQi/+o=
github.com/saracen/go7z v0.0.0-20191010121135-9c09b6bd7fda/go.mod h1:MSotTrCv1PwoR8QgU1JurEx+lNNbtr25I+m0zbLyAGw=
github.com/saracen/go7z-fixtures v0.0.0-20190623165746-aa6b8fba1d2f h1:PF9WV5j/x6MT+x/sauUHd4objCvJbZb0wdxZkHSdd5A=
github.com/saracen/solidblock v0.0.0-20190426153529-45df20abab6f h1:1cJITU3JUI8qNS5T0BlXwANsVdyoJQHQ4hvOxbunPCw=
github.com/saracen/solidblock v0.0.0-20190426153529-45df20abab6f/go.mod h1:LyBTue+RWeyIfN3ZJ4wVxvDuvlGJtDgCLgCb6HCPgps=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8=
github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
go.dtapp.net/goip v1.0.17 h1:xkUSTwMNjx1uc4MCs6vmSmiDNlcFNlCTA1tqrM9fmAY=
go.dtapp.net/goip v1.0.17/go.mod h1:VrFnytj/KJH81m7Hx43mekbqJvsHbiNOS1Ikm2XaFFw=
go.dtapp.net/gorequest v1.0.19 h1:ZBkXb/oD59aChfzHfReK6M3M4eu3dwpaPTefe1c8P90=
go.dtapp.net/gorequest v1.0.19/go.mod h1:EwOfdfxsWPszOWrphCWHTN4DbYtU6fyQ/fuWQyQwSnk=
go.dtapp.net/gostring v1.0.3 h1:KSOq4D77/g5yZN/bqWfZ0kOOaPr/P1240vg03+XdENI=
go.dtapp.net/gostring v1.0.3/go.mod h1:+ggrOvgQDQturi1QGsXEpyRN/ZPoRDaqhMujIk5lrgQ=
go.dtapp.net/gotime v1.0.2 h1:CFIJHQXC/4t9bsJhk2cLhjHd6rpdPcJXr8BcHKHDuQo=
go.dtapp.net/gotime v1.0.2/go.mod h1:Gq7eNLr2iMLP18UNWONRq4V3Uhf/ADp4bIrS+Tc6ktY=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8=
go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8=
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
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-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898 h1:SLP7Q4Di66FONjDJbCYrCRrh97focO6sLogHO7/g8F0=
golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM=
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 h1:id054HUawV2/6IGm2IV8KZQjqtwAOo2CYlOToYqa0d0=
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/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.11 h1:loJ25fNOEhSXfHrpoGj91eCUThwdNX6u24rO1xnNteY=
golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/tklauser/go-sysconf v0.3.13 h1:GBUpcahXSpR2xN01jhkNAbTLRk2Yzgggk8IM08lq3r4=
github.com/tklauser/go-sysconf v0.3.13/go.mod h1:zwleP4Q4OehZHGn4CYZDipCgg9usW5IJePewFCGVEa0=
github.com/tklauser/numcpus v0.7.0 h1:yjuerZP127QG9m5Zh/mSO4wqurYil27tHrqwRoRjpr4=
github.com/tklauser/numcpus v0.7.0/go.mod h1:bb6dMVcj8A42tSE7i32fsIUCbQNllK5iDguyOZRUzAY=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.dtapp.net/gojson v1.0.4 h1:9en9iyOOLWoEIo2eKhqt3/Djh/3HhwsTpXxgI9efPRo=
go.dtapp.net/gojson v1.0.4/go.mod h1:G9CMVzNSRkbNzIic/vJqHCOyKtw6BW2YM8Vyn64zfM0=
go.dtapp.net/gorandom v1.0.2 h1:08BdcBP/fQiRw2Ii0OXATSTtObwNydlDlqc/j/u5O8Q=
go.dtapp.net/gorandom v1.0.2/go.mod h1:ZPdgalKpvFV/ATQqR0k4ns/F/IpITAZpx6WkWirr5Y8=
go.dtapp.net/gorequest v1.0.46 h1:fnUTRMbxqTGxSkiO0sMAks01WuMTDlDoVqa/SU1htEI=
go.dtapp.net/gorequest v1.0.46/go.mod h1:Nx1FM3yiKwCDaWABxEup5/GFwh+vJC+suAj7ek/zAtg=
go.dtapp.net/gostring v1.0.13 h1:Z4R6f9q9arQlkSGNywVQssxxv0LkjHsRjHt/hfwO6/M=
go.dtapp.net/gostring v1.0.13/go.mod h1:qgEjuf0/TFC3ZtvZ9y5tWHBSmV1GmlKiRxhJYJih9uk=
go.dtapp.net/gotime v1.0.6 h1:f8YowUxpZtJbYawe5s5PmvGxRj61ydlzxAPYr5Fcetg=
go.dtapp.net/gotime v1.0.6/go.mod h1:Gq7eNLr2iMLP18UNWONRq4V3Uhf/ADp4bIrS+Tc6ktY=
go.dtapp.net/gotrace_id v1.0.8 h1:cNsPYzOz55B8SKit1smz5b03clr3CVaSCrFns9oOruU=
go.dtapp.net/gotrace_id v1.0.8/go.mod h1:tJzdWgm+8baWZa8K84GdiD99iPguXrn1CehFSh2W0OM=
go.dtapp.net/gourl v1.0.0 h1:Zbe0GiMFyyRy2+bjbVsYulakH5d58w3CDZkUPonlMoQ=
go.dtapp.net/gourl v1.0.0/go.mod h1:x9A/pJ3iKDTb6Gu2RtJy2iHg56IowXIcIGQdogqpGjs=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.6.0 h1:S0JTfE48HbRj80+4tbvZDYsJ3tGv6BUU3XxyZ7CirAc=
golang.org/x/arch v0.6.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U=
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/datatypes v1.0.6 h1:3cqbakp1DIgC+P7wyODb5k+lSjW8g3mjkg/BIsmhjlE=
gorm.io/datatypes v1.0.6/go.mod h1:Gh/Xd/iUWWybMEk8CzYCK/swqlni2r+ROeM1HGIM0ck=
gorm.io/driver/mysql v1.3.2/go.mod h1:ChK6AHbHgDCFZyJp0F+BmVGb06PSIoh9uVYKAlRbb2U=
gorm.io/driver/mysql v1.3.4 h1:/KoBMgsUHC3bExsekDcmNYaBnfH2WNeFuXqqrqMc98Q=
gorm.io/driver/mysql v1.3.4/go.mod h1:s4Tq0KmD0yhPGHbZEwg1VPlH0vT/GBHJZorPzhcxBUE=
gorm.io/driver/postgres v1.3.1/go.mod h1:WwvWOuR9unCLpGWCL6Y3JOeBWvbKi6JLhayiVclSZZU=
gorm.io/driver/postgres v1.3.6 h1:Q0iLoYvWwsJVpYQrSrY5p5P4YzW7fJjFMBG2sa4Bz5U=
gorm.io/driver/postgres v1.3.6/go.mod h1:f02ympjIcgtHEGFMZvdgTxODZ9snAHDb4hXfigBVuNI=
gorm.io/driver/postgres v1.3.7 h1:FKF6sIMDHDEvvMF/XJvbnCl0nu6KSKUaPXevJ4r+VYQ=
gorm.io/driver/postgres v1.3.7/go.mod h1:f02ympjIcgtHEGFMZvdgTxODZ9snAHDb4hXfigBVuNI=
gorm.io/driver/sqlite v1.3.1 h1:bwfE+zTEWklBYoEodIOIBwuWHpnx52Z9zJFW5F33WLk=
gorm.io/driver/sqlite v1.3.1/go.mod h1:wJx0hJspfycZ6myN38x1O/AqLtNS6c5o9TndewFbELg=
gorm.io/driver/sqlserver v1.3.1 h1:F5t6ScMzOgy1zukRTIZgLZwKahgt3q1woAILVolKpOI=
gorm.io/driver/sqlserver v1.3.1/go.mod h1:w25Vrx2BG+CJNUu/xKbFhaKlGxT/nzRkhWCCoptX8tQ=
gorm.io/gorm v1.23.1/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
gorm.io/gorm v1.23.2/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
gorm.io/gorm v1.23.4/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
gorm.io/gorm v1.23.6 h1:KFLdNgri4ExFFGTRGGFWON2P1ZN28+9SJRN8voOoYe0=
gorm.io/gorm v1.23.6/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls=
gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

@ -1,127 +0,0 @@
package golog
import (
"github.com/natefinch/lumberjack"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"os"
"time"
)
type ConfigGoLog struct {
LogPath string // 日志文件路径
LogName string // 日志文件名
LogLevel string // 日志级别 debug/info/warn/errordebug输出debug/info/warn/error日志。 info输出info/warn/error日志。 warn输出warn/error日志。 error输出error日志。
MaxSize int // 单个文件大小,MB
MaxBackups int // 保存的文件个数
MaxAge int // 保存的天数 0=不删除
Compress bool // 压缩
JsonFormat bool // 是否输出为json格式
ShowLine bool // 显示代码行
LogInConsole bool // 是否同时输出到控制台
}
type GoLog struct {
ConfigGoLog
Logger *zap.Logger
}
func NewGoLog(config *ConfigGoLog) *GoLog {
g := &GoLog{}
g.LogPath = config.LogPath
g.LogName = config.LogName
g.LogLevel = config.LogLevel
g.MaxSize = config.MaxSize
g.MaxBackups = config.MaxBackups
g.MaxAge = config.MaxAge
g.Compress = config.Compress
g.JsonFormat = config.JsonFormat
g.ShowLine = config.ShowLine
g.LogInConsole = config.LogInConsole
// 设置日志级别
var level zapcore.Level
switch g.LogLevel {
case "debug":
level = zap.DebugLevel
case "info":
level = zap.InfoLevel
case "warn":
level = zap.WarnLevel
case "error":
level = zap.ErrorLevel
default:
level = zap.InfoLevel
}
var (
syncer zapcore.WriteSyncer
// 自定义时间输出格式
customTimeEncoder = func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
enc.AppendString(t.Format("2006-01-02 15:04:05.000"))
}
// 自定义日志级别显示
customLevelEncoder = func(level zapcore.Level, enc zapcore.PrimitiveArrayEncoder) {
enc.AppendString(level.CapitalString())
}
)
// 定义日志切割配置
hook := lumberjack.Logger{
Filename: g.LogPath + g.LogName, // 日志文件的位置
MaxSize: g.MaxSize, // 在进行切割之前日志文件的最大大小以MB为单位
MaxBackups: g.MaxBackups, // 保留旧文件的最大个数
Compress: g.Compress, // 是否压缩 disabled by default
}
if g.MaxAge > 0 {
hook.MaxAge = g.MaxAge // days
}
// 判断是否控制台输出日志
if g.LogInConsole {
syncer = zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout), zapcore.AddSync(&hook))
} else {
syncer = zapcore.AddSync(&hook)
}
// 定义zap配置信息
encoderConfig := zapcore.EncoderConfig{
TimeKey: "time",
LevelKey: "level",
NameKey: "logger",
CallerKey: "line",
MessageKey: "msg",
StacktraceKey: "stacktrace",
LineEnding: zapcore.DefaultLineEnding,
EncodeTime: customTimeEncoder, // 自定义时间格式
EncodeLevel: customLevelEncoder, // 小写编码器
EncodeCaller: zapcore.ShortCallerEncoder, // 全路径编码器
EncodeDuration: zapcore.SecondsDurationEncoder,
EncodeName: zapcore.FullNameEncoder,
}
var encoder zapcore.Encoder
// 判断是否json格式输出
if g.JsonFormat {
encoder = zapcore.NewJSONEncoder(encoderConfig)
} else {
encoder = zapcore.NewConsoleEncoder(encoderConfig)
}
core := zapcore.NewCore(
encoder,
syncer,
level,
)
g.Logger = zap.New(core)
// 判断是否显示代码行号
if g.ShowLine {
g.Logger = g.Logger.WithOptions(zap.AddCaller())
}
return g
}

@ -1,31 +0,0 @@
package golog
import (
"log"
"testing"
)
func TestLog(t *testing.T) {
g := NewGoLog(&ConfigGoLog{
LogPath: "./",
LogName: "all.log",
LogLevel: "debug",
MaxSize: 2,
MaxBackups: 30,
MaxAge: 0,
Compress: false,
JsonFormat: false,
ShowLine: true,
LogInConsole: true,
})
log.Println(g.Logger)
g.Logger.Debug("debug 日志")
g.Logger.Sugar().Debug("debug 日志")
g.Logger.Info("info 日志")
g.Logger.Sugar().Info("info 日志")
g.Logger.Warn("warning 日志")
g.Logger.Sugar().Warn("warning 日志")
g.Logger.Error("error 日志")
g.Logger.Sugar().Error("error 日志")
log.Println(g.Logger)
}

@ -1,8 +0,0 @@
package golog
import "encoding/json"
func JsonEncodeNoError(data interface{}) string {
jsons, _ := json.Marshal(data)
return string(jsons)
}

@ -0,0 +1,131 @@
package golog
import (
"context"
"github.com/natefinch/lumberjack"
"go.dtapp.net/gotime"
"go.dtapp.net/gotrace_id"
"log/slog"
"os"
)
type SLogFun func() *SLog
type sLogConfig struct {
logPath string // [File]日志文件路径
logName string // [File]日志文件名
maxSize int // [File]单位为MB,默认为512MB
maxBackups int // [File]保留旧文件的最大个数
maxAge int // [File]文件最多保存多少天 0=不删除
showLine bool // [File、Console]显示代码行
}
type SLog struct {
config *sLogConfig
jsonHandler *slog.JSONHandler
textHandler *slog.TextHandler
logger *slog.Logger
}
type SLogFileConfig struct {
LogPath string // 日志文件路径
LogName string // 日志文件名
MaxSize int // 单位为MB,默认为512MB
MaxBackups int // 保留旧文件的最大个数
MaxAge int // 文件最多保存多少天 0=不删除
ShowLine bool // 显示代码行
}
func NewSlogFile(ctx context.Context, config *SLogFileConfig) *SLog {
sl := &SLog{
config: &sLogConfig{
logPath: config.LogPath,
logName: config.LogName,
maxSize: config.MaxSize,
maxBackups: config.MaxBackups,
maxAge: config.MaxAge,
showLine: config.ShowLine,
},
}
opts := slog.HandlerOptions{
AddSource: sl.config.showLine,
Level: slog.LevelDebug,
ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
if a.Key == slog.TimeKey {
a.Value = slog.StringValue(a.Value.Time().Format(gotime.DateTimeFormat))
//return slog.Attr{}
}
return a
},
}
lumberjackLogger := lumberjack.Logger{
Filename: sl.config.logPath + sl.config.logName, // ⽇志⽂件路径
MaxSize: sl.config.maxSize, // 单位为MB,默认为512MB
MaxAge: sl.config.maxAge, // 文件最多保存多少天
MaxBackups: sl.config.maxBackups, // 保留旧文件的最大个数
}
// json格式输出
sl.jsonHandler = slog.NewJSONHandler(&lumberjackLogger, &opts)
sl.logger = slog.New(sl.jsonHandler)
return sl
}
type SLogConsoleConfig struct {
ShowLine bool // 显示代码行
}
func NewSlogConsole(ctx context.Context, config *SLogConsoleConfig) *SLog {
sl := &SLog{
config: &sLogConfig{
showLine: config.ShowLine,
},
}
opts := slog.HandlerOptions{
AddSource: sl.config.showLine,
Level: slog.LevelDebug,
ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
if a.Key == slog.TimeKey {
a.Value = slog.StringValue(a.Value.Time().Format(gotime.DateTimeFormat))
//return slog.Attr{}
}
return a
},
}
// json格式输出
sl.jsonHandler = slog.NewJSONHandler(os.Stdout, &opts)
sl.logger = slog.New(sl.jsonHandler)
return sl
}
// WithLogger 跟踪编号
func (sl *SLog) WithLogger() *slog.Logger {
logger := slog.New(sl.jsonHandler)
return logger
}
// WithTraceId 跟踪编号
func (sl *SLog) WithTraceId(ctx context.Context) *slog.Logger {
jsonHandler := sl.jsonHandler.WithAttrs([]slog.Attr{
slog.String("trace_id", gotrace_id.GetTraceIdContext(ctx)),
})
logger := slog.New(jsonHandler)
return logger
}
// WithTraceIdStr 跟踪编号
func (sl *SLog) WithTraceIdStr(traceId string) *slog.Logger {
jsonHandler := sl.jsonHandler.WithAttrs([]slog.Attr{
slog.String("trace_id", traceId),
})
logger := slog.New(jsonHandler)
return logger
}

@ -1,33 +0,0 @@
package golog
import (
"os"
"runtime"
)
type System struct {
Variable []string // 环境变量
Hostname string // 主机名
Twd string // 当前目录
Uid int // 用户ID
EUid int // 有效用户ID
Gid int // 组ID
EGid int // 有效组ID
Pid int // 进程ID
PPid int // 父进程ID
Version string // 版本
}
func (s *System) Init() *System {
s.Variable = os.Environ()
s.Hostname, _ = os.Hostname()
s.Twd, _ = os.Getwd()
s.Uid = os.Getuid()
s.EUid = os.Geteuid()
s.Gid = os.Getgid()
s.EGid = os.Getegid()
s.Pid = os.Getpid()
s.PPid = os.Getppid()
s.Version = runtime.Version()
return s
}

@ -1,15 +0,0 @@
package golog
import (
"runtime"
"strings"
"testing"
)
func TestSystem(t *testing.T) {
var s System
s.Init()
t.Logf("%+v", s)
t.Logf("%+v", runtime.Version())
t.Logf("%+v", strings.TrimPrefix(runtime.Version(), "go"))
}

@ -1,71 +0,0 @@
package golog
import (
"database/sql/driver"
"errors"
"fmt"
"go.dtapp.net/gotime"
"gorm.io/gorm"
"gorm.io/gorm/schema"
"time"
)
type TimeString struct {
Time time.Time
}
// Value 插入数据,把时间转字符串
func (t TimeString) Value() (driver.Value, error) {
return gotime.SetCurrent(t.Time).Format(), nil
}
// Scan 查询数据,把字符串转时间
func (t *TimeString) Scan(value interface{}) error {
// 如果是空值,直接返回
data, ok := value.(string)
if !ok {
return errors.New(fmt.Sprint("无法解析:", value))
}
// 解析时间
result := gotime.SetCurrentParse(data)
*t = TimeString{
Time: result.Time,
}
return nil
}
// MarshalJSON JSON序列化
func (t TimeString) MarshalJSON() ([]byte, error) {
return []byte(fmt.Sprintf(`"%s"`, gotime.SetCurrent(t.Time).Format())), nil
}
// UnmarshalJSON JSON反序列化
func (t *TimeString) UnmarshalJSON(data []byte) (err error) {
// 删除双引号
if data[0] == '"' && data[len(data)-1] == '"' {
data = data[1 : len(data)-1]
}
result := gotime.SetCurrentParse(string(data))
*t = TimeString{
Time: result.Time,
}
return
}
func (t TimeString) GormDBDataType(db *gorm.DB, field *schema.Field) string {
// 使用 field.Tag、field.TagSettings 获取字段的 tag
// 查看 https://github.com/go-gorm/gorm/blob/master/schema/field.go 获取全部的选项
// 根据不同的数据库驱动返回不同的数据类型
switch db.Dialector.Name() {
case "mysql", "sqlite":
return "varchar"
case "postgres":
return "text"
}
return ""
}

@ -1,3 +1,5 @@
package golog
const Version = "1.0.14"
const (
Version = "1.0.109"
)

@ -1,7 +0,0 @@
package golog
import "testing"
func TestVersion(t *testing.T) {
t.Log(Version)
}
Loading…
Cancel
Save