[TOC]
示例1.编写代码统计出字符串"为 Hello 中国 World,Go 语言 学习"
中汉字的数量。
// 方式1.统计字符串中中文个数
s2 := "为 Hello 中国 World,Go 语言 学习"
res := []rune(s2)
reslen := len(res)
count := 0
for i := 0; i < reslen; i++ {
// UTF-8 由于中文或者其它语言占用3b~4b所以排除(0~255) 英文、符号以及特殊字符
if res[i] > 255 {
count++
}
}
fmt.Printf("字符串:%s (Length = %d),一共有 %d 个中文字符", s2, reslen, count)
// 方式2.利用unicode中提供的方法统计字符串中的个数
count = 0
for _, c := range s2 {
// 判断字符是否该字符集编码,来统计中文字符
if unicode.Is(unicode.Han, c) {
count++
}
}
fmt.Printf("字符串:%s (Length = %d),一共有 %d 个中文字符", s2, reslen, count)
执行结果:
字符串:为 Hello 中国 World,Go 语言 学习 (Length = 25),一共有 7 个中文字符
示例1.有一堆数字,如果除了一个数字以外,其他数字都出现了两次,那么如何找到出现一次的数字?
// 查看出现一次的数字
func showsinglenumber() {
numbers := 1234945579785321
snumbers := fmt.Sprintf("%d", numbers)
slength := len(snumbers)
for i := 0; i < slength; i++ {
// 判断其索引位置
if strings.Index(snumbers, string(snumbers[i])) == strings.LastIndex(snumbers, string(snumbers[i])) {
fmt.Println("只出现一次的数字 : ", string(snumbers[i])) // 注意输出时需要强制转换否则为byte类型输出Ascii值
}
}
}
执行结果:
只出现一次的数字 : 8
示例2.用Go语言编写一个九九乘法表
// 九九乘法表
func MultiTable() {
for i := 1; i <= 9; i++ {
for j := 1; j <= i; j++ {
fmt.Printf("%d * %d = %d, ", i, j, i*j)
}
fmt.Println()
}
}
WeiyiGeek.GO九九乘法表
示例1:求数组[1, 3, 5, 7, 8]
所有元素的和
func homework1() {
// 求数组`[1, 3, 5, 7, 8]`所有元素的和
arr := [...]int{1, 3, 5, 7, 8}
sum := 0
for _, v := range arr {
sum += v
}
fmt.Printf("%v 元素集合之和 : %d", arr, sum)
}
执行结果:
[1 3 5 7 8] 元素集合之和 : 24
示例2.找出数组中和为指定值的两个元素的下标,比如从数组[1, 3, 5, 7, 8]
中找出和为8的两个元素的下标分别为(0,3)
和(1,2)
。
func homework2() {
// 比如从数组`[1, 3, 5, 7, 8]`中找出和为8的两个元素的下标分别为`(0,3)`和`(1,2)`。**
arr := [...]int{1, 3, 5, 7, 8}
for i, v := range arr {
for j := i + 1; j < len(arr); j++ {
if v+arr[j] == 8 {
fmt.Printf("arr[%d] + arr[%d] = %d \n", i, j, 8)
}
}
}
}
执行结果:
arr[0] + arr[3] = 8
arr[1] + arr[2] = 8
示例3.请使用内置的sort包对数组var a = [...]int{3, 7, 8, 9, 1}
进行排序(附加题,自行查资料解答)。
# 把数组变成切片
func textsort() {
a := [...]int{3, 7, 8, 9, 1}
fmt.Printf("a : %T , %v , ptr : %p \n", a, a, &a) // 数组
sort.Ints(a[:]) // 排序
fmt.Printf("a[:] : %T , %v , ptr : %p \n", a[:], a[:], a[:]) // 切片
fmt.Println("After sorted: ", a)
}
a : [5]int , [3 7 8 9 1] , ptr : 0xc0000c6000
a[:] : []int , [1 3 7 8 9] , ptr : 0xc0000c6000
After sorted: [1 3 7 8 9]
示例1.请写出下面代码的输出结果
func text() {
var a = make([]string, 5, 10)
for i := 0; i < 10; i++ {
a = append(a, fmt.Sprintf("%v", i)) // 扩容 + 10 ,容量 + 10
}
fmt.Println(len(a), cap(a), a)
}
实际执行结果:
15 20 [0 1 2 3 4 5 6 7 8 9]
示例1.观察下面代码,写出最终的打印结果。
func main() {
type Map map[string][]int
m := make(Map)
s := []int{1, 2}
s = append(s, 3)
fmt.Printf("%+v\n", s) // [ 1,2,3 ]
m["s"] = s
s = append(s[:1], s[2:]...) // 关键点
fmt.Printf("%+v\n", s) // [ 1,3 ]
fmt.Printf("%+v\n", m["s"]) // [ 1,3,3 ]
}
示例2.写一个程序,统计一个字符串中每个单词出现的次数。比如:”how do you do”中how=1 do=2 you=1。
func homework2() {
var a = "how do you do"
var splitA = strings.Split(a, " ")
var count = make(map[string]int, len(splitA))
// 方式1
for _, v := range splitA {
value, ok := count[v]
if !ok {
value = 1
} else {
value++
}
count[v] = value
}
fmt.Printf("统计 %v 字符串每个单词出现的结果: %+v \n", a, count)
// 方式2 (非常值得学习)
for _, w := range splitA {
if _, ok := count[w]; !ok {
count[w] = 1
} else {
count[w]++
}
}
fmt.Printf("统计 %v 字符串每个单词出现的结果: %+v \n", a, count)
}
执行结果:
统计 how do you do 字符串每个单词出现的结果: map[do:2 how:1 you:1]
示例3.判断中文字符串的回文如,一行白鹭与鹭白行一
func homework1() {
s := "a一行白鹭与鹭白行一a"
srune := []rune(s)
fmt.Printf("%T, Len : %d , Cap : %d \n", srune, len(srune), cap(srune))
for i := range srune {
if srune[i] != srune[len(srune)-1-i] {
fmt.Printf("`%v`不是回文字符串", s)
return
} else if i >= len(srune)/2 {
fmt.Printf("`%v`是回文字符串", s)
return
}
}
}
执行结果:
[]int32, Len : 11 , Cap : 12
`a一行白鹭与鹭白行一a`是回文字符串
示例1.分金币
/*
你有50枚金币,需要分配给以下几个人:Matthew,Sarah,Augustus,Heidi,Emilie,Peter,Giana,Adriano,Aaron,Elizabeth。
分配规则如下:
a. 名字中每包含1个'e'或'E'分1枚金币
b. 名字中每包含1个'i'或'I'分2枚金币
c. 名字中每包含1个'o'或'O'分3枚金币
d: 名字中每包含1个'u'或'U'分4枚金币
写一个程序,计算每个用户分到多少金币,以及最后剩余多少金币?
程序结构如下,请实现 ‘dispatchCoin’ 函数
*/
var (
coins = 50
users = []string{
"Matthew", "Sarah", "Augustus", "Heidi", "Emilie", "Peter", "Giana", "Adriano", "Aaron", "Elizabeth",
}
distribution = make(map[string]int, len(users))
)
func dispatchCoin() int {
// 1.遍历用户
for _, name := range users {
// 2.判断用户时候是否在 map 中
_, ok := distribution[name]
if ok {
continue
}
// 3.利用匿名函数求出每个人的获得金币数
temp := func() int {
count := 0
for _, crune := range name {
// 方式1
// c := string(crune)
// if c == "e" || c == "E" {
// count += 1
// } else if c == "i" || c == "I" {
// count += 2
// } else if c == "o" || c == "O" {
// count += 3
// } else if c == "u" || c == "U" {
// count += 4
// }
// 方式2(推荐)
switch crune {
case 'e', 'E':
count += 1
coins -= 1
case 'i', 'I':
count += 2
coins -= 2
case 'o', 'O':
count += 3
coins -= 3
case 'u', 'U':
count += 4
coins -= 4
}
}
return count
}()
// 4.将人员和金币数进行绑定
distribution[name] = temp
}
// 5.输出所有人员占有的金币数
fmt.Println(distribution)
// 6.返回剩余金币数量
return coins
}
func homework() {
left := dispatchCoin()
fmt.Println("剩下的金币数:", left)
}
执行结果:
map[Aaron:3 Adriano:5 Augustus:12 Elizabeth:4 Emilie:6 Giana:2 Heidi:5 Matthew:1 Peter:2 Sarah:0]
剩下的金币数: 10
示例2.有n个台阶,一次可以走一步或者两步,问一共有多少种走法
func ladder(step uint64) (ret uint64) {
// 如果只有一步阶梯则只有一种走法。
if step == 1 {
return 1
}
// 如果有两步阶梯则有两种走法。
if step == 2 {
return 2
}
// 如有三步则有3种走法,所以 n 步 = (n - 1) + (n -2)
return ladder(step-1) + ladder(step-2)
}
func demo2() {
ret := ladder(6)
fmt.Println("当有六步阶梯时,有", ret, "种走法!")
}
执行结果:
当有六步阶梯时,有 13 种走法!
示例1.请问下面代码的执行结果是什么?
type student struct {
name string
age int
}
func homework1() {
m := make(map[string]*student)
// 后进先出
stus := []student{
{name: "小王", age: 18},
{name: "娜扎", age: 23},
{name: "大王", age: 900}, // 初始指针指向的位置
}
fmt.Printf("stus : %p \n", &stus)
for _, stu := range stus {
m[stu.name] = &stu
fmt.Printf("for stu :%p, value : %v, Size: %d\n", &stu, stu, unsafe.Sizeof(stu.age))
}
for k, v := range m {
fmt.Println(k, "=>", v.name)
}
}
执行结果:
stus : 0xc00000c030
for stu :0xc00000c048, value : {小王 18}, Size: 8
for stu :0xc00000c048, value : {娜扎 23}, Size: 8
for stu :0xc00000c048, value : {大王 900}, Size: 8
小王 => 大王
娜扎 => 大王
大王 => 大王
示例2.利用函数Funtion实现考生信息系统
package main
import (
"fmt"
"os"
)
/** Desc: 采用函数实现考生信息的增加与删除**/
var (
// options
flag uint8
// key : value
allStudent map[int64]*Student
)
// 结构体
type Student struct {
id int64
name string
age uint8
}
// 构造函数
func newStudent(id int64, name string, age uint8) *Student {
return &Student{
id: id,
name: name,
age: age,
}
}
// 验证考生是否存在
func judgeStudent(id int64) bool {
_, ok := allStudent[id]
if ok {
return true
} else {
return false
}
}
// 添加考生
func addStudent() {
var (
id int64
name string
age uint8
)
fmt.Println("#请输入考生 id name age 信息以空格分割:")
fmt.Scan(&id, &name, &age)
if !judgeStudent(id) {
student := newStudent(id, name, age)
allStudent[id] = student
} else {
fmt.Println("该id号的考生已存在无需重复添加.")
}
}
// 删除考生
func delStuednt(id int64) {
if judgeStudent(id) {
delete(allStudent, id)
fmt.Printf("已删除考生号为 %d 的学生信息.\n", id)
} else {
fmt.Println("该id号的考生不存在.")
}
}
// 显示考生
func showStudent() {
for k, v := range allStudent {
fmt.Printf("uid: %d 姓名: %s 年龄: %d\n", k, v.name, v.age)
}
}
func main() {
// Step 0.初始化存放考生信息的Map
allStudent = make(map[int64]*Student)
fmt.Printf("allStudent len %v", len(allStudent))
// Step 1
fmt.Println("欢迎使用学生信息系统简单版本")
fmt.Printf("功能说明: \n1.增加学生\n2.查看学生\n3.删除学生\n4.退出程序\n\n")
// Step 2
for {
fmt.Printf("功能选择: ")
fmt.Scanln(&flag)
fmt.Printf("#你选择第 %d 个的选项\n", flag)
// step 3
switch flag {
case 1:
addStudent()
case 2:
showStudent()
case 3:
fmt.Printf("请输入要删除的学生 uid:")
var uid int64
fmt.Scanln(&uid)
delStuednt(uid)
case 4:
fmt.Printf("程序结束 :")
os.Exit(1)
default:
fmt.Printf("功能说明: \n1.增加学生\n2.查看学生\n3.删除学生\n4.退出程序")
}
}
}
执行结果:
欢迎使用学生信息系统简单版本
功能说明:
1.增加学生
2.查看学生
3.删除学生
4.退出程序
功能选择: 1
# 你选择第 1 个的选项
# 请输入考生 id name age 信息以空格分割:
10086 移动 25
功能选择: 1
# 你选择第 1 个的选项
# 请输入考生 id name age 信息以空格分割:
10010 联通 25
功能选择: 1
# 你选择第 1 个的选项
# 请输入考生 id name age 信息以空格分割:
10000 典电信 26
功能选择: 2
# 你选择第 2 个的选项
uid: 10086 姓名: 移动 年龄: 25
uid: 10010 姓名: 联通 年龄: 25
uid: 10000 姓名: 电信 年龄: 26
功能选择: 3
# 你选择第 3 个的选项
请输入要删除的学生 uid:10000
已删除考生号为 10000 的学生信息.
功能选择: 2
# 你选择第 2 个的选项
uid: 10086 姓名: 移动 年龄: 25
uid: 10010 姓名: 联通 年龄: 25
示例3.利用结构体方法进行实现考生信息管理系统
package main
import (
"fmt"
"os"
)
/** Desc: 采用结构体方法实现考生信息的增加与删除**/
var (
// options
flag uint8
// key : value
allStudent map[int64]*Student
)
// 结构体
type Student struct {
id int64
name string
age uint8
}
// 构造函数
func newStudent(id int64, name string, age uint8) *Student {
return &Student{
id: id,
name: name,
age: age,
}
}
// 验证考生是否存在
func (s *Student) judge(id int64) bool {
_, ok := allStudent[id]
if ok {
return true
} else {
return false
}
}
// 添加考生
func (s *Student) add() {
var (
id int64
name string
age uint8
)
fmt.Println("#请输入考生 id name age 信息以空格分割:")
fmt.Scan(&id, &name, &age)
if !s.judge(id) {
student := newStudent(id, name, age)
allStudent[id] = student
} else {
fmt.Println("该id号的考生已存在无需重复添加.")
}
}
// 删除考生
func (s *Student) del(id int64) {
if s.judge(id) {
delete(allStudent, id)
fmt.Printf("已删除考生号为 %d 的学生信息.\n", id)
} else {
fmt.Println("该id号的考生不存在.")
}
}
// 显示考生
func (s *Student) show() {
for k, v := range allStudent {
fmt.Printf("uid: %d 姓名: %s 年龄: %d\n", k, v.name, v.age)
}
}
func main() {
// Step 0.初始化存放考生信息的Map
allStudent = make(map[int64]*Student, 48)
student := Student{}
// Step 1
fmt.Println("欢迎使用学生信息系统简单版本")
fmt.Printf("功能说明: \n1.增加学生\n2.查看学生\n3.删除学生\n4.退出程序\n\n")
// Step 2
for {
fmt.Printf("功能选择: ")
fmt.Scanln(&flag)
fmt.Printf("#你选择第 %d 个的选项\n", flag)
// step 3
switch flag {
case 1:
student.add() // 结构体中调用add方法
case 2:
student.show() // 结构体中调用show方法
case 3:
fmt.Printf("请输入要删除的学生 uid:")
var uid int64
fmt.Scanln(&uid)
student.del(uid) // 结构体中调用del方法
case 4:
fmt.Printf("程序结束 :")
os.Exit(1)
default:
fmt.Printf("功能说明: \n1.增加学生\n2.查看学生\n3.删除学生\n4.退出程序")
}
}
}
执行结果:
功能选择: 1
#你选择第 1 个的选项
#请输入考生 id name age 信息以空格分割:
10086 移动 25
功能选择: 1
#你选择第 1 个的选项
#请输入考生 id name age 信息以空格分割:
10010 联通 24
功能选择: 2
#你选择第 2 个的选项
uid: 10086 姓名: 移动 年龄: 25
uid: 10010 姓名: 联通 年龄: 24
功能选择: 3
#你选择第 3 个的选项
请输入要删除的学生 uid:10010
已删除考生号为 10010 的学生信息.
功能选择: 2
#你选择第 2 个的选项
uid: 10086 姓名: 移动 年龄: 25
功能选择: 4
#你选择第 4 个的选项
程序结束 :exit status 1
作业4.反转一个单链表
示例说明: 1->2->3->4->5->NULL
, 5->4->3->2->1->NULL
代码示例:
package main
import "fmt"
// 链表结构体
type ListNode struct {
val int
Next *ListNode
}
// 反转链表函数
func reverseList(head *ListNode) *ListNode {
var pre *ListNode
cur := head
for cur != nil {
tmp := cur.Next // 把下一个节点存放到临时变量中
cur.Next = pre // 首节点赋予下一指向节点(首次Nil)
pre = cur // 将当前节点(首次val为1的地址)赋予pre
cur = tmp // 将上面临时变量地址(首次val为2的地址)赋予cur
}
return pre // 返回反转链表
}
func main() {
// 1.实例化链表
head := &ListNode{
val: 1,
Next: &ListNode{
val: 2,
Next: &ListNode{
val: 3,
Next: &ListNode{
val: 4,
Next: &ListNode{
val: 5,
Next: nil,
},
},
},
},
}
// 链表实例化对象副本拷贝
header := head
fmt.Println("反转前首地址:", header)
for header != nil {
fmt.Printf("%v->", header.val)
header = header.Next
if header == nil {
fmt.Printf("Nil\n")
}
}
// 2.调用反转链表
res := reverseList(head)
fmt.Println("反转后首地址:", res)
for res != nil {
fmt.Printf("%v->", res.val)
res = res.Next
if res == nil {
fmt.Printf("Nil\n")
}
}
}
执行结果:
反转前首地址: &{1 0xc000046240}
1->2->3->4->5->Nil
反转后首地址: &{5 0xc000046260}
5->4->3->2->1->Nil
作业1.利用接口既可以求三角形的面积也能求正方形的面积
package main
import (
"fmt"
"math"
)
type GarphicArea interface {
Square(float64) float64 // 正方形
Triangle(float64, float64, float64) float64 //三角形
}
type GarphicName struct{ Name string }
func (g *GarphicName) Square(a float64) float64 {
// S = a^2
return math.Pow(a, 2)
}
func (g *GarphicName) Triangle(a, b, c float64) float64 {
//(海伦公式)(p=(a+b+c)/2) S=sqrt[p(p-a)(p-b)(p-c)]
p := (a + b + c) / 2
return math.Sqrt(p * (p - a) * (p - b) * (p - c))
}
func main() {
var ga GarphicArea = &GarphicName{"正方形"}
fmt.Println("正方形面积: ", ga.Square(4))
ga = &GarphicName{"三角形"}
fmt.Println("三角形面积: ", ga.Triangle(3, 4, 5))
}
执行结果:
正方形面积: 16
三角形面积: 6
作业1.利用包来封装调用位移运算符进行左移或者右移多少位,并输出其二进制表示的结果
// # weiyigeek.top/custom/pkg/bitmove.go
// # packeageName bitmove
package bitmove
import "fmt"
func Leftmove(n int, shift int) {
fmt.Printf("%v << %v = %08b\n", n, shift, n<<shift)
}
func RightMove(n int, shift int) {
fmt.Printf("%v >> %v = %08b\n", n, shift, n>>shift)
}
// # 主入口文件我们可使用import来导入我们自己编写的包(注意包中属性、函数、结构体、接口需要首字母大小才能被外部引用)
// # weiyigeek.top/studygo/homework/2.packeage/displacement.go
package main
import (
"fmt"
bitmove "weiyigeek.top/custom/pkg"
)
func main() {
bitmove.Leftmove(4, 2)
bitmove.RightMove(10, 2)
}
执行结果:
4 << 2 = 00010000
10 >> 2 = 00000010
作业1.获取当前时间,格式化输出为2006/01/02 15:04:05`格式。
fmt.Println(“当前时间: “, time.Now().Format(“2006/01/02 15:04:05”)) 当前时间: 2021/09/27 15:02:31
作业2.编写程序统计一段代码的执行耗时时间,单位精确到微秒。
func demo2() {
// 开始时间
startTime := time.Now()
// 从1+...+1000之和
sum := 0
for i := 0; i <= 1000; i++ {
sum += i
}
fmt.Println("从1+...+1000之和为", sum)
// 结束时间
endTime := time.Now()
fmt.Println("该段代码耗时时间为:", endTime.Sub(startTime))
}
// ==== 执行结果 ====
从1+...+1000之和为 500500
该段代码耗时时间为: 6.765µs
日志库 程序需求分析:
stdout
。Debug/Trace/Info/Warning/Error/Falat
项目代码演示:
package mlogger
import (
"errors"
"fmt"
"path"
"runtime"
"strings"
"time"
)
// 1.自定义日志库等级
type Loglevel uint8
const (
UNKNOWN Loglevel = iota
DEBUG
TRACE
INFO
WARNING
ERROR
FATAL
)
// 2.解析日志等级方法
func parseLogLevel(s string) (Loglevel, error) {
s = strings.ToUpper(s)
switch s {
case "DEBUG":
return DEBUG, nil
case "TRACE":
return TRACE, nil
case "INFO":
return INFO, nil
case "WARNING":
return WARNING, nil
case "ERROR":
return ERROR, nil
case "FATAL":
return FATAL, nil
default:
// 主意不能以标点符号来结束错误信息抛出
err := errors.New("无效的日志级别-Log Levels Error")
return UNKNOWN, err
}
}
// 3.ConsoleLogger结构体
type ConsoleLogger struct {
level Loglevel
}
// 4.ConsoleLogger结构体构造方法
func NewConsoleLog(level string) ConsoleLogger {
logLevel, err := parseLogLevel(level)
if err != nil {
fmt.Print(err)
}
// 返回ConsoleLogger结构体对象
return ConsoleLogger{
level: logLevel,
}
}
// 5.解析日志等级字符串名称
func getLevelName(lv Loglevel) string {
switch lv {
case DEBUG:
return "DEBUG"
case TRACE:
return "TRACE"
case INFO:
return "INFO"
case WARNING:
return "WARNING"
case ERROR:
return "ERROR"
case FATAL:
return "FATAL"
default:
return "UNKNOWN"
}
}
// 6.获取错误的函数名 文件 以及 行号的方法
func getFileLine(skip int) (funcName, fileName string, lineNum int) {
pc, fileName, lineNum, ok := runtime.Caller(skip)
if !ok {
fmt.Printf("Runtime,Caller() failed\n")
}
funcName = runtime.FuncForPC(pc).Name()
fileName = path.Base(fileName)
funcName = strings.Split(funcName, ".")[1]
return
}
// 7.日志显示的等级(当前日志级别大传入的日志级别则显示)
func (l ConsoleLogger) enable(Loglevel Loglevel) bool {
return Loglevel >= l.level
}
// 8.向终端打印日志的方法
func printLogs(lv, format string, custom ...interface{}) {
// 错误信息拼接
msg := fmt.Sprintf(format, custom...)
// 时间&时区
now := time.Now()
//timezone, _ := time.LoadLocation("Asia/Shanghai")
// 错误文件以及函数名称
funcName, fileName, linwNum := getFileLine(2)
fmt.Printf("[%s] [%s] [%s:%s:%d] %s \n", now.Format("2006-01-02 15:04:05"), lv, fileName, funcName, linwNum, msg)
}
// 9.各日志等级打印
// Debug
func (l ConsoleLogger) Debug(format string, custom ...interface{}) {
// 满足等级则打印日志
if l.enable(DEBUG) {
printLogs("DEBUG", format, custom...)
}
}
// Trace
func (l ConsoleLogger) Trace(format string, custom ...interface{}) {
// 满足等级则打印日志
if l.enable(TRACE) {
printLogs("TRACE", format, custom...)
}
}
// Info
func (l ConsoleLogger) Info(format string, custom ...interface{}) {
// 满足等级则打印日志
if l.enable(INFO) {
printLogs("INFO", format, custom...)
}
}
// Error
func (l ConsoleLogger) Error(format string, custom ...interface{}) {
// 满足等级则打印日志
if l.enable(ERROR) {
printLogs("ERROR", format, custom...)
}
}
// Warning
func (l ConsoleLogger) Warning(format string, custom ...interface{}) {
// 满足等级则打印日志
if l.enable(WARNING) {
printLogs("WARNING", format, custom...)
}
}
// Falat
func (l ConsoleLogger) Falat(format string, custom ...interface{}) {
// 满足等级则打印日志
if l.enable(FATAL) {
printLogs("FATAL", format, custom...)
}
}
// 方法接口定义
type Logger interface {
Debug(format string, custom ...interface{})
Trace(format string, custom ...interface{})
Info(format string, custom ...interface{})
Warning(format string, custom ...interface{})
Error(format string, custom ...interface{})
Falat(format string, custom ...interface{})
}
(2) weiyigeek.top/custom/mlogger/filelogger.go
package mlogger
// 主要向文件里写入日志相关信息
import (
"fmt"
"os"
"path"
"time"
)
// 1.文件记录日志结构体
type FileLogger struct {
level Loglevel
filePath string
fileName string
fileObj *os.File
errFileObj *os.File
maxFileSize int64
}
// 2.文件日志构造方法
func NewFileLogger(levelStr, fp, fn string, maxSize int64) *FileLogger {
// 解析日志等级
Loglevel, err := parseLogLevel(levelStr)
if err != nil {
panic(err)
}
// 文件日志结构体对象
fobj := &FileLogger{
level: Loglevel,
filePath: fp,
fileName: fn,
maxFileSize: maxSize,
}
// 初始化文件日志文件句柄
initErr := fobj.init()
if initErr != nil {
panic(initErr)
}
// 返回对象
return fobj
}
// 3.文件日志显示的等级控制方法(当前日志级别大传入的日志级别则显示)
func (f FileLogger) enable(Loglevel Loglevel) bool {
return Loglevel >= f.level
}
// 4.初始化文件日志文件句柄方法
func (f *FileLogger) init() error {
// 通用日志名称与错误日志名称路径拼接
fullName := path.Join(f.filePath, f.fileName) + ".logs"
fullErrName := path.Join(f.filePath, f.fileName) + ".err.logs"
// 创建文件句柄并
fileObj, err := os.OpenFile(fullName, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
fmt.Printf("Open Logs file failed, err : %v \n", err)
return err
}
errFileObj, err := os.OpenFile(fullErrName, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
fmt.Printf("Open Error logs file failed, err : %v \n", err)
return err
}
// 赋值其对象
f.fileObj = fileObj
f.errFileObj = errFileObj
return nil
}
// 5.验证文件的大小方法
func (f *FileLogger) checkFileSize(file *os.File) bool {
// 文件信息返回的是结构体
fileInfo, err := file.Stat()
if err != nil {
fmt.Printf("checkFileSize - Get file info failed, err : %v \n", err)
return false
}
// 如果当前文件的大小值大于返回True,则进行切割
if fileInfo.Size() > f.maxFileSize {
return true
}
return false
}
// 6.进行日志文件的切割方法
func (f *FileLogger) SplitFile(file *os.File) (*os.File, error) {
// 1) 获取文件句柄信息并关闭当前日志文件
fileInfo, err := file.Stat()
if err != nil {
fmt.Printf("SplitFile - Get file info failed, err : %v \n", err)
return nil, err
}
nowStr := time.Now().Format("2006-01-02_150405")
LogName := path.Join(f.filePath, fileInfo.Name())
newLogName := fmt.Sprintf("%s.bak.%s", LogName, nowStr)
file.Close()
// 2) 备份并重命名日志文件
os.Rename(LogName, newLogName)
// 3) 重新打开一个新的日志文件
fileObj, err := os.OpenFile(LogName, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
fmt.Printf("Open New Logs file failed, err : %v \n", err)
return nil, err
}
// 4) 将对象进行返回
return fileObj, nil
}
// 7.将日志分别打印进对应的文件之中
func (f *FileLogger) printLogs(level Loglevel, format string, custom ...interface{}) error {
// 1) 判断日志等级是否满足指定等级之上的要求
if f.enable(level) {
// 2) 异常信息
msg := fmt.Sprintf(format, custom...)
// 3) 时间与时区
now := time.Now()
//timezone, _ := time.LoadLocation("Asia/Shanghai")
// 4) 异常文件函数信息
funcName, fileName, linwNum := getFileLine(2)
// 5) 通用日志文件切割判断及输入
if f.checkFileSize(f.fileObj) {
f.fileObj, _ = f.SplitFile(f.fileObj)
}
fmt.Fprintf(f.fileObj, "[%s] [%s] [%s:%s:%d] %s \n", now.Format("2006-01-02 15:04:05"), getLevelName(level), fileName, funcName, linwNum, msg)
// 6) 严重与错误日志文件切割判断及输入
if level >= ERROR {
if f.checkFileSize(f.errFileObj) {
f.errFileObj, _ = f.SplitFile(f.errFileObj)
}
fmt.Fprintf(f.errFileObj, "[%s] [%s] [%s:%s:%d] %s \n", now.Format("2006-01-02 15:04:05"), getLevelName(level), fileName, funcName, linwNum, msg)
}
}
return nil
}
// 8.日志等级调用分别方法
// Debug
func (f *FileLogger) Debug(format string, custom ...interface{}) {
f.printLogs(DEBUG, format, custom...)
}
// Trace
func (f *FileLogger) Trace(format string, custom ...interface{}) {
f.printLogs(TRACE, format, custom...)
}
// Info
func (f *FileLogger) Info(format string, custom ...interface{}) {
f.printLogs(INFO, format, custom...)
}
// Error
func (f *FileLogger) Error(format string, custom ...interface{}) {
f.printLogs(ERROR, format, custom...)
}
// Warning
func (f *FileLogger) Warning(format string, custom ...interface{}) {
f.printLogs(WARNING, format, custom...)
}
// Falat
func (f *FileLogger) Falat(format string, custom ...interface{}) {
f.printLogs(FATAL, format, custom...)
}
func (f *FileLogger) Close() {
f.fileObj.Close()
f.errFileObj.Close()
}
(3) main.go
/**自定义日志库调用演示入口**/
package main
import (
"time"
"weiyigeek.top/custom/mlogger"
)
// 示例1.向终端中进行日志信息的打印
func demo1() {
log := mlogger.NewConsoleLog("Debug")
for {
log.Debug("Debug 日志: %s", "Deveploment Debug")
log.Trace("Trace 日志: %s", "Deveploment Trace")
log.Info("Info 日志: %s,%d", "Production Info", 1024)
log.Warning("Warning 日志: %s", "Production Warning")
log.Error("Error 日志: %s", "Production Error")
log.Falat("Falat 日志: %s", "Production Falat")
time.Sleep(time.Second * 1)
}
}
// 示例2.将日志信息输入到文件之中
func demo2() {
log := mlogger.NewFileLogger("INFO", "./", "weiyigeek", 5*1024)
for {
log.Debug("Debug 日志: %s", "FileLogger - 开发环境日志基级别 Debug")
log.Trace("Trace 日志: %s", "FileLogger - 开发环境日志基级别 Trace")
log.Info("Info 日志: %s,%d", "FileLogger - 生产环境日志基级别 Info", 1024)
log.Warning("Warning 日志: %s", "FileLogger - 生产环境日志基级别 Warning")
log.Error("Error 日志: %s", "FileLogger - 生产环境日志基级别 Error")
log.Falat("Falat 日志: %s", "FileLogger - 生产环境日志基级别 Falat")
time.Sleep(time.Second * 1)
}
}
func main() {
demo1()
demo2()
}
运行结果:
WeiyiGeek.Logger两种实现方式