go17/book/config/config.go

229 lines
5.2 KiB
Go
Raw Normal View History

2024-11-16 16:00:55 +08:00
package config
2024-11-16 18:11:24 +08:00
import (
"encoding/json"
"fmt"
2024-11-23 15:17:11 +08:00
"io"
"strconv"
"strings"
2024-11-16 18:11:24 +08:00
"sync"
2024-11-23 15:17:11 +08:00
"time"
2024-11-16 18:11:24 +08:00
2024-11-23 15:17:11 +08:00
"github.com/rs/zerolog"
"github.com/rs/zerolog/pkgerrors"
"gopkg.in/natefinch/lumberjack.v2"
2024-11-16 18:11:24 +08:00
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
2024-11-16 17:22:21 +08:00
2024-11-16 16:00:55 +08:00
// 定义程序配置
// dsn := "root:123456@tcp(127.0.0.1:3306)/test?charset=utf8mb4&parseTime=True&loc=Local"
// 凡是可以提出出配置的
// 程序的配置对象
type Config struct {
2024-11-16 17:22:21 +08:00
App *App `json:"app"`
MySQL *MySQL `json:"mysql"`
2024-11-23 15:17:11 +08:00
Log *Log `json:"log"`
2024-11-16 17:22:21 +08:00
}
// &{0x102317ec0 0x10231c8a0}
//
// String() string, fmt.Strigger接口
//
// fmt.
func (c *Config) String() string {
v, _ := json.Marshal(c)
return string(v)
2024-11-16 16:00:55 +08:00
}
// app:
//
// host: 127.0.0.1
// port: 8080
type App struct {
Host string `json:"host"`
Port int `json:"port"`
}
2024-11-16 18:11:24 +08:00
func (c *App) Address() string {
return fmt.Sprintf("%s:%d", c.Host, c.Port)
}
2024-11-16 16:00:55 +08:00
// mysql:
//
// host: 127.0.0.1
// port: 3306
// database: test
// username: "root"
// password: "123456"
// debug: true
type MySQL struct {
Host string `json:"host"`
Port int `json:"port"`
Database string `json:"database"`
Username string `json:"username"`
Password string `json:"password"`
Debug bool `json:"debug"`
2024-11-16 18:11:24 +08:00
db *gorm.DB
lock sync.Mutex
}
// 初始化数据库, 能过与数据库交互的 连接池对象: db
// 复用, 只要有就用 之前的没有才初始化新的db对象
func (c *MySQL) DB() *gorm.DB {
c.lock.Lock()
defer c.lock.Unlock()
if c.db == nil {
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",
c.Username,
c.Password,
c.Host,
c.Port,
c.Database,
)
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic("failed to connect database")
}
if c.Debug {
db = db.Debug()
}
c.db = db
}
return c.db
2024-11-16 16:00:55 +08:00
}
2024-11-23 15:17:11 +08:00
// 怎么配置
// 是否要打印到控制台
// 是否要打印到文件里,是否要轮转 100M 5
type Log struct {
// 0 为打印日志全路径, 默认打印2层路径
CallerDeep int `toml:"caller_deep" json:"caller_deep" yaml:"caller_deep" env:"CALLER_DEEP"`
// 日志的级别, 默认Debug
Level zerolog.Level `toml:"level" json:"level" yaml:"level" env:"LEVEL"`
// 控制台日志配置
Console Console `toml:"console" json:"console" yaml:"console" envPrefix:"CONSOLE_"`
// 日志文件配置
File File `toml:"file" json:"file" yaml:"file" envPrefix:"FILE_"`
root *zerolog.Logger
lock sync.Mutex
}
type Console struct {
Enable bool `toml:"enable" json:"enable" yaml:"enable" env:"ENABLE"`
NoColor bool `toml:"no_color" json:"no_color" yaml:"no_color" env:"NO_COLOR"`
}
func (c *Console) ConsoleWriter() io.Writer {
output := zerolog.NewConsoleWriter(func(w *zerolog.ConsoleWriter) {
w.NoColor = c.NoColor
w.TimeFormat = time.RFC3339
})
output.FormatLevel = func(i interface{}) string {
return strings.ToUpper(fmt.Sprintf("%-6s", i))
}
output.FormatMessage = func(i interface{}) string {
return fmt.Sprintf("%s", i)
}
output.FormatFieldName = func(i interface{}) string {
return fmt.Sprintf("%s:", i)
}
output.FormatFieldValue = func(i interface{}) string {
return strings.ToUpper(fmt.Sprintf("%s", i))
}
return output
}
type File struct {
// 是否开启文件记录
Enable bool `toml:"enable" json:"enable" yaml:"enable" env:"ENABLE"`
// 文件的路径
FilePath string `toml:"file_path" json:"file_path" yaml:"file_path" env:"PATH"`
// 单位M, 默认100M
MaxSize int `toml:"max_size" json:"max_size" yaml:"max_size" env:"MAX_SIZE"`
// 默认保存 6个文件
MaxBackups int `toml:"max_backups" json:"max_backups" yaml:"max_backups" env:"MAX_BACKUPS"`
// 保存多久
MaxAge int `toml:"max_age" json:"max_age" yaml:"max_age" env:"MAX_AGE"`
// 是否压缩
Compress bool `toml:"compress" json:"compress" yaml:"compress" env:"COMPRESS"`
}
func (f *File) FileWriter() io.Writer {
return &lumberjack.Logger{
Filename: f.FilePath,
MaxSize: f.MaxSize,
MaxAge: f.MaxAge,
MaxBackups: f.MaxBackups,
Compress: f.Compress,
}
}
func (l *Log) Logger() *zerolog.Logger {
l.lock.Lock()
defer l.lock.Unlock()
if l.root == nil {
var writers []io.Writer
if l.Console.Enable {
writers = append(writers, l.Console.ConsoleWriter())
}
if l.File.Enable {
if l.File.FilePath == "" {
l.File.FilePath = "logs/app.log"
}
writers = append(writers, l.File.FileWriter())
}
zerolog.ErrorStackMarshaler = pkgerrors.MarshalStack
// logger对象配置一些上下文信息: 2024-11-23T15:11:42+08:00
// 带上中间信息Any
// book/controller/book.go:60
root := zerolog.New(io.MultiWriter(writers...)).With().Timestamp().Any("服务", "book-api")
if l.CallerDeep > 0 {
zerolog.CallerMarshalFunc = l.CallerMarshalFunc
root = root.Caller()
}
// 带有这些上下文信息的Logger对象 提前出来作为全局Logger对象供全局使用
l.SetRoot(root.Logger().Level(l.Level))
}
return l.root
}
func (m *Log) SetRoot(v zerolog.Logger) {
m.root = &v
}
func (m *Log) CallerMarshalFunc(pc uintptr, file string, line int) string {
if m.CallerDeep == 0 {
return file
}
short := file
count := 0
for i := len(file) - 1; i > 0; i-- {
if file[i] == '/' {
short = file[i+1:]
count++
}
if count >= m.CallerDeep {
break
}
}
file = short
return file + ":" + strconv.Itoa(line)
}