版本封存

This commit is contained in:
zeaslity
2025-12-06 11:26:05 +08:00
parent 13949e1ba8
commit c0ae5e30c4
57 changed files with 2443 additions and 1428 deletions

View File

@@ -1,3 +1,7 @@
module cmii-uav-watchdog-common
go 1.24
require cmii-uav-watchdog-otp v0.0.0
replace cmii-uav-watchdog-otp => ../cmii-uav-watchdog-otp

View File

@@ -1,21 +0,0 @@
package main
import (
"fmt"
)
//TIP <p>To run your code, right-click the code and select <b>Run</b>.</p> <p>Alternatively, click
// the <icon src="AllIcons.Actions.Execute"/> icon in the gutter and select the <b>Run</b> menu item from here.</p>
func main() {
//TIP <p>Press <shortcut actionId="ShowIntentionActions"/> when your caret is at the underlined text
// to see how GoLand suggests fixing the warning.</p><p>Alternatively, if available, click the lightbulb to view possible fixes.</p>
s := "gopher"
fmt.Println("Hello and welcome, %s!", s)
for i := 1; i <= 5; i++ {
//TIP <p>To start your debugging session, right-click your code in the editor and select the Debug option.</p> <p>We have set one <icon src="AllIcons.Debugger.Db_set_breakpoint"/> breakpoint
// for you, but you can always add more by pressing <shortcut actionId="ToggleLineBreakpoint"/>.</p>
fmt.Println("i =", 100/i)
}
}

View File

@@ -1,28 +1,32 @@
package models
import "time"
// AuthorizationFile 授权文件模型
type AuthorizationFile struct {
EncryptedHosts []string `json:"encrypted_hosts"` // 加密后的主机信息列表
TOTPCode string `json:"totp_code"` // TOTP验证码
CurrentTime time.Time `json:"current_time"` // 当前系统时间
FirstAuthTime time.Time `json:"first_auth_time"` // 初次授权时间
TimeOffset int64 `json:"time_offset"` // 授权时间偏移
EncryptedHostMap map[string]HostInfo `json:"encrypted_host_map"` // 加密后的主机信息列表
TOTPCode string `json:"totp_code"` // TOTP验证码
CurrentTime string `json:"current_time"` // 当前系统时间
FirstAuthTime string `json:"first_auth_time"` // 初次授权时间
TimeOffset int64 `json:"time_offset"` // 授权时间偏移
ProjectNamespace string `json:"project_namespace"` // 项目命名空间
EncryptedNamespace string `json:"encrypted_namespace"` // 加密后的项目命名空间 防止信息篡改
}
// AuthorizationCode 授权码模型
type AuthorizationCode struct {
TOTPCode string `json:"totp_code"` // TOTP验证码
CurrentTime time.Time `json:"current_time"` // 当前系统时间
EncryptedHosts []string `json:"encrypted_hosts"` // 授权主机的加密字符串列表
TOTPCode string `json:"totp_code"` // TOTP验证码
CurrentTime string `json:"current_time"` // 当前系统时间
EncryptedHostMap map[string]HostInfo `json:"encrypted_host_map"` // 加密后的主机信息列表 防止信息篡改
ProjectNamespace string `json:"project_namespace"` // 项目命名空间
EncryptedNamespace string `json:"encrypted_namespace"` // 加密后的项目命名空间 防止信息篡改
}
// AuthorizationStorage 授权存储信息
type AuthorizationStorage struct {
EncryptedCode string `json:"encrypted_code"` // 加密后的授权码
FirstAuthTime time.Time `json:"first_auth_time"` // 初次授权时间
TimeOffset int64 `json:"time_offset"` // 授权时间偏移
AuthorizedHosts []string `json:"authorized_hosts"` // 已授权主机列表
SecondTOTPSecret string `json:"second_totp_secret"` // 第二级的totp密钥
EncryptedAuthrizationCode string `json:"encrypted_authrization_code"` // 加密后的授权码,防止信息篡改
FirstAuthTime string `json:"first_auth_time"` // 初次授权时间
TimeOffset int64 `json:"time_offset"` // 授权时间偏移
AuthorizedHostMap map[string]HostInfo `json:"authorized_host_map"` // 已授权主机列表
SecondTOTPSecret string `json:"second_totp_secret"` // 第二级的totp密钥
ProjectNamespace string `json:"project_namespace"` // 项目命名空间
EncryptedNamespace string `json:"encrypted_namespace"` // 加密后的项目命名空间 防止信息篡改
}

View File

@@ -2,10 +2,13 @@ package models
// CPUInfo 结构体用于存储 CPU 信息
type CPUInfo struct {
ModelName string `json:"model_name"`
Cores int `json:"cores"`
Architecture string `json:"architecture"`
UUID string `json:"uuid"`
ModelName string `json:"model_name"`
Cores int `json:"cores"`
Architecture string `json:"architecture"`
Flags []string `json:"flags"`
Hypervisor string `json:"hypervisor"`
Virtualization string `json:"virtualization"`
Frequency string `json:"frequency"`
}
// MemoryInfo holds the memory information.
@@ -49,22 +52,21 @@ type MotherboardInfo struct {
}
type SystemInfo struct {
MachineID string `json:"machine_id"` // 唯一标识符
MachineID string `json:"machine_id"` // 主机唯一标识符
MachineSerial string `json:"machine_serial"` // 主机序列号
OS OSInfo `json:"os"` // 操作系统
KernelVersion string `json:"kernel_version"` // 内核版本
NodeName string `json:"node_name"` // 节点名称
NodeIP string `json:"node_ip"` // 节点IP
}
type OSInfo struct {
Name string `json:"name"`
Version string `json:"version"`
ID string `json:"id"`
IDLike string `json:"id_like"`
VersionID string `json:"version_id"`
PrettyName string `json:"pretty_name"`
HomeURL string `json:"home_url"`
SupportURL string `json:"support_url"`
BugReportURL string `json:"bug_report_url"`
PrivacyURL string `json:"privacy_url"`
Name string `json:"name"`
Version string `json:"version"`
ID string `json:"id"`
IDLike string `json:"id_like"`
VersionID string `json:"version_id"`
PrettyName string `json:"pretty_name"`
}
// HostInfo 主机信息模型
@@ -77,12 +79,32 @@ type HostInfo struct {
MotherboardInfo MotherboardInfo `json:"motherboard_info"`
}
// EnvInfo 环境信息
type EnvInfo struct {
K8S_NAMESPACE string `json:"k8s_namespace"` // 环境名称
APPLICATION_NAME string `json:"application_name"` // 应用名称
CUST_JAVA_OPTS string `json:"cust_java_opts"` // 自定义java参数
BIZ_CONFIG_GROUP string `json:"biz_config_group"` // 业务配置组
SYS_CONFIG_GROUP string `json:"sys_config_group"` // 系统配置组
IMAGE_NAME string `json:"image_name"` // 镜像名称
JAVA_VERSION string `json:"java_version"` // java版本
GIT_COMMIT string `json:"git_commit"` // git commit
GIT_BRANCH string `json:"git_branch"` // git branch
NODE_NAME string `json:"node_name"` // 节点名称
NODE_IP string `json:"node_ip"` // 节点ip
POD_NAME string `json:"pod_name"` // pod名称
LIMIT_CPU string `json:"limit_cpu"` // 限制cpu
LIMIT_MEMORY string `json:"limit_memory"` // 限制内存
REQUEST_CPU string `json:"request_cpu"` // 请求cpu
REQUEST_MEMORY string `json:"request_memory"` // 请求内存
}
// HeartbeatRequest 心跳请求
type HeartbeatRequest struct {
HostInfo HostInfo `json:"host_info"` // 主机信息
EnvInfo EnvInfo `json:"env_info"` // 环境信息
Timestamp int64 `json:"timestamp"` // 时间戳
TOTPCode string `json:"totp_code"` // TOTP验证码
AppName string `json:"app_name"` // 应用名称
}
// HeartbeatResponse 心跳响应

View File

@@ -0,0 +1,22 @@
package models
// Project 项目信息
type Project struct {
Name string `json:"name" binding:"required"`
Namespace string `json:"namespace" binding:"required"`
Province string `json:"province"`
City string `json:"city"`
Domain string `json:"domain"`
BusinessMan string `json:"business_man"`
MarketMan string `json:"market_man" `
TierOneSecret string `json:"tier_one_secret" `
TierTwoSecret string `json:"tier_two_secret"`
AuthTime string `json:"auth_time"`
AuthFilePath string `json:"auth_file_path"`
CreateTime string `json:"create_time"`
}
// ProjectPO 项目持久化对象
type ProjectPO struct {
Project
}

View File

@@ -0,0 +1,169 @@
package totp_tier_one
import (
"cmii-uav-watchdog-common/models"
"cmii-uav-watchdog-common/wdd_log"
"crypto/aes"
"crypto/cipher"
"crypto/sha256"
"encoding/base64"
"errors"
"fmt"
"strconv"
"strings"
)
// Encrypt 加密字符串
// 保证使用相同的plaintext和key进行加密在不同时间、不同机器上输出的加密字符串都是固定的
func Encrypt(plaintext string, key string) (encryptedPlaintext string, encryptError error) {
// 创建hash
hasher := sha256.New()
hasher.Write([]byte(key))
keyBytes := hasher.Sum(nil)
// 创建cipher
block, err := aes.NewCipher(keyBytes)
if err != nil {
return "", err
}
// 创建gcm
gcm, err := cipher.NewGCM(block)
if err != nil {
return "", err
}
// 生成确定性nonce - 使用plaintext和key的组合生成确保对相同输入产生相同nonce
nonceHasher := sha256.New()
nonceHasher.Write([]byte(key + "fixed_salt_for_deterministic_result" + plaintext[:min(10, len(plaintext))]))
nonceSource := nonceHasher.Sum(nil)
// 创建合适大小的nonce
nonce := nonceSource[:gcm.NonceSize()]
// 加密
ciphertext := gcm.Seal(nil, nonce, []byte(plaintext), nil)
// 将nonce和密文连接起来
result := append(nonce, ciphertext...)
// 编码为base64
return base64.StdEncoding.EncodeToString(result), nil
}
// Decrypt 解密字符串
// 可以使用加密字符串和key在不同时间、不同机器上解密出原始plaintext
func Decrypt(encrypted string, key string) (decryptedPlaintext string, decryptError error) {
// 解码base64
data, err := base64.StdEncoding.DecodeString(encrypted)
if err != nil {
return "", err
}
// 创建hash
hasher := sha256.New()
hasher.Write([]byte(key))
keyBytes := hasher.Sum(nil)
// 创建cipher
block, err := aes.NewCipher(keyBytes)
if err != nil {
return "", err
}
// 创建gcm
gcm, err := cipher.NewGCM(block)
if err != nil {
return "", err
}
// 检查长度
if len(data) < gcm.NonceSize() {
return "", errors.New("密文太短")
}
// 分离nonce和密文
nonce, ciphertext := data[:gcm.NonceSize()], data[gcm.NonceSize():]
// 解密
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
return "", err
}
return string(plaintext), nil
}
// min 返回两个整数中较小的一个
func min(a, b int) int {
if a < b {
return a
}
return b
}
// EncryptHostInfo 加密主机信息
func EncryptHostInfo(hostInfo models.HostInfo, key string) (encryptedHostInfo string, encryptError error) {
// 生成主机唯一标识作为加密密钥
info := generateHostUniqueID(hostInfo)
// 加密主机信息
encrypted, err := Encrypt(info, key)
if err != nil {
return "", err
}
return encrypted, nil
}
// DecryptHostInfo 解密主机信息
func DecryptHostInfo(encrypted string, hostInfo models.HostInfo, key string) (decryptedOK bool, decryptError error) {
// 解密主机信息
decryptedStr, err := Decrypt(encrypted, key)
if err != nil {
wdd_log.Error("[DecryptHostInfo] - Decrypt失败: %v", err)
return false, err
}
// wdd_log.Info("[DecryptHostInfo] - Decrypt成功: %s", decryptedStr)
// 生成主机唯一标识作为加密密钥
hostUniqueID := generateHostUniqueID(hostInfo)
if hostUniqueID != decryptedStr {
wdd_log.Error("[DecryptHostInfo] - 主机信息篡改")
return false, errors.New("主机信息篡改")
}
return true, nil
}
// generateHostUniqueID 生成主机唯一标识作为加密密钥
func generateHostUniqueID(hostInfo models.HostInfo) string {
// hostinfo.SystemInfo.MachineID
machineID := hostInfo.SystemInfo.MachineID
// hostinfo.SystemInfo.KernelVersion
kernelVersion := hostInfo.SystemInfo.KernelVersion
// 将CPU信息中的ModelName Cores Hypervisor 拼接起来
cpuCores := hostInfo.CPUInfo.Cores
cpuHypervisor := hostInfo.CPUInfo.Hypervisor
cpuModelName := hostInfo.CPUInfo.ModelName
cpuInfo := fmt.Sprintf("%s-%d-%s", cpuModelName, cpuCores, cpuHypervisor)
// 不能使用 mac 地址,因为 每个Pod 的 mac 地址会变化
// hostinfo.SystemInfo.MachineSerial
machineSerial := hostInfo.SystemInfo.MachineSerial
// 将memory中的total 和 used 拼接起来
memoryTotal := hostInfo.MemoryInfo.Total
// 将上述的全部信息拼接起来
info := strings.Join([]string{machineID, kernelVersion, cpuInfo, machineSerial, strconv.FormatUint(memoryTotal, 10)}, "==")
return info
}

View File

@@ -0,0 +1,218 @@
package totp_tier_one
import (
"cmii-uav-watchdog-common/models"
"fmt"
"testing"
)
// TestEncryptDecrypt 测试加密和解密功能
func TestEncryptDecrypt(t *testing.T) {
tests := []struct {
name string
plaintext string
key string
}{
{
name: "正常文本加密解密",
plaintext: "这是一段测试文本",
key: "测试密钥",
},
{
name: "空文本加密解密",
plaintext: "",
key: "测试密钥",
},
{
name: "特殊字符加密解密",
plaintext: "!@#$%^&*()_+{}|:<>?",
key: "!@#$%^&*()_+{}|:<>?",
},
{
name: "长文本加密解密",
plaintext: "这是一段非常长的测试文本,用于测试加密和解密功能是否能够正确处理长文本。这是一段非常长的测试文本,用于测试加密和解密功能是否能够正确处理长文本。",
key: "测试密钥",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// 加密
encrypted, err := Encrypt(tt.plaintext, tt.key)
if err != nil {
t.Fatalf("加密失败: %v", err)
}
// 确保加密后的文本与原始文本不同
if tt.plaintext != "" && encrypted == tt.plaintext {
t.Errorf("加密后的文本与原始文本相同,可能未正确加密")
}
// 解密
decrypted, err := Decrypt(encrypted, tt.key)
if err != nil {
t.Fatalf("解密失败: %v", err)
}
// 确保解密后的文本与原始文本相同
if decrypted != tt.plaintext {
t.Errorf("解密后的文本与原始文本不同,期望:%s实际%s", tt.plaintext, decrypted)
}
})
}
}
// TestDecryptWithWrongKey 测试使用错误密钥解密
func TestDecryptWithWrongKey(t *testing.T) {
plaintext := "这是一段测试文本"
correctKey := "正确密钥"
wrongKey := "错误密钥"
// 使用正确密钥加密
encrypted, err := Encrypt(plaintext, correctKey)
if err != nil {
t.Fatalf("加密失败: %v", err)
}
// 使用错误密钥解密,期望失败
_, err = Decrypt(encrypted, wrongKey)
fmt.Println("解密失败的 error :", err)
if err == nil {
t.Errorf("使用错误密钥解密应该失败,但成功了")
}
}
// TestDecryptInvalidData 测试解密无效的数据
func TestDecryptInvalidData(t *testing.T) {
invalidEncrypted := "这不是有效的加密数据"
key := "测试密钥"
_, err := Decrypt(invalidEncrypted, key)
if err == nil {
t.Errorf("解密无效数据应该失败,但成功了")
}
}
// createMockHostInfo 创建模拟的主机信息用于测试
func createMockHostInfo() models.HostInfo {
return models.HostInfo{
SystemInfo: models.SystemInfo{
MachineID: "test-machine-id",
KernelVersion: "5.10.0-test",
OS: models.OSInfo{
Name: "TestOS",
Version: "1.0",
ID: "test",
IDLike: "test",
VersionID: "1.0",
PrettyName: "Test OS 1.0",
},
},
CPUInfo: models.CPUInfo{
ModelName: "Test CPU",
Cores: 4,
Architecture: "x86_64",
Flags: []string{},
Hypervisor: "",
Virtualization: "",
Frequency: "",
},
DiskInfo: []models.DiskInfo{
{
Device: "/dev/sda1",
Filesystem: "ext4",
Type: "ext4",
Size: 1000000,
Used: 500000,
Available: 500000,
UsePercent: "50%",
MountPoint: "/",
PhysicalDevice: "/dev/sda",
PhysicalSize: 2000000,
},
},
MemoryInfo: models.MemoryInfo{
Total: 8000000,
Free: 4000000,
Available: 4000000,
Used: 4000000,
Buffers: 1000000,
Cached: 1000000,
Shared: 500000,
},
NetInfo: []models.NetworkInterfaceInfo{
{
Name: "eth0",
MACAddress: "00:11:22:33:44:55",
IPAddresses: []string{"192.168.1.100", "fe80::1234"},
},
},
MotherboardInfo: models.MotherboardInfo{
Manufacturer: "Test Manufacturer",
Product: "Test Product",
Version: "1.0",
Serial: "TEST123456",
},
}
}
// TestEncryptDecryptHostInfo 测试主机信息的加密和解密
func TestEncryptDecryptHostInfo(t *testing.T) {
hostInfo := createMockHostInfo()
key := "test-key"
// 加密主机信息
encrypted, err := EncryptHostInfo(hostInfo, key)
fmt.Println("加密后的主机信息:", encrypted)
if err != nil {
t.Fatalf("加密主机信息失败: %v", err)
}
// 确保加密后的文本不为空
if encrypted == "" {
t.Errorf("加密后的主机信息为空")
}
// 解密主机信息
decrypted, err := DecryptHostInfo(encrypted, hostInfo, key)
if err != nil {
t.Fatalf("解密主机信息失败: %v", err)
}
if !decrypted {
t.Errorf("解密后的主机信息与原始主机信息不同")
}
}
// TestEncryptDecryptHostInfoWithModifiedInfo 测试使用修改过的主机信息解密
func TestEncryptDecryptHostInfoWithModifiedInfo(t *testing.T) {
originalHostInfo := createMockHostInfo()
key := "test-key"
// 加密主机信息
encrypted, err := EncryptHostInfo(originalHostInfo, key)
if err != nil {
t.Fatalf("加密主机信息失败: %v", err)
}
t.Logf("加密后的主机信息: %s", encrypted)
// 创建修改过的主机信息
modifiedHostInfo := originalHostInfo
// 加密修改过的主机信息
encryptedModified, err := EncryptHostInfo(modifiedHostInfo, key)
if err != nil {
t.Fatalf("加密修改过的主机信息失败: %v", err)
}
t.Logf("加密修改后的主机信息: %s", encryptedModified)
// 使用修改过的主机信息尝试解密,期望失败
isOK, err := DecryptHostInfo(encryptedModified, originalHostInfo, key)
fmt.Println("解密后的主机信息 error :", err)
if isOK {
t.Errorf("解密后的主机信息与修改后的主机信息相同, 不应该相同")
}
}

View File

@@ -0,0 +1,39 @@
package totp_tier_one
import (
otp "cmii-uav-watchdog-otp"
"cmii-uav-watchdog-otp/totp"
"time"
)
var TierOneTOTPSecretOpts = totp.GenerateOpts{
SecretSize: 64,
Issuer: "cmii-uav-watchdog-center",
AccountName: "cmii-uav-watchdog-center",
Period: 30 * 2 * 30, // 30分钟
Digits: otp.DigitsEight,
Algorithm: otp.AlgorithmSHA256,
}
// GenerateTierOneTOTPCode 生成一级TOTP验证码
func GenerateTierOneTOTPCode(secret string) (string, error) {
validateOpts := totp.ValidateOpts{}
validateOpts.ConvertToValidateOpts(TierOneTOTPSecretOpts)
code, err := totp.GenerateCodeCustom(secret, time.Now(), validateOpts)
if err != nil {
return "", err
}
return code, nil
}
// VerifyTierOneTOTPCode 验证一级TOTP验证码
func VerifyTierOneTOTPCode(code string, secret string) bool {
validateOpts := totp.ValidateOpts{}
validateOpts.ConvertToValidateOpts(TierOneTOTPSecretOpts)
valid, err := totp.ValidateCustom(code, secret, time.Now(), validateOpts)
if err != nil {
return false
}
return valid
}

View File

@@ -0,0 +1,54 @@
package totp_tier_two
import (
"cmii-uav-watchdog-common/utils"
"cmii-uav-watchdog-common/wdd_log"
otp "cmii-uav-watchdog-otp"
"cmii-uav-watchdog-otp/totp"
)
var TierTwoTOTPSecretOpts = totp.GenerateOpts{
SecretSize: 32,
Issuer: "cmii-uav-watchdog",
AccountName: "cmii-uav-watchdog",
Period: 30,
Secret: []byte{},
Digits: otp.DigitsSix,
Algorithm: otp.AlgorithmSHA1,
Rand: nil,
}
// GenerateTierTwoTOTPSecret 生成二级TOTP密钥
func GenerateTierTwoTOTPSecret() (string, error) {
secret, err := totp.Generate(TierTwoTOTPSecretOpts)
if err != nil {
wdd_log.Error("生成TOTP密钥失败: %v", err)
return "", err
}
wdd_log.Info("生成TOTP密钥成功: %s", secret.Secret())
return secret.Secret(), nil
}
// GenerateTierTwoTOTPCode 生成二级TOTP验证码
func GenerateTierTwoTOTPCode(secret string) (string, error) {
validateOpts := totp.ValidateOpts{}
validateOpts.ConvertToValidateOpts(TierTwoTOTPSecretOpts)
code, err := totp.GenerateCodeCustom(secret, utils.CurentTime(), validateOpts)
if err != nil {
wdd_log.Error("TierTwo TOTP验证码生成失败: %v", err)
return "", err
}
return code, nil
}
// VerifyTierTwoTOTPCode 验证二级TOTP验证码
func VerifyTierTwoTOTPCode(code string, secret string) bool {
validateOpts := totp.ValidateOpts{}
validateOpts.ConvertToValidateOpts(TierTwoTOTPSecretOpts)
valid, err := totp.ValidateCustom(code, secret, utils.CurentTime(), validateOpts)
if err != nil {
wdd_log.Error("TierTwo TOTP验证失败: %v", err)
return false
}
return valid
}

View File

@@ -0,0 +1,36 @@
package utils
import (
"time"
)
var CST = time.FixedZone("CST", 8*60*60)
// CurentTimeString 获取当前时间字符串 东八区时间
func CurentTimeString() string {
return time.Now().In(CST).Format("2006-01-02 15:04:05")
}
// CurentTime 获取当前时间 东八区时间
func CurentTime() time.Time {
return time.Now().In(CST)
}
// CurentTimeUnix 获取当前时间戳 东八区时间
func CurentTimeUnix() int64 {
return CurentTime().Unix()
}
// ParseTimeString 解析时间字符串 东八区时间
func ParseTimeString(timeString string) (time.Time, error) {
return time.ParseInLocation("2006-01-02 15:04:05", timeString, CST)
}
// ParseTimeUnix 解析时间戳 东八区时间
func ParseTimeUnix(unix int64) (time.Time, error) {
return time.Unix(unix, 0).In(CST), nil
}

View File

@@ -0,0 +1,150 @@
package wdd_log
import (
"cmii-uav-watchdog-common/utils"
"fmt"
"sync"
)
const (
colorReset = "\033[0m"
colorRed = "\033[31m"
colorGreen = "\033[32m"
colorYellow = "\033[33m"
colorBlue = "\033[34m"
colorPurple = "\033[35m"
colorCyan = "\033[36m"
colorWhite = "\033[37m"
)
type LogLevel int
const (
LevelDebug LogLevel = iota
LevelInfo
LevelWarn
LevelError
LevelFatal
LogPrefix = "cmii-uav-watchdog"
)
var logLevelColors = map[LogLevel]string{
LevelDebug: colorCyan,
LevelInfo: colorGreen,
LevelWarn: colorYellow,
LevelError: colorRed,
LevelFatal: colorPurple,
}
var logLevelNames = map[LogLevel]string{
LevelDebug: "DEBUG",
LevelInfo: "INFO",
LevelWarn: "WARN",
LevelError: "ERROR",
LevelFatal: "FATAL",
}
// Logger 日志单例结构体
type Logger struct {
// EnableDebug 是否启用Debug日志
EnableDebug bool
}
var (
instance *Logger
once sync.Once
)
// GetInstance 获取Logger单例实例
// 返回Logger的单例对象指针
func GetInstance() *Logger {
once.Do(func() {
instance = &Logger{
EnableDebug: false, // 默认不启用Debug日志
}
})
return instance
}
// SetEnableDebug 设置是否启用Debug日志
// enable: true表示启用Debug日志false表示禁用
func (l *Logger) SetEnableDebug(enable bool) {
l.EnableDebug = enable
}
// Log 记录指定级别的日志
// level: 日志级别
// format: 日志格式
// args: 格式化参数
func (l *Logger) Log(level LogLevel, format string, args ...interface{}) {
// Debug级别日志在未启用时不输出
if level == LevelDebug && !l.EnableDebug {
return
}
now := utils.CurentTimeString()
color := logLevelColors[level]
levelName := logLevelNames[level]
message := fmt.Sprintf(format, args...)
fmt.Printf("[%s] %s %s%s%s %s\n", LogPrefix, now, color, levelName, colorReset, message)
}
// Debug 记录Debug级别日志
// format: 日志格式
// args: 格式化参数
func (l *Logger) Debug(format string, args ...interface{}) {
l.Log(LevelDebug, format, args...)
}
// Info 记录Info级别日志
// format: 日志格式
// args: 格式化参数
func (l *Logger) Info(format string, args ...interface{}) {
l.Log(LevelInfo, format, args...)
}
// Warn 记录Warn级别日志
// format: 日志格式
// args: 格式化参数
func (l *Logger) Warn(format string, args ...interface{}) {
l.Log(LevelWarn, format, args...)
}
// Error 记录Error级别日志
// format: 日志格式
// args: 格式化参数
func (l *Logger) Error(format string, args ...interface{}) {
l.Log(LevelError, format, args...)
}
// Fatal 记录Fatal级别日志
// format: 日志格式
// args: 格式化参数
func (l *Logger) Fatal(format string, args ...interface{}) {
l.Log(LevelFatal, format, args...)
}
// 为了兼容原有代码,保留全局函数,但内部调用单例实例
func Log(level LogLevel, format string, args ...interface{}) {
GetInstance().Log(level, format, args...)
}
func Debug(format string, args ...interface{}) {
GetInstance().Debug(format, args...)
}
func Info(format string, args ...interface{}) {
GetInstance().Info(format, args...)
}
func Warn(format string, args ...interface{}) {
GetInstance().Warn(format, args...)
}
func Error(format string, args ...interface{}) {
GetInstance().Error(format, args...)
}
func Fatal(format string, args ...interface{}) {
GetInstance().Fatal(format, args...)
}