feat(auth): 添加完整的用户认证API项目
- 实现用户注册、登录、JWT令牌认证功能 - 集成Gin、GORM、Viper、Zap等框架 - 添加密码加密、数据库操作、中间件等完整功能 - 配置多环境支持、日志轮转、CORS处理 - 创建完整的项目结构和配置文件体系
This commit is contained in:
3
go基础语法/01hello-go/go.mod
Normal file
3
go基础语法/01hello-go/go.mod
Normal file
@@ -0,0 +1,3 @@
|
||||
module hello-go
|
||||
|
||||
go 1.22.2
|
||||
BIN
go基础语法/01hello-go/hello-go
Executable file
BIN
go基础语法/01hello-go/hello-go
Executable file
Binary file not shown.
7
go基础语法/01hello-go/main.go
Normal file
7
go基础语法/01hello-go/main.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
fmt.Println("Hello, Go from Linux Mint!")
|
||||
}
|
||||
3
go基础语法/02go-basics/go.mod
Normal file
3
go基础语法/02go-basics/go.mod
Normal file
@@ -0,0 +1,3 @@
|
||||
module go-basics
|
||||
|
||||
go 1.22.2
|
||||
82
go基础语法/02go-basics/main.go
Normal file
82
go基础语法/02go-basics/main.go
Normal file
@@ -0,0 +1,82 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
// ========== 1. 基本类型 ==========
|
||||
var a int = 42
|
||||
var b float64 = 3.14
|
||||
var c bool = true
|
||||
var d string = "Hello, Go!"
|
||||
|
||||
fmt.Println("基本类型:")
|
||||
fmt.Printf("int: %d\n", a)
|
||||
fmt.Printf("float64: %.2f\n", b)
|
||||
fmt.Printf("bool: %t\n", c)
|
||||
fmt.Printf("string: %s\n", d)
|
||||
|
||||
// ========== 2. 变量声明方式 ==========
|
||||
// 方式1:var 声明(可带或不带初始值)
|
||||
var x int
|
||||
x = 100
|
||||
var y = 200 // 类型自动推导
|
||||
|
||||
// 方式2:批量声明
|
||||
var (
|
||||
name = "Liu"
|
||||
age = 30
|
||||
height = 175.5
|
||||
isAdmin = true
|
||||
)
|
||||
|
||||
fmt.Println("\n变量声明:")
|
||||
fmt.Println("x =", x, ", y =", y)
|
||||
fmt.Println("name:", name, "age:", age, "height:", height, "isAdmin:", isAdmin)
|
||||
|
||||
// ========== 3. := 简短声明(仅在函数内部可用)==========
|
||||
z := 999 // 自动推导为 int
|
||||
message := "Use := !" // 自动推导为 string
|
||||
fmt.Println("\n简短声明 (:=):")
|
||||
fmt.Println("z =", z, ", message =", message)
|
||||
|
||||
// 注意::= 必须至少声明一个新变量
|
||||
// 下面这行会报错:no new variables on left side of :=
|
||||
//z := 888 // ❌ 错误!
|
||||
|
||||
// 但可以这样混合新旧变量:
|
||||
w, z := "new", 888 // w 是新的,z 是重新赋值(shadowing)
|
||||
fmt.Println("w =", w, ", z =", z)
|
||||
|
||||
// ========== 4. 指针 ==========
|
||||
p := &a // p 是指向 a 的指针(*int 类型)
|
||||
fmt.Println("\n指针:")
|
||||
fmt.Printf("a 的值: %d\n", a)
|
||||
fmt.Printf("a 的地址: %p\n", &a)
|
||||
fmt.Printf("p 的值(即 a 的地址): %p\n", p)
|
||||
fmt.Printf("*p(解引用): %d\n", *p)
|
||||
|
||||
*p = 1000 // 通过指针修改 a 的值
|
||||
fmt.Println("修改后 a =", a)
|
||||
|
||||
// ========== 5. new vs make ==========
|
||||
// new(T) → 分配零值内存,返回 *T
|
||||
ptrInt := new(int) // *int,值为 0
|
||||
*ptrInt = 42
|
||||
fmt.Println("\nnew(int):", *ptrInt)
|
||||
|
||||
// make 仅用于 slice, map, chan —— 返回初始化后的 T(不是指针!)
|
||||
s := make([]int, 3) // 长度为3的 slice,元素为 [0 0 0]
|
||||
m := make(map[string]int) // 空 map
|
||||
ch := make(chan int, 2) // 缓冲通道
|
||||
|
||||
s[0] = 10
|
||||
m["count"] = 1
|
||||
ch <- 100
|
||||
|
||||
fmt.Println("make(slice):", s)
|
||||
fmt.Println("make(map):", m)
|
||||
fmt.Println("make(chan) 示例(略去接收)")
|
||||
|
||||
// ⚠️ 不能对 slice/map/chan 使用 new!
|
||||
// bad := new([]int) // 这是一个 *[]int,但底层数组未初始化,不能直接用!
|
||||
}
|
||||
3
go基础语法/03go-slice-map/go.mod
Normal file
3
go基础语法/03go-slice-map/go.mod
Normal file
@@ -0,0 +1,3 @@
|
||||
module go-slice-map
|
||||
|
||||
go 1.22.2
|
||||
119
go基础语法/03go-slice-map/main.go
Normal file
119
go基础语法/03go-slice-map/main.go
Normal file
@@ -0,0 +1,119 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println("=== 1. Slice 扩容机制 ===")
|
||||
demoSliceGrowth()
|
||||
|
||||
fmt.Println("\n=== 2. Slice 深拷贝 vs 浅拷贝 ===")
|
||||
demoSliceCopy()
|
||||
|
||||
fmt.Println("\n=== 3. Map 基本使用 ===")
|
||||
demoMapUsage()
|
||||
|
||||
fmt.Println("\n=== 4. Map 并发写风险(会 panic!)===")
|
||||
// ⚠️ 下面这行默认注释掉,避免程序崩溃
|
||||
// demoUnsafeConcurrentMap()
|
||||
|
||||
fmt.Println("\n=== 5. 安全的并发 map(sync.Map)===")
|
||||
demoSafeConcurrentMap()
|
||||
}
|
||||
|
||||
// 1. 演示 slice 扩容机制
|
||||
func demoSliceGrowth() {
|
||||
var s []int
|
||||
fmt.Printf("初始: len=%d, cap=%d, addr=%p\n", len(s), cap(s), s)
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
s = append(s, i)
|
||||
fmt.Printf("append %d: len=%d, cap=%d, addr=%p\n", i, len(s), cap(s), s)
|
||||
}
|
||||
// 观察:当容量不足时,底层数组会重新分配,地址改变
|
||||
}
|
||||
|
||||
// 2. 深拷贝 vs 浅拷贝
|
||||
func demoSliceCopy() {
|
||||
original := []int{1, 2, 3, 4, 5}
|
||||
shallow := original // 浅拷贝:共享底层数组
|
||||
deep := make([]int, len(original))
|
||||
copy(deep, original) // 深拷贝:独立底层数组
|
||||
|
||||
fmt.Println("修改前:")
|
||||
fmt.Println("original:", original)
|
||||
fmt.Println("shallow :", shallow)
|
||||
fmt.Println("deep :", deep)
|
||||
|
||||
// 修改原 slice
|
||||
original[0] = 999
|
||||
|
||||
fmt.Println("\n修改 original[0] = 999 后:")
|
||||
fmt.Println("original:", original) // [999, 2, 3, 4, 5]
|
||||
fmt.Println("shallow :", shallow) // [999, 2, 3, 4, 5] ← 被影响!
|
||||
fmt.Println("deep :", deep) // [1, 2, 3, 4, 5] ← 不受影响
|
||||
}
|
||||
|
||||
// 3. Map 基本使用
|
||||
func demoMapUsage() {
|
||||
m := make(map[string]int)
|
||||
m["apple"] = 5
|
||||
m["banana"] = 3
|
||||
|
||||
fmt.Println("map 内容:", m)
|
||||
fmt.Println("apple 数量:", m["apple"])
|
||||
|
||||
// 检查 key 是否存在
|
||||
if count, ok := m["orange"]; ok {
|
||||
fmt.Println("orange 存在,数量:", count)
|
||||
} else {
|
||||
fmt.Println("orange 不存在")
|
||||
}
|
||||
|
||||
// 删除 key
|
||||
delete(m, "apple")
|
||||
fmt.Println("删除 apple 后:", m)
|
||||
}
|
||||
|
||||
// 4. 不安全的并发 map 写入(会导致 panic)
|
||||
func demoUnsafeConcurrentMap() {
|
||||
m := make(map[int]int)
|
||||
var wg sync.WaitGroup
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
wg.Add(1)
|
||||
go func(key int) {
|
||||
defer wg.Done()
|
||||
m[key] = key * 10 // 并发写 → fatal error: concurrent map writes
|
||||
}(i)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
fmt.Println("unsafe map done (should not reach here)")
|
||||
}
|
||||
|
||||
// 5. 使用 sync.Map 实现安全并发
|
||||
func demoSafeConcurrentMap() {
|
||||
var sm sync.Map
|
||||
var wg sync.WaitGroup
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
wg.Add(1)
|
||||
go func(key int) {
|
||||
defer wg.Done()
|
||||
sm.Store(key, key*10)
|
||||
}(i)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
// 读取部分值验证
|
||||
sm.Range(func(key, value any) bool {
|
||||
if key.(int) < 5 { // 只打印前几个
|
||||
fmt.Printf("key=%v, value=%v\n", key, value)
|
||||
}
|
||||
return true // 继续遍历
|
||||
})
|
||||
}
|
||||
3
go基础语法/04go-struct-methods/go.mod
Normal file
3
go基础语法/04go-struct-methods/go.mod
Normal file
@@ -0,0 +1,3 @@
|
||||
module go-struct-methods
|
||||
|
||||
go 1.22.2
|
||||
61
go基础语法/04go-struct-methods/main.go
Normal file
61
go基础语法/04go-struct-methods/main.go
Normal file
@@ -0,0 +1,61 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
// 定义一个 Person 结构体
|
||||
type Person struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
|
||||
// ========== 值接收者方法 ==========
|
||||
func (p Person) SayHello() {
|
||||
fmt.Printf("Hello, I'm %s (值接收者)\n", p.Name)
|
||||
}
|
||||
|
||||
// 值接收者无法修改原始结构体字段
|
||||
func (p Person) SetName(name string) {
|
||||
p.Name = name // 修改的是副本!
|
||||
fmt.Printf("在值接收者中设置 Name = %s\n", p.Name)
|
||||
}
|
||||
|
||||
// ========== 指针接收者方法 ==========
|
||||
func (p *Person) SayHelloPtr() {
|
||||
fmt.Printf("Hello, I'm %s (指针接收者)\n", p.Name)
|
||||
}
|
||||
|
||||
// 指针接收者可以真正修改原始结构体
|
||||
func (p *Person) SetNamePtr(name string) {
|
||||
p.Name = name // 修改的是原始对象!
|
||||
fmt.Printf("在指针接收者中设置 Name = %s\n", p.Name)
|
||||
}
|
||||
|
||||
// ========== 对比调用行为 ==========
|
||||
func main() {
|
||||
fmt.Println("=== 1. 使用值类型变量调用方法 ===")
|
||||
p1 := Person{Name: "Alice", Age: 30}
|
||||
p1.SayHello() // OK
|
||||
p1.SetName("Alicia") // ❌ 不会改变 p1.Name
|
||||
fmt.Printf("调用值接收者 SetName 后,p1.Name = %s\n", p1.Name)
|
||||
|
||||
p1.SayHelloPtr() // ✅ Go 自动取地址 (&p1)
|
||||
p1.SetNamePtr("Anna") // ✅ 真正修改了 p1
|
||||
fmt.Printf("调用指针接收者 SetNamePtr 后,p1.Name = %s\n", p1.Name)
|
||||
|
||||
fmt.Println("\n=== 2. 使用指针类型变量调用方法 ===")
|
||||
p2 := &Person{Name: "Bob", Age: 25}
|
||||
p2.SayHello() // ✅ Go 自动解引用 (*p2)
|
||||
p2.SayHelloPtr() // ✅ 直接调用
|
||||
|
||||
p2.SetName("Bobby") // ❌ 副本修改,无效
|
||||
fmt.Printf("调用值接收者 SetName 后,p2.Name = %s\n", p2.Name)
|
||||
|
||||
p2.SetNamePtr("Robert") // ✅ 真正修改
|
||||
fmt.Printf("调用指针接收者 SetNamePtr 后,p2.Name = %s\n", p2.Name)
|
||||
|
||||
fmt.Println("\n=== 3. 关键规则总结 ===")
|
||||
fmt.Println("- 值接收者:操作副本,不能修改原结构体")
|
||||
fmt.Println("- 指针接收者:操作原始数据,可修改")
|
||||
fmt.Println("- Go 会自动处理 & 和 * 转换(只要变量可寻址)")
|
||||
fmt.Println("- 如果方法需要修改字段,请使用指针接收者!")
|
||||
}
|
||||
3
go基础语法/05go-interfaces/go.mod
Normal file
3
go基础语法/05go-interfaces/go.mod
Normal file
@@ -0,0 +1,3 @@
|
||||
module go-interfaces
|
||||
|
||||
go 1.22.2
|
||||
94
go基础语法/05go-interfaces/main.go
Normal file
94
go基础语法/05go-interfaces/main.go
Normal file
@@ -0,0 +1,94 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
// ========== 1. 定义接口(鸭子类型)==========
|
||||
type Speaker interface {
|
||||
Speak() string
|
||||
}
|
||||
|
||||
type Dog struct{}
|
||||
|
||||
func (d Dog) Speak() string {
|
||||
return "Woof!"
|
||||
}
|
||||
|
||||
type Cat struct{}
|
||||
|
||||
func (c Cat) Speak() string {
|
||||
return "Meow~"
|
||||
}
|
||||
|
||||
type Robot struct{}
|
||||
|
||||
func (r Robot) Speak() string {
|
||||
return "Beep boop."
|
||||
}
|
||||
|
||||
// ========== 2. 空接口(interface{})==========
|
||||
// 在 Go 1.18+ 中,推荐使用 any(它是 interface{} 的别名)
|
||||
func printAnything(v any) {
|
||||
fmt.Printf("接收到: %v (类型: %T)\n", v, v)
|
||||
}
|
||||
|
||||
// ========== 3. 类型断言 ==========
|
||||
func describeSpeaker(s Speaker) {
|
||||
fmt.Println("它说:", s.Speak())
|
||||
|
||||
// 类型断言:判断具体类型
|
||||
if d, ok := s.(Dog); ok {
|
||||
fmt.Println("这是一只狗!", d)
|
||||
} else if c, ok := s.(Cat); ok {
|
||||
fmt.Println("这是一只猫!", c)
|
||||
}
|
||||
}
|
||||
|
||||
// 使用 switch 进行类型断言(更优雅)
|
||||
func identify(v any) {
|
||||
switch x := v.(type) {
|
||||
case string:
|
||||
fmt.Printf("字符串: %s\n", x)
|
||||
case int:
|
||||
fmt.Printf("整数: %d\n", x)
|
||||
case Speaker:
|
||||
fmt.Printf("会说话的东西: %s\n", x.Speak())
|
||||
default:
|
||||
fmt.Printf("未知类型: %T\n", x)
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 主函数 ==========
|
||||
func main() {
|
||||
fmt.Println("=== 1. 鸭子类型:只要会 Speak(),就是 Speaker ===")
|
||||
animals := []Speaker{Dog{}, Cat{}, Robot{}}
|
||||
for _, a := range animals {
|
||||
fmt.Println(a.Speak())
|
||||
}
|
||||
|
||||
fmt.Println("\n=== 2. 空接口(any)可接收任意类型 ===")
|
||||
printAnything(42)
|
||||
printAnything("Hello")
|
||||
printAnything(Dog{})
|
||||
|
||||
fmt.Println("\n=== 3. 类型断言:从接口还原具体类型 ===")
|
||||
describeSpeaker(Dog{})
|
||||
describeSpeaker(Cat{})
|
||||
|
||||
fmt.Println("\n=== 4. 类型 switch:安全高效的类型判断 ===")
|
||||
identify("Gopher")
|
||||
identify(100)
|
||||
identify(Robot{})
|
||||
identify(true) // 未知类型
|
||||
|
||||
fmt.Println("\n=== 5. 安全 vs 不安全的类型断言 ===")
|
||||
var i any = "hello"
|
||||
s := i.(string) // 不安全:如果类型不对,会 panic
|
||||
fmt.Println("不安全断言结果:", s)
|
||||
|
||||
// 安全方式
|
||||
if val, ok := i.(int); ok {
|
||||
fmt.Println("是整数:", val)
|
||||
} else {
|
||||
fmt.Println("不是整数!")
|
||||
}
|
||||
}
|
||||
3
go基础语法/06go-error-handling/go.mod
Normal file
3
go基础语法/06go-error-handling/go.mod
Normal file
@@ -0,0 +1,3 @@
|
||||
module go-error-handling
|
||||
|
||||
go 1.22.2
|
||||
122
go基础语法/06go-error-handling/main.go
Normal file
122
go基础语法/06go-error-handling/main.go
Normal file
@@ -0,0 +1,122 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
// ========== 1. 自定义错误 + fmt.Errorf 封装 ==========
|
||||
func divide(a, b float64) (float64, error) {
|
||||
if b == 0 {
|
||||
// 使用 fmt.Errorf 包装原始错误,添加上下文
|
||||
return 0, fmt.Errorf("divide by zero: cannot divide %.2f by %.2f", a, b)
|
||||
}
|
||||
return a / b, nil
|
||||
}
|
||||
|
||||
// 更复杂的错误链(Go 1.13+ 支持 %w)
|
||||
var ErrNegativeInput = errors.New("input must be non-negative")
|
||||
|
||||
func sqrt(x float64) (float64, error) {
|
||||
if x < 0 {
|
||||
// 使用 %w 包装错误,支持 errors.Is 和 errors.As
|
||||
return 0, fmt.Errorf("invalid input for sqrt: %w", ErrNegativeInput)
|
||||
}
|
||||
return x * x, nil // 注意:这里故意写成平方,方便测试
|
||||
}
|
||||
|
||||
// ========== 2. defer 的典型用途 ==========
|
||||
func readFile(filename string) error {
|
||||
fmt.Println("尝试打开文件:", filename)
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return fmt.Errorf("无法打开文件 %s: %w", filename, err)
|
||||
}
|
||||
// defer 确保文件在函数退出前关闭(即使 panic)
|
||||
defer func() {
|
||||
fmt.Println("defer: 关闭文件")
|
||||
file.Close()
|
||||
}()
|
||||
|
||||
// 模拟读取
|
||||
fmt.Println("文件已打开,正在读取...")
|
||||
return nil
|
||||
}
|
||||
|
||||
// ========== 3. panic 与 recover ==========
|
||||
func riskyFunction(n int) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
fmt.Printf("recover 捕获到 panic: %v\n", r)
|
||||
}
|
||||
}()
|
||||
if n < 0 {
|
||||
panic("n 不能为负数!")
|
||||
}
|
||||
fmt.Println("riskyFunction 正常执行,n =", n)
|
||||
}
|
||||
|
||||
// recover 只在 defer 中有效!
|
||||
func noRecover() {
|
||||
// ❌ 这样写无法捕获 panic
|
||||
recover()
|
||||
panic("不会被捕获")
|
||||
}
|
||||
|
||||
// ========== 4. 综合示例:安全计算 ==========
|
||||
func safeCompute(a, b float64) {
|
||||
defer fmt.Println("safeCompute 结束\n---")
|
||||
|
||||
fmt.Printf("计算 %.2f / %.2f\n", a, b)
|
||||
result, err := divide(a, b)
|
||||
if err != nil {
|
||||
fmt.Println("错误:", err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("结果: %.2f\n", result)
|
||||
|
||||
fmt.Println("尝试对结果开方(实际是平方)")
|
||||
sq, err := sqrt(result)
|
||||
if err != nil {
|
||||
if errors.Is(err, ErrNegativeInput) {
|
||||
fmt.Println("检测到特定错误:输入为负")
|
||||
}
|
||||
fmt.Println("sqrt 错误:", err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("平方结果: %.2f\n", sq)
|
||||
}
|
||||
|
||||
// ========== 主函数 ==========
|
||||
func main() {
|
||||
fmt.Println("=== 1. 基本 error 处理 ===")
|
||||
safeCompute(10, 2)
|
||||
safeCompute(10, 0) // 触发除零错误
|
||||
|
||||
fmt.Println("\n=== 2. defer 文件操作 ===")
|
||||
fileErr := readFile("/nonexistent/file.txt") // 文件不存在,触发错误
|
||||
if fileErr != nil {
|
||||
fmt.Println("文件操作错误:", fileErr)
|
||||
}
|
||||
fileErr2 := readFile("/etc/hostname") // 存在的文件(Linux 系统通常有)
|
||||
if fileErr2 != nil {
|
||||
fmt.Println("文件操作错误:", fileErr2)
|
||||
}
|
||||
fmt.Println("\n=== 3. panic 与 recover ===")
|
||||
riskyFunction(5)
|
||||
riskyFunction(-1) // 触发 panic,但被 recover 捕获
|
||||
|
||||
fmt.Println("\n=== 4. 错误封装与识别 ===")
|
||||
_, err := sqrt(-4)
|
||||
if err != nil {
|
||||
fmt.Println("原始错误信息:", err)
|
||||
// 使用 errors.Is 判断是否包含特定错误
|
||||
if errors.Is(err, ErrNegativeInput) {
|
||||
fmt.Println("✅ 成功识别自定义错误 ErrNegativeInput")
|
||||
}
|
||||
}
|
||||
|
||||
// ⚠️ 取消注释下面这行会 crash(recover 不在 defer 中无效)
|
||||
//noRecover()
|
||||
}
|
||||
3
go基础语法/07go-mini-logger/app.log
Normal file
3
go基础语法/07go-mini-logger/app.log
Normal file
@@ -0,0 +1,3 @@
|
||||
[2025-12-22 17:16:55] [INFO] 应用开始处理请求
|
||||
[2025-12-22 17:16:55] [WARN] 用户 liumangmang 尝试越权操作
|
||||
[2025-12-22 17:16:55] [ERROR] 写入数据库时发生唯一键冲突
|
||||
3
go基础语法/07go-mini-logger/go.mod
Normal file
3
go基础语法/07go-mini-logger/go.mod
Normal file
@@ -0,0 +1,3 @@
|
||||
module go-mini-logger
|
||||
|
||||
go 1.22.2
|
||||
90
go基础语法/07go-mini-logger/logger.go
Normal file
90
go基础语法/07go-mini-logger/logger.go
Normal file
@@ -0,0 +1,90 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
// LogLevel 日志级别
|
||||
type LogLevel int
|
||||
|
||||
const (
|
||||
LevelInfo LogLevel = iota
|
||||
LevelWarn
|
||||
LevelError
|
||||
)
|
||||
|
||||
func (l LogLevel) String() string {
|
||||
switch l {
|
||||
case LevelInfo:
|
||||
return "INFO"
|
||||
case LevelWarn:
|
||||
return "WARN"
|
||||
case LevelError:
|
||||
return "ERROR"
|
||||
default:
|
||||
return "UNKNOWN"
|
||||
}
|
||||
}
|
||||
|
||||
// Logger 接口:定义日志行为
|
||||
type Logger interface {
|
||||
Log(level LogLevel, format string, args ...any)
|
||||
Info(format string, args ...any)
|
||||
Warn(format string, args ...any)
|
||||
Error(format string, args ...any)
|
||||
SetMinLevel(level LogLevel)
|
||||
}
|
||||
|
||||
// defaultLogger 结构体:默认实现
|
||||
type defaultLogger struct {
|
||||
minLevel LogLevel
|
||||
writer io.Writer
|
||||
}
|
||||
|
||||
// NewLogger 创建新日志实例,默认输出到 os.Stdout
|
||||
func NewLogger() Logger {
|
||||
return &defaultLogger{
|
||||
minLevel: LevelInfo,
|
||||
writer: os.Stdout,
|
||||
}
|
||||
}
|
||||
|
||||
// NewFileLogger 创建写入文件的日志实例
|
||||
func NewFileLogger(filename string) (Logger, error) {
|
||||
file, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("无法创建日志文件 %s: %w", filename, err)
|
||||
}
|
||||
return &defaultLogger{
|
||||
minLevel: LevelInfo,
|
||||
writer: file,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// SetMinLevel 设置最低日志级别
|
||||
func (l *defaultLogger) SetMinLevel(level LogLevel) {
|
||||
l.minLevel = level
|
||||
}
|
||||
|
||||
// Log 核心日志方法
|
||||
func (l *defaultLogger) Log(level LogLevel, format string, args ...any) {
|
||||
if level < l.minLevel {
|
||||
return // 低于最小级别,丢弃
|
||||
}
|
||||
|
||||
// 构建日志消息:[时间] [级别] 消息
|
||||
timestamp := time.Now().Format("2006-01-02 15:04:05")
|
||||
message := fmt.Sprintf(format, args...)
|
||||
line := fmt.Sprintf("[%s] [%s] %s\n", timestamp, level.String(), message)
|
||||
|
||||
// 写入目标(控制台或文件)
|
||||
fmt.Fprint(l.writer, line)
|
||||
}
|
||||
|
||||
// 快捷方法
|
||||
func (l *defaultLogger) Info(format string, args ...any) { l.Log(LevelInfo, format, args...) }
|
||||
func (l *defaultLogger) Warn(format string, args ...any) { l.Log(LevelWarn, format, args...) }
|
||||
func (l *defaultLogger) Error(format string, args ...any) { l.Log(LevelError, format, args...) }
|
||||
35
go基础语法/07go-mini-logger/main.go
Normal file
35
go基础语法/07go-mini-logger/main.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println("=== 1. 控制台日志 ===")
|
||||
consoleLogger := NewLogger()
|
||||
consoleLogger.Info("程序启动,PID: %d", os.Getpid())
|
||||
consoleLogger.Warn("磁盘使用率超过 80%")
|
||||
consoleLogger.Error("数据库连接失败: timeout")
|
||||
|
||||
fmt.Println("\n=== 2. 设置最低级别为 WARN ===")
|
||||
consoleLogger.SetMinLevel(LevelWarn)
|
||||
consoleLogger.Info("这条不会显示") // 被过滤
|
||||
consoleLogger.Warn("这条会显示") // 显示
|
||||
consoleLogger.Error("错误也会显示") // 显示
|
||||
|
||||
fmt.Println("\n=== 3. 文件日志(写入 app.log)===")
|
||||
fileLogger, err := NewFileLogger("app.log")
|
||||
if err != nil {
|
||||
consoleLogger.Error("无法创建文件日志: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fileLogger.Info("应用开始处理请求")
|
||||
fileLogger.Warn("用户 %s 尝试越权操作", "liumangmang")
|
||||
fileLogger.Error("写入数据库时发生唯一键冲突")
|
||||
|
||||
fmt.Println("日志已写入 app.log,请查看内容:")
|
||||
os.ReadFile("app.log") // 不打印,仅确保写入
|
||||
fmt.Println("✅ 查看 app.log 可验证文件日志内容")
|
||||
}
|
||||
Reference in New Issue
Block a user