diff --git a/const.go b/const.go index 3f42458..9168895 100644 --- a/const.go +++ b/const.go @@ -1,3 +1,7 @@ package golog -const Version = "1.0.37" +const ( + Version = "1.0.38" + logTypeGorm = "gorm" + logTypeMongo = "mongo" +) diff --git a/gin.go b/gin.go index 1357fc2..d72252c 100644 --- a/gin.go +++ b/gin.go @@ -22,16 +22,25 @@ type GinClientConfig struct { // GinClient 框架 type GinClient struct { - gormClient *dorm.GormClient // 数据库驱动 - ipService *goip.Client // ip服务 - logClient *ZapLog // 日志驱动 - config struct { + gormClient *dorm.GormClient // 数据库驱动 + mongoClient *dorm.MongoClient // 数据库驱动 + ipService *goip.Client // ip服务 + logClient *ZapLog // 日志驱动 + config struct { tableName string // 表名 insideIp string // 内网ip hostname string // 主机名 goVersion string // go版本 logDebug bool // 日志开关 } + mongoConfig struct { + databaseName string // 库名 + collectionName string // 表名 + insideIp string // 内网ip + hostname string // 主机名 + goVersion string // go版本 + debug bool // 日志开关 + } } // NewGinClient 创建框架实例化 @@ -69,6 +78,44 @@ func NewGinClient(config *GinClientConfig) (*GinClient, error) { return c, nil } +// NewGinMongoClient 创建框架实例化 +// mongoClient 数据库服务 +// ipService ip服务 +// databaseName 库名 +// collectionName 表名 +func NewGinMongoClient(mongoClient *dorm.MongoClient, ipService *goip.Client, databaseName string, collectionName string, debug bool) (*GinClient, error) { + + c := &GinClient{} + + if mongoClient.Db == nil { + return nil, errors.New("没有设置驱动") + } + + c.mongoClient = mongoClient + + if databaseName == "" { + return nil, errors.New("没有设置库名") + } + c.mongoConfig.databaseName = databaseName + + if collectionName == "" { + return nil, errors.New("没有设置表名") + } + c.mongoConfig.collectionName = collectionName + + c.ipService = ipService + + c.mongoConfig.debug = debug + + hostname, _ := os.Hostname() + + c.mongoConfig.hostname = hostname + c.mongoConfig.insideIp = goip.GetInsideIp(context.Background()) + c.mongoConfig.goVersion = runtime.Version() + + return c, nil +} + type bodyLogWriter struct { gin.ResponseWriter body *bytes.Buffer diff --git a/gin_mongo.go b/gin_mongo.go new file mode 100644 index 0000000..54f684f --- /dev/null +++ b/gin_mongo.go @@ -0,0 +1,247 @@ +package golog + +import ( + "bytes" + "context" + "encoding/json" + "github.com/gin-gonic/gin" + "go.dtapp.net/dorm" + "go.dtapp.net/gorequest" + "go.dtapp.net/gotime" + "go.dtapp.net/gotrace_id" + "go.dtapp.net/gourl" + "go.dtapp.net/goxml" + "go.mongodb.org/mongo-driver/bson/primitive" + "io/ioutil" + "log" + "net" +) + +// 模型结构体 +type ginMongoLog struct { + LogId primitive.ObjectID `json:"log_id,omitempty" bson:"_id,omitempty"` //【记录】编号 + TraceId string `json:"trace_id,omitempty" bson:"trace_id,omitempty"` //【系统】跟踪编号 + RequestTime int64 `json:"request_time,omitempty" bson:"request_time,omitempty"` //【请求】时间 + RequestUri string `json:"request_uri,omitempty" bson:"request_uri,omitempty"` //【请求】请求链接 域名+路径+参数 + RequestUrl string `json:"request_url,omitempty" bson:"request_url,omitempty"` //【请求】请求链接 域名+路径 + RequestApi string `json:"request_api,omitempty" bson:"request_api,omitempty"` //【请求】请求接口 路径 + RequestMethod string `json:"request_method,omitempty" bson:"request_method,omitempty"` //【请求】请求方式 + RequestProto string `json:"request_proto,omitempty" bson:"request_proto,omitempty"` //【请求】请求协议 + RequestUa string `json:"request_ua,omitempty" bson:"request_ua,omitempty"` //【请求】请求UA + RequestReferer string `json:"request_referer,omitempty" bson:"request_referer,omitempty"` //【请求】请求referer + RequestBody interface{} `json:"request_body,omitempty" bson:"request_body,omitempty"` //【请求】请求主体 + RequestUrlQuery interface{} `json:"request_url_query,omitempty" bson:"request_url_query,omitempty"` //【请求】请求URL参数 + RequestIp string `json:"request_ip,omitempty" bson:"request_ip,omitempty"` //【请求】请求客户端Ip + RequestIpCountry string `json:"request_ip_country,omitempty" bson:"request_ip_country,omitempty"` //【请求】请求客户端城市 + RequestIpRegion string `json:"request_ip_region,omitempty" bson:"request_ip_region,omitempty"` //【请求】请求客户端区域 + RequestIpProvince string `json:"request_ip_province,omitempty" bson:"request_ip_province,omitempty"` //【请求】请求客户端省份 + RequestIpCity string `json:"request_ip_city,omitempty" bson:"request_ip_city,omitempty"` //【请求】请求客户端城市 + RequestIpIsp string `json:"request_ip_isp,omitempty" bson:"request_ip_isp,omitempty"` //【请求】请求客户端运营商 + RequestHeader interface{} `json:"request_header,omitempty" bson:"request_header,omitempty"` //【请求】请求头 + ResponseTime int64 `json:"response_time,omitempty" bson:"response_time,omitempty"` //【返回】时间 + ResponseCode int `json:"response_code,omitempty" bson:"response_code,omitempty"` //【返回】状态码 + ResponseMsg string `json:"response_msg,omitempty" bson:"response_msg,omitempty"` //【返回】描述 + ResponseData interface{} `json:"response_data,omitempty" bson:"response_data,omitempty"` //【返回】数据 + CostTime int64 `json:"cost_time,omitempty" bson:"cost_time,omitempty"` //【系统】花费时间 + SystemHostName string `json:"system_host_name,omitempty" bson:"system_host_name,omitempty"` //【系统】主机名 + SystemInsideIp string `json:"system_inside_ip,omitempty" bson:"system_inside_ip,omitempty"` //【系统】内网ip + GoVersion string `json:"go_version,omitempty" bson:"go_version,omitempty"` //【程序】Go版本 + SdkVersion string `json:"sdk_version,omitempty" bson:"sdk_version,omitempty"` //【程序】Sdk版本 +} + +// 记录日志 +func (c *GinClient) mongoRecord(mongoLog ginMongoLog) error { + + mongoLog.SystemHostName = c.mongoConfig.hostname + if mongoLog.SystemInsideIp == "" { + mongoLog.SystemInsideIp = c.mongoConfig.insideIp + } + mongoLog.GoVersion = c.mongoConfig.goVersion + + mongoLog.SdkVersion = Version + + mongoLog.LogId = primitive.NewObjectID() + + _, err := c.mongoClient.Database(c.mongoConfig.databaseName).Collection(c.mongoConfig.collectionName).InsertOne(mongoLog) + + return err +} + +// MongoQuery 查询 +func (c *GinClient) MongoQuery() *dorm.MongoClient { + return c.mongoClient.Database(c.mongoConfig.databaseName).Collection(c.mongoConfig.collectionName) +} + +// MongoMiddleware 中间件 +func (c *GinClient) MongoMiddleware() gin.HandlerFunc { + return func(ginCtx *gin.Context) { + + // 开始时间 + startTime := gotime.Current().TimestampWithMillisecond() + requestTime := gotime.Current().Time + + // 获取 + data, _ := ioutil.ReadAll(ginCtx.Request.Body) + + if c.mongoConfig.debug { + log.Printf("[golog.MongoMiddleware] %s\n", data) + } + + // 复用 + ginCtx.Request.Body = ioutil.NopCloser(bytes.NewBuffer(data)) + + blw := &bodyLogWriter{body: bytes.NewBufferString(""), ResponseWriter: ginCtx.Writer} + ginCtx.Writer = blw + + // 处理请求 + ginCtx.Next() + + // 响应 + responseCode := ginCtx.Writer.Status() + responseBody := blw.body.String() + + //结束时间 + endTime := gotime.Current().TimestampWithMillisecond() + + go func() { + + var ctx = gotrace_id.SetGinTraceIdContext(context.Background(), ginCtx) + + var dataJson = true + + // 解析请求内容 + var xmlBody map[string]string + var jsonBody map[string]interface{} + + // 判断是否有内容 + if len(data) > 0 { + err := json.Unmarshal(data, &jsonBody) + if len(jsonBody) <= 0 { + dataJson = false + xmlBody = goxml.XmlDecode(string(data)) + } + + if c.mongoConfig.debug { + log.Printf("[golog.MongoMiddleware.len(jsonBody)] %v\n", len(jsonBody)) + } + + if err != nil { + if c.mongoConfig.debug { + log.Printf("[golog.MongoMiddleware.json.Unmarshal] %s %s\n", jsonBody, err) + } + dataJson = false + xmlBody = goxml.XmlDecode(string(data)) + } + } + + if c.mongoConfig.debug { + log.Printf("[golog.MongoMiddleware.xmlBody] %s\n", xmlBody) + log.Printf("[golog.MongoMiddleware.jsonBody] %s\n", jsonBody) + } + + clientIp := gorequest.ClientIp(ginCtx.Request) + + requestClientIpCountry, requestClientIpRegion, requestClientIpProvince, requestClientIpCity, requestClientIpIsp := "", "", "", "", "" + if c.ipService != nil { + if net.ParseIP(ginCtx.ClientIP()).To4() != nil { + // 判断是不是IPV4 + _, info := c.ipService.Ipv4(ginCtx.ClientIP()) + requestClientIpCountry = info.Country + requestClientIpRegion = info.Region + requestClientIpProvince = info.Province + requestClientIpCity = info.City + requestClientIpIsp = info.ISP + } + if net.ParseIP(ginCtx.ClientIP()).To16() != nil { + // 判断是不是IPV6 + info := c.ipService.Ipv6(ginCtx.ClientIP()) + requestClientIpCountry = info.Country + requestClientIpProvince = info.Province + requestClientIpCity = info.City + } + } + + // 记录 + if c.mongoClient != nil && c.mongoClient.Db != nil { + + var traceId = gotrace_id.GetGinTraceId(ginCtx) + + host := "" + if ginCtx.Request.TLS == nil { + host = "http://" + ginCtx.Request.Host + } else { + host = "https://" + ginCtx.Request.Host + } + if dataJson { + if c.mongoConfig.debug { + log.Printf("[golog.MongoMiddleware.gormRecord.json.request_body] %s\n", jsonBody) + } + err := c.mongoRecord(ginMongoLog{ + TraceId: traceId, //【系统】跟踪编号 + RequestTime: gotime.SetCurrent(requestTime).Timestamp(), //【请求】时间 + RequestUri: host + ginCtx.Request.RequestURI, //【请求】请求链接 + RequestUrl: ginCtx.Request.RequestURI, //【请求】请求链接 + RequestApi: gourl.UriFilterExcludeQueryString(ginCtx.Request.RequestURI), //【请求】请求接口 + RequestMethod: ginCtx.Request.Method, //【请求】请求方式 + RequestProto: ginCtx.Request.Proto, //【请求】请求协议 + RequestUa: ginCtx.Request.UserAgent(), //【请求】请求UA + RequestReferer: ginCtx.Request.Referer(), //【请求】请求referer + RequestBody: jsonBody, //【请求】请求主体 + RequestUrlQuery: ginCtx.Request.URL.Query(), //【请求】请求URL参数 + RequestIp: clientIp, //【请求】请求客户端Ip + RequestIpCountry: requestClientIpCountry, //【请求】请求客户端城市 + RequestIpRegion: requestClientIpRegion, //【请求】请求客户端区域 + RequestIpProvince: requestClientIpProvince, //【请求】请求客户端省份 + RequestIpCity: requestClientIpCity, //【请求】请求客户端城市 + RequestIpIsp: requestClientIpIsp, //【请求】请求客户端运营商 + RequestHeader: ginCtx.Request.Header, //【请求】请求头 + ResponseTime: gotime.SetCurrent(gotime.Current().Time).Timestamp(), //【返回】时间 + ResponseCode: responseCode, //【返回】状态码 + ResponseData: c.jsonUnmarshal(responseBody), //【返回】数据 + CostTime: endTime - startTime, //【系统】花费时间 + }) + if err != nil { + if c.mongoConfig.debug { + c.logClient.Errorf(ctx, "[log.gormRecord] %s", err.Error()) + log.Printf("[golog.MongoMiddleware.gormRecord.json] %s\n", err) + } + } + } else { + if c.mongoConfig.debug { + log.Printf("[golog.MongoMiddleware.gormRecord.xml.request_body] %s\n", xmlBody) + } + err := c.mongoRecord(ginMongoLog{ + TraceId: traceId, //【系统】跟踪编号 + RequestTime: gotime.SetCurrent(requestTime).Timestamp(), //【请求】时间 + RequestUri: host + ginCtx.Request.RequestURI, //【请求】请求链接 + RequestUrl: ginCtx.Request.RequestURI, //【请求】请求链接 + RequestApi: gourl.UriFilterExcludeQueryString(ginCtx.Request.RequestURI), //【请求】请求接口 + RequestMethod: ginCtx.Request.Method, //【请求】请求方式 + RequestProto: ginCtx.Request.Proto, //【请求】请求协议 + RequestUa: ginCtx.Request.UserAgent(), //【请求】请求UA + RequestReferer: ginCtx.Request.Referer(), //【请求】请求referer + RequestBody: xmlBody, //【请求】请求主体 + RequestUrlQuery: ginCtx.Request.URL.Query(), //【请求】请求URL参数 + RequestIp: clientIp, //【请求】请求客户端Ip + RequestIpCountry: requestClientIpCountry, //【请求】请求客户端城市 + RequestIpRegion: requestClientIpRegion, //【请求】请求客户端区域 + RequestIpProvince: requestClientIpProvince, //【请求】请求客户端省份 + RequestIpCity: requestClientIpCity, //【请求】请求客户端城市 + RequestIpIsp: requestClientIpIsp, //【请求】请求客户端运营商 + RequestHeader: ginCtx.Request.Header, //【请求】请求头 + ResponseTime: gotime.SetCurrent(gotime.Current().Time).Timestamp(), //【返回】时间 + ResponseCode: responseCode, //【返回】状态码 + ResponseData: c.jsonUnmarshal(responseBody), //【返回】数据 + CostTime: endTime - startTime, //【系统】花费时间 + }) + if err != nil { + if c.mongoConfig.debug { + c.logClient.Errorf(ctx, "[log.gormRecord] %s", err.Error()) + log.Printf("[golog.MongoMiddleware.gormRecord.xml] %s\n", err) + } + } + } + } + }() + } +} diff --git a/go.mod b/go.mod index 304c175..896cdae 100644 --- a/go.mod +++ b/go.mod @@ -15,6 +15,7 @@ require ( go.dtapp.net/gotrace_id v1.0.6 go.dtapp.net/gourl v1.0.0 go.dtapp.net/goxml v1.0.1 + go.mongodb.org/mongo-driver v1.10.1 go.uber.org/zap v1.22.0 gorm.io/datatypes v1.0.7 gorm.io/gorm v1.23.8 @@ -73,7 +74,6 @@ require ( github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect go.dtapp.net/gorandom v1.0.1 // indirect go.dtapp.net/gostring v1.0.9 // indirect - go.mongodb.org/mongo-driver v1.10.1 // indirect go.uber.org/atomic v1.10.0 // indirect go.uber.org/multierr v1.8.0 // indirect golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8 // indirect