// Package logger ... // // Description : logger ... // // Author : go_developer@163.com<白茶清欢> // // Date : 2022-06-12 18:39 package logger import ( "context" "fmt" "os" "strings" "git.zhangdeman.cn/zhangdeman/consts" "git.zhangdeman.cn/zhangdeman/network/util" "git.zhangdeman.cn/zhangdeman/serialize" "git.zhangdeman.cn/zhangdeman/wrapper/op_any" "git.zhangdeman.cn/zhangdeman/websocket/storage" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) var ( wsLoggerConnect storage.IConnection // ws 日志连接管理实例 ) // SetWsLoggerConnect 设置ws connect // // Author : go_developer@163.com<白茶清欢> // // Date : 15:20 2024/7/23 func SetWsLoggerConnect(connect storage.IConnection) { wsLoggerConnect = connect } // GetWsLoggConnect 获取ws连接 // // Author : go_developer@163.com<白茶清欢> // // Date : 15:34 2024/7/23 func GetWsLoggConnect() storage.IConnection { return wsLoggerConnect } // LogData 记录日志数据 type LogData struct { Env string `json:"env"` // 运行环境 Uri string `json:"uri"` // 请求的接口 TraceID string `json:"trace_id"` // 请求的trace_id UserID string `json:"user_id"` // 用户ID OperateMode string `json:"operate_mode"` // 操作模式(PC/APP/PAD等) LogType string `json:"log_type"` // 日志类型 CodeVersion string `json:"code_version"` // 代码版本(可以设置为git commit id) ServiceVersion string `json:"service_version"` // 服务本身的版本 ClientIp string `json:"client_ip"` // 客户端IP ServerIp string `json:"server_ip"` // 服务器IP Hostname string `json:"hostname"` // 服务器主机名 Code string `json:"code"` // 日志分类标记码 Data map[string]any `json:"data"` // 扩展记录的数据, 会展开一层进行记录 } // ToFieldList 转换为 zap.Field 列表 func (ld *LogData) ToFieldList() []zap.Field { var ( fieldList []zap.Field mapLogData map[string]any ) serialize.JSON.TransitionIgnoreError(ld, &mapLogData) for k, v := range mapLogData { fieldList = append(fieldList, zap.Any(k, v)) } return fieldList } // InputLogConfig 输入的日志配置 type InputLogConfig struct { Name string `json:"name" yaml:"name"` // 日志文件名 Path string `json:"path" yaml:"path"` // 日志文件路径 TimeIntervalType LogSplit `json:"time_interval_type" yaml:"time_interval_type"` // 日志切割规则 DivisionChar string `json:"division_char" yaml:"division_char"` // 文件名分隔符 LogLevel LogLevel `json:"log_level" yaml:"log_level"` // 日志等级 Console bool `json:"console" yaml:"console"` // 是否进行控制台日志输出 UseJson bool `json:"use_json" yaml:"use_json"` // 日志是否使用JSON格式 FileLine bool `json:"file_line" yaml:"file_line"` // 日志是否打印行号 MessageKey string `json:"message_key" yaml:"message_key"` // message 字段 LevelKey string `json:"level_key" yaml:"level_key"` // level 字段 TimeKey string `json:"time_key" yaml:"time_key"` // 时间字段 CallerKey string `json:"caller_key" yaml:"caller_key"` // 记录日志的文件的代码行数 UseShortFile bool `json:"use_short_file" yaml:"use_short_file"` // 是否使用短文件格式 CallerSkip int `json:"caller_skip" yaml:"caller_skip"` // 日志记录的文件跳过多少层 MaxAge int64 `json:"max_age" yaml:"max_age"` // 日志最长保存时间, 单位 : 秒 ZincSyncConfig *ZincConfig `json:"zinc_sync_config" yaml:"zinc_sync_config"` // 日志同步至zinc的配置 } // NewLogData ... func NewLogData(ctx context.Context, logType string, code string, logData map[string]any) *LogData { hostname, _ := os.Hostname() if nil == ctx { ctx = context.Background() } commonLogData := &LogData{ Env: getStrVal(ctx, consts.GinEnvField), Uri: getStrVal(ctx, consts.GinRequestURIField), TraceID: getStrVal(ctx, consts.GinTraceIDField), UserID: getStrVal(ctx, consts.GinUserIDField), OperateMode: getStrVal(ctx, consts.GinOperateModeField), LogType: logType, CodeVersion: getStrVal(ctx, consts.GinCodeVersionField), ServiceVersion: getStrVal(ctx, consts.GinServiceVersionField), ClientIp: getStrVal(ctx, consts.GinClientIpField), ServerIp: util.IP.GetHostIP(), Hostname: hostname, Code: code, Data: logData, } return commonLogData } func getStrVal(ctx context.Context, key string) string { val := ctx.Value(key) if nil != val { return op_any.AnyDataType(val).ToString() } if v := ctx.Value(consts.GinContextDataField); nil != v { if data, ok := v.(map[string]any); ok { if searchVal, exist := data[key]; exist && nil != searchVal { return fmt.Sprintf("%v", searchVal) } } } return "" } // GetLogInstanceFromInputConfig 从输入配置获取日志实例 func GetLogInstanceFromInputConfig(logConf *InputLogConfig) (*zap.Logger, error) { if nil == logConf { return nil, nil } logConfList := []SetLoggerOptionFunc{ WithCallerSkip(logConf.CallerSkip), WithCaller(), WithUseJsonFormat(logConf.UseJson), WithShortCaller(logConf.UseShortFile), } if logConf.Console { logConfList = append(logConfList, WithConsoleOutput()) } // 配置zinc日志同步 logConfList = append(logConfList, WithZincLogCollect(logConf.ZincSyncConfig)) var ( err error loggerInstance *zap.Logger splitConfig *RotateLogConfig ) if splitConfig, err = NewRotateLogConfig( logConf.Path, logConf.Name, WithDivisionChar(logConf.DivisionChar), WithTimeIntervalType(logConf.TimeIntervalType), WithMaxAge(logConf.MaxAge)); nil != err { return nil, err } if loggerInstance, err = NewLogger(logConf.LogLevel, splitConfig, logConfList...); nil != err { return nil, err } return loggerInstance, nil } // inputLevel2ZapLevel 输入日志等级转化为zap日志等级 func inputLevel2ZapLevel(inputLoggerLevel LogLevel) zapcore.Level { inputLoggerLevel = LogLevel(strings.ToUpper(inputLoggerLevel.String())) if !inputLoggerLevel.IsValid() { // 非法的日志等级, 自动重定向为 info 级别 inputLoggerLevel = LogLevelInfo } loggerLevel := zapcore.DebugLevel switch inputLoggerLevel { case LogLevelDebug: loggerLevel = zapcore.DebugLevel case LogLevelInfo: loggerLevel = zapcore.InfoLevel case LogLevelWarn: loggerLevel = zapcore.WarnLevel case LogLevelError: loggerLevel = zapcore.ErrorLevel case LogLevelPanic: loggerLevel = zapcore.PanicLevel } return loggerLevel } // ZincConfig zinc服务配置 type ZincConfig struct { Authorization string `json:"authorization" dc:"授权secret,生成方式base64(user:password)"` Domain string `json:"domain" dc:"zinc服务域名"` Timeout int `json:"timeout" dc:"超时时间,单位毫秒,默认5000"` Async bool `json:"async" dc:"数据异步写入"` Index string `json:"index" dc:"日志使用的索引"` CreateType string `json:"create_type" dc:"日志同步的类型: single - 单个同步 batch - 批量创建"` BufferSize int `json:"buffer_size" dc:"批量创建时, 数据缓存buffer大小, 默认1000"` ForceSyncTime int `json:"force_sync_time" dc:"批量同步日志时,强制同步的时间间隔,buffer没满也会强制同步, 单位: 秒"` } const ( DefaultTimeout = 5000 // 默认超时时间 DefaultBufferSize = 1000 // 默认buffer大小 DefaultForceFlushLogTime = 1000 // 强制刷新日志的时间间隔, 单位毫秒 ) const ( CreateTypeSingle = "single" // 逐条日志同步 CreateTypeBatch = "batch" // 批量日志同步 )