233 lines
5.1 KiB
Go
233 lines
5.1 KiB
Go
package openlog
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
const (
|
|
CtxReqID = "open_req_id"
|
|
CtxDebug = "open_debug"
|
|
CtxBody = "open_body"
|
|
CtxStart = "open_start"
|
|
CtxAPIKey = "open_api_key"
|
|
CtxSide = "open_side"
|
|
CtxAPIType = "open_api_type"
|
|
CtxAction = "open_action"
|
|
)
|
|
|
|
const (
|
|
SideClient = "client"
|
|
SideSource = "source"
|
|
|
|
APITypeClientV1 = "client_v1"
|
|
APITypeSourceOpenV1 = "source_open_v1"
|
|
APITypeUnknown = "unknown"
|
|
|
|
ActionAuth = "auth"
|
|
ActionList = "list"
|
|
ActionCreate = "create"
|
|
ActionQuery = "query"
|
|
ActionCancel = "cancel"
|
|
ActionWallet = "wallet"
|
|
ActionPush = "push"
|
|
ActionUnknown = "unknown"
|
|
)
|
|
|
|
// Enabled 是否开启开放接口详细日志
|
|
var Enabled bool
|
|
|
|
func Init(enabled bool) {
|
|
Enabled = enabled
|
|
if enabled {
|
|
log.Printf("[open] 开放接口调试日志已开启 (OPEN_API_DEBUG)")
|
|
}
|
|
}
|
|
|
|
func NewReqID() string {
|
|
return strings.ReplaceAll(uuid.NewString(), "-", "")[:12]
|
|
}
|
|
|
|
func GetReqID(c *gin.Context) string {
|
|
if v, ok := c.Get(CtxReqID); ok {
|
|
if s, ok := v.(string); ok {
|
|
return s
|
|
}
|
|
}
|
|
return "-"
|
|
}
|
|
|
|
func EnsureReqID(c *gin.Context) string {
|
|
if id := GetReqID(c); id != "-" {
|
|
return id
|
|
}
|
|
id := NewReqID()
|
|
c.Set(CtxReqID, id)
|
|
return id
|
|
}
|
|
|
|
func SetScope(c *gin.Context, side, action string) {
|
|
c.Set(CtxSide, side)
|
|
c.Set(CtxAPIType, APITypeFromSide(side))
|
|
c.Set(CtxAction, action)
|
|
}
|
|
|
|
func SetAction(c *gin.Context, action string) {
|
|
c.Set(CtxAction, action)
|
|
}
|
|
|
|
func ScopeFromPath(side, method, path string) (string, string) {
|
|
action := ActionUnknown
|
|
switch side {
|
|
case SideClient:
|
|
action = clientAction(method, path)
|
|
case SideSource:
|
|
action = sourceAction(method, path)
|
|
}
|
|
return side, action
|
|
}
|
|
|
|
func APITypeFromSide(side string) string {
|
|
switch side {
|
|
case SideClient:
|
|
return APITypeClientV1
|
|
case SideSource:
|
|
return APITypeSourceOpenV1
|
|
default:
|
|
return APITypeUnknown
|
|
}
|
|
}
|
|
|
|
func IsDebug(c *gin.Context) bool {
|
|
if !Enabled {
|
|
return false
|
|
}
|
|
if v, ok := c.Get(CtxDebug); ok {
|
|
if b, ok := v.(bool); ok {
|
|
return b
|
|
}
|
|
}
|
|
return Enabled
|
|
}
|
|
|
|
// MaskKey 脱敏 api_key:保留前缀与后 4 位
|
|
func MaskKey(key string) string {
|
|
if key == "" {
|
|
return "(empty)"
|
|
}
|
|
if len(key) <= 8 {
|
|
return key[:2] + "***"
|
|
}
|
|
return key[:8] + "***" + key[len(key)-4:]
|
|
}
|
|
|
|
// MaskSign 脱敏签名:只保留前 8 位
|
|
func MaskSign(sign string) string {
|
|
if sign == "" {
|
|
return "(empty)"
|
|
}
|
|
if len(sign) <= 8 {
|
|
return sign + "..."
|
|
}
|
|
return sign[:8] + "..."
|
|
}
|
|
|
|
// Truncate 截断过长字符串
|
|
func Truncate(s string, max int) string {
|
|
if max <= 0 || len(s) <= max {
|
|
return s
|
|
}
|
|
return s[:max] + fmt.Sprintf("...(%d bytes)", len(s))
|
|
}
|
|
|
|
func Prefix(c *gin.Context) string {
|
|
side, _ := c.Get(CtxSide)
|
|
apiType, _ := c.Get(CtxAPIType)
|
|
action, _ := c.Get(CtxAction)
|
|
sideText, _ := side.(string)
|
|
apiTypeText, _ := apiType.(string)
|
|
actionText, _ := action.(string)
|
|
tag := "open"
|
|
if apiTypeText != "" && apiTypeText != APITypeUnknown {
|
|
tag += "." + apiTypeText
|
|
} else if sideText != "" {
|
|
tag += "." + sideText
|
|
}
|
|
if actionText != "" {
|
|
tag += "." + actionText
|
|
}
|
|
fields := ""
|
|
if apiTypeText != "" {
|
|
fields += fmt.Sprintf(" api_type=%s", apiTypeText)
|
|
}
|
|
if actionText != "" {
|
|
fields += fmt.Sprintf(" action=%s", actionText)
|
|
}
|
|
return fmt.Sprintf("[%s] req_id=%s%s ", tag, GetReqID(c), fields)
|
|
}
|
|
|
|
func Info(c *gin.Context, format string, args ...interface{}) {
|
|
if !IsDebug(c) {
|
|
return
|
|
}
|
|
log.Printf(Prefix(c)+format, args...)
|
|
}
|
|
|
|
func Warn(c *gin.Context, format string, args ...interface{}) {
|
|
// 鉴权失败等也值得在 debug 时打出
|
|
if !Enabled {
|
|
return
|
|
}
|
|
log.Printf(Prefix(c)+format, args...)
|
|
}
|
|
|
|
func Elapsed(c *gin.Context) time.Duration {
|
|
if v, ok := c.Get(CtxStart); ok {
|
|
if t, ok := v.(time.Time); ok {
|
|
return time.Since(t)
|
|
}
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func clientAction(method, path string) string {
|
|
method = strings.ToUpper(method)
|
|
switch {
|
|
case method == "GET" && path == "/api/client/v1/products":
|
|
return ActionList
|
|
case method == "POST" && path == "/api/client/v1/orders":
|
|
return ActionCreate
|
|
case method == "GET" && strings.HasPrefix(path, "/api/client/v1/orders/") && strings.Contains(path, "/delivery"):
|
|
return ActionQuery
|
|
case method == "POST" && strings.HasPrefix(path, "/api/client/v1/orders/") && strings.Contains(path, "/delivery/bind"):
|
|
return ActionCreate
|
|
case method == "POST" && strings.HasPrefix(path, "/api/client/v1/orders/") && strings.Contains(path, "/delivery/submit"):
|
|
return ActionCreate
|
|
case method == "GET" && strings.HasPrefix(path, "/api/client/v1/orders/"):
|
|
return ActionQuery
|
|
case method == "POST" && strings.HasPrefix(path, "/api/client/v1/orders/") && strings.HasSuffix(path, "/cancel"):
|
|
return ActionCancel
|
|
case method == "GET" && path == "/api/client/v1/wallet":
|
|
return ActionWallet
|
|
default:
|
|
return ActionUnknown
|
|
}
|
|
}
|
|
|
|
func sourceAction(method, path string) string {
|
|
method = strings.ToUpper(method)
|
|
switch {
|
|
case method == "GET" && strings.HasPrefix(path, "/api/open/v1/orders/"):
|
|
return ActionQuery
|
|
case method == "POST" && path == "/api/open/v1/orders/ship-notify":
|
|
return ActionPush
|
|
default:
|
|
return ActionUnknown
|
|
}
|
|
}
|