当前位置: 首页 > news >正文

Go-gin-example 第三部分 编写一个简单的文件日志系统

文章目录

  • 本文目标
  • 新建`logging`包
    • file.go
    • 编写log文件
    • 当前目录结构
  • 接入自定义的log功能
  • 验证功能

本文目标

在上一节中,我们解决了 API’s 可以任意访问的问题,那么我们现在还有一个问题,就是我们的日志,都是输出到控制台上的,这显然对于一个项目来说是不合理的,因此我们这一节简单封装log库,使其支持简单的文件日志!

新建logging

我们在pkg下新建logging目录,用来包含自定义logger相关的文件,新建file.golog.go文件

file.go

用来创建记录log的相关目录和文件,其内容为:

package loggingimport ("fmt""log""os""time"
)// 适用枚举,将所有固定的量提前列出在这里,方便后期维护	我们这里将原来的var修改为const
const (LogSavePath = "runtime/logs/"LogSaveName = "log"LogFileExt  = "log"TimeFormat  = "20060102"
)// 返回log文件的前缀路径,算是一个具有仪式感的函数
func getLogFilePath() string {return fmt.Sprintf("%s", LogSavePath)
}// 获得log文件的整体路径,以当前日期作为.log文件的名字
func getLogFileFullPath() string {prefixPath := getLogFilePath()suffixPath := fmt.Sprintf("%s%s.%s", LogSaveName, time.Now().Format(TimeFormat), LogFileExt)return fmt.Sprintf("%s%s", prefixPath, suffixPath)
}// 打开日志文件,返回写入的句柄handle
func openLogFile(filePath string) *os.File {//根据文件目录是否存在进行判断_, err := os.Stat(filePath)switch {//目录不存在case os.IsNotExist(err):mkDir()//权限不够case os.IsPermission(err):log.Fatalf("Permission :%v", err)}//如果.log文件不存在,这里会创建一个handle, err := os.OpenFile(filePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)if err != nil {log.Fatalf("Fail to OpenFile :%v", err)}return handle
}// 创建log目录
func mkDir() {//获得当前目录 dir: /home/wang2/gin-exampledir, _ := os.Getwd()//适用MKdirAll会直接创建所有依赖的父目录,减少报错的可能性err := os.MkdirAll(dir+"/"+getLogFilePath(), os.ModePerm)if err != nil {panic(err)}
}
  • os.Stat:返回文件信息结构描述文件。如果出现错误,会返回*PathError
    type PathError struct {Op   stringPath stringErr  error
    }
    
  • os.IsNotExist:能够接受ErrNotExistsyscall的一些错误,它会返回一个布尔值,能够得知文件不存在或目录不存在
  • os.IsPermission:能够接受ErrPermissionsyscall的一些错误,它会返回一个布尔值,能够得知权限是否满足
  • os.OpenFile:调用文件,支持传入文件名称、指定的模式调用文件、文件权限,返回的文件的方法可以用于 I/O。如果出现错误,则为*PathError
    const (// Exactly one of O_RDONLY, O_WRONLY, or O_RDWR must be specified.O_RDONLY int = syscall.O_RDONLY // 以只读模式打开文件O_WRONLY int = syscall.O_WRONLY // 以只写模式打开文件O_RDWR   int = syscall.O_RDWR   // 以读写模式打开文件// The remaining values may be or'ed in to control behavior.O_APPEND int = syscall.O_APPEND // 在写入时将数据追加到文件中O_CREATE int = syscall.O_CREAT  // 如果不存在,则创建一个新文件O_EXCL   int = syscall.O_EXCL   // 使用O_CREATE时,文件必须不存在O_SYNC   int = syscall.O_SYNC   // 同步IOO_TRUNC  int = syscall.O_TRUNC  // 如果可以,打开时
    )
    
  • os.Getwd:返回与当前目录对应的根路径名
  • os.MkdirAll:创建对应的目录以及所需的子目录,若成功则返回nil,否则返回error
  • os.ModePermconst定义ModePerm FileMode = 0777

文件权限的知识补充:err := os.MkdirAll(“wang2/”+path, os.ModePerm)
这里的modePerm中Perm为permission的缩写
在Linux和Unix操作系统中,文件和目录的权限通常用八进制数表示,例如 0777。这个八进制数表示了文件或目录的权限位,以及哪些用户或用户组有权访问它。在 0777 中,每一位都有特定的含义:

  • 最高位(左边的0)表示特殊权限位,通常不使用,所以它通 常是0。
  • 下一个三位(中间的777)表示用户(文件的所有者)的权限。
  • 接下来的三位表示组(文件的所属组)的权限。
  • 最后三位表示其他人(不是文件所有者或所属组的用户)的权限。

每个三位权限位由三个位组成,它们可以是以下之一:

  • 4:读权限(R):用户可以读取文件或列出目录中的内容。
  • 2:写权限(W):用户可以编辑或写入文件,对于目录来说,用户可以在其中创建、删除或重命名文件。
  • 1:执行权限(X):用户可以执行文件或进入目录。

编写log文件

log.go:

package loggingimport ("fmt""log""os""path/filepath""runtime"
)// 类型声明,基于int建立level,方便后续维护
type Level intvar (//传入写log文件的句柄F *os.File//默认的前缀DefaultPrefix = ""//这里的定义在后续的caller中被调用,该参数指定了要跳过的调用堆栈帧数,每个调用堆栈帧代表了代码中的一个函数调用。//以info写入log为例,我们这里调用caller的函数为setPrefix,Info调用setPrefix,适用Info进行写入的函数调用info。//                                     0                      1                    2//而我们想要得到的信息就是调用info的函数的信息。所以我们这里设置的跳过调用堆栈帧数为2DefaultCallerDepth = 2//提前定义logger记录器,方便维护阅读logger *log.LoggerlogPrefix = ""//结合level的定义,方便读取维护levelFlags = []string{"DEBUG", "INFO", "WARN", "ERROR", "FATAL"}
)// 实现枚举
const (DEBUG Level = iotaINFOWARNINGERRORFATAL
)// 自定义logger的初始化
func init() {//获取log文件目录filePath := getLogFileFullPath()//得到log文件句柄F = openLogFile(filePath)//创建一个新的日志记录器logger = log.New(F, DefaultPrefix, log.LstdFlags)
}func Debug(v ...interface{}) {setPrefix(DEBUG)logger.Println(v)
}// 这里先设置每条log的前缀部分,首先为log模式,这里为info;然后为具体到某个函数第几行出错;接下来为时间;最后为日志信息
func Info(v ...interface{}) {setPrefix(INFO)logger.Println(v)
}func Warn(v ...interface{}) {setPrefix(WARNING)logger.Println(v)
}func Error(v ...interface{}) {setPrefix(ERROR)logger.Println(v)
}func Fatal(v ...interface{}) {setPrefix(FATAL)logger.Fatalln(v)
}// 从进程中读取当前运行的函数信息
func setPrefix(level Level) {//获取文件名,具体行数,是否读取成功_, file, line, ok := runtime.Caller(DefaultCallerDepth)if ok { //获取成功logPrefix = fmt.Sprintf("[%s][%s:%d]", levelFlags[level], filepath.Base(file), line)} else { //获取失败,则前缀不加如具体的文件名和行号logPrefix = fmt.Sprintf("[%s]", levelFlags[level])}//将前缀写入log文件logger.SetPrefix(logPrefix)
}
  • log.New:创建一个新的日志记录器。out定义要写入日志数据的IO句柄。prefix定义每个生成的日志行的开头。flag定义了日志记录属性
    func New(out io.Writer, prefix string, flag int) *Logger {return &Logger{out: out, prefix: prefix, flag: flag}
    }
    
  • log.LstdFlags:日志记录的格式属性之一,其余的选项如下
    const (Ldate         = 1 << iota     // the date in the local time zone: 2009/01/23Ltime                         // the time in the local time zone: 01:23:23Lmicroseconds                 // microsecond resolution: 01:23:23.123123.  assumes Ltime.Llongfile                     // full file name and line number: /a/b/c/d.go:23Lshortfile                    // final file name element and line number: d.go:23. overrides LlongfileLUTC                          // if Ldate or Ltime is set, use UTC rather than the local time zoneLstdFlags     = Ldate | Ltime // initial values for the standard logger
    )
    

当前目录结构

gin-blog/
├── conf
│   └── app.ini
├── main.go
├── middleware
│   └── jwt
│       └── jwt.go
├── models
│   ├── article.go
│   ├── auth.go
│   ├── models.go
│   └── tag.go
├── pkg
│   ├── e
│   │   ├── code.go
│   │   └── msg.go
│   ├── logging
│   │   ├── file.go
│   │   └── log.go
│   ├── setting
│   │   └── setting.go
│   └── util
│       ├── jwt.go
│       └── pagination.go
├── routers
│   ├── api
│   │   ├── auth.go
│   │   └── v1
│   │       ├── article.go
│   │       └── tag.go
│   └── router.go
├── runtime

接入自定义的log功能

我们自定义的logging包,已经基本完成了,接下来让它接入到我们的项目之中吧。我们打开先前包含log包的代码,如下:

  1. 打开routers目录下的article.gotag.goauth.go
    2。 将log包的引用删除,修改引用我们自己的日志包为github.com/kingsill/gin-example/pkg/logging
  2. 将原本的log.Println(...)改为logging.Info(...)

验证功能

修改文件后,重启服务,我们来试试吧!

获取到 APIToken 后,我们故意传错误 URL 参数给接口,如:http://127.0.0.1:8000/api/v1/articles?tag_id=0&state=9999999&token=eyJhbG..

然后我们到$GOPATH/gin-blog/runtime/logs查看日志:

$ tail -f log20180216.log
[INFO][article.go:79]2018/02/16 18:33:12 [state 状态只允许0或1]
[INFO][article.go:79]2018/02/16 18:33:42 [state 状态只允许0或1]
[INFO][article.go:79]2018/02/16 18:33:42 [tag_id 标签ID必须大于0]
[INFO][article.go:79]2018/02/16 18:38:39 [state 状态只允许0或1]
[INFO][article.go:79]2018/02/16 18:38:39 [tag_id 标签ID必须大于0]

日志结构一切正常,我们的记录模式都为Info,因此前缀是对的,并且我们是入参有问题,也把错误记录下来了,这样排错就很方便了!

至此,本节就完成了,这只是一个简单的扩展,实际上我们线上项目要使用的文件日志,是更复杂一些,开动你的大脑 举一反三吧!

相关文章:

Go-gin-example 第三部分 编写一个简单的文件日志系统

文章目录 本文目标新建logging包file.go编写log文件当前目录结构 接入自定义的log功能验证功能 本文目标 在上一节中&#xff0c;我们解决了 API’s 可以任意访问的问题&#xff0c;那么我们现在还有一个问题&#xff0c;就是我们的日志&#xff0c;都是输出到控制台上的&…...

SQL中如何添加数据

SQL中如何添加数据 一、SQL中如何添加数据&#xff08;方法汇总&#xff09;二、SQL中如何添加数据&#xff08;方法详细解说&#xff09;1. 使用SQL脚本&#xff08;推荐&#xff09;1.1 在表中插入1.1.1 **第一种形式**1.1.2 **第二种形式**SQL INSERT INTO 语法示例SQL INSE…...

如何更好的理解设计模式之桥接模式

桥接模式 点奶茶的时候, 我们一般选择原味奶茶/牛奶奶茶/咸味奶茶 现在假设我们也有这些已经实现好的奶茶类, 且这个奶茶类仅仅就是一个类, 什么也没有, 不可改动 类似 class 奶茶{ }class 原味奶茶 extends 奶茶{ }但是奶茶也分大杯奶茶, 中杯奶茶, 小杯奶茶, 如果我们要实现…...

归并排序

参考链接 排序算法&#xff1a;归并排序【图解代码】_哔哩哔哩_bilibili #include <stdio.h> #include <stdlib.h>// 合并 void merge(int arr[], int tempArr[], int left, int mid, int right) {// 标记左半区第一个未排序的元素int l_pos left;// 标记右半区…...

ELF 1技术贴|在NXP源码基础上适配开发板的按键功能

本次源代码适配是在NXP i.MX6ULL EVK评估板的Linux内核源代码&#xff08;特定版本号为Linux-imx_4.1.15&#xff09;的基础中展开的。 首要任务集中在对功能接口引脚配置的精细调整&#xff0c;确保其能无缝匹配至ELF 1开发板。接下来&#xff0c;我们将详细阐述适配过程中关…...

Linux:kubernetes(k8s)pod的基础操作(6)

Linux&#xff1a;kubernetes&#xff08;k8s&#xff09;允许在任意节点使用kubectl命令&#xff08;5&#xff09;-CSDN博客https://blog.csdn.net/w14768855/article/details/136460090?spm1001.2014.3001.5501 我在前两张进行了基础环境的一系列搭建&#xff0c;现在就正…...

【Docker】掌握 Docker 镜像操作:从基础到进阶

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;Linux ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 正文 结语 我的其他博客 前言 在现代软件开发和部署中&#xff0c;容器化技术已经成为不可或缺的一部分。而 Docker 作为最流行的容器化…...

flink重温笔记(十一):Flink 高级 API 开发——flink 四大基石之 Checkpoint(详解存储后端)

Flink学习笔记 前言&#xff1a;今天是学习 flink 的第 11 天啦&#xff01;学习了 flink 四大基石之 Checkpoint &#xff08;检查点&#xff09;&#xff0c;主要是解决大数据领域持久化中间结果数据&#xff0c;以及取消任务&#xff0c;下次启动人可以恢复累加数据问题&…...

React富文本编辑器开发(九)位置

位置是指在使用Slate编辑器中进行插入、删除或执行其它操作时引用文档中特定位置的方式。有几种不同类型的位置接口&#xff0c;每种接口都用于不同的用例。 Path路径 路径是引用位置的最低级别的方法。每个路径都是一个简单的数字数组&#xff0c;它通过文档树下每个祖先节点…...

一次直播和图像识别技术应用的探索之旅

背景 Think Better 很多行业都在“卷”&#xff0c;作为金融科技行业的信也&#xff0c;也不例外。除了卷云计算、大数据和人工智能这些非常有深度的技术以外&#xff0c;信也向着技术融合创新的方向逐步探索&#xff0c;做得更好一些&#xff0c;为用户提供更好的价值&#xf…...

Ubuntu 下使用 Pybind11 实现 C++ 调用 Python 接口的示例

Pybind11 是一个轻量级的库&#xff0c;它提供了在 C 中无缝集成 Python 代码的能力。使用 Pybind11&#xff0c;你可以很容易地从 C 调用 Python 代码&#xff0c;反之亦然。下面我将通过一个简单的例子来展示如何在 Ubuntu 系统上使用 Pybind11 从 C 调用 Python 接口。 安装…...

docker安装和使用kafka

1. 启动zookeeper Kafka依赖zookeeper, 首先安装zookeeper -p&#xff1a;设置映射端口&#xff08;默认2181&#xff09; docker run --name zookeeper \--network app-tier \-e ALLOW_ANONYMOUS_LOGINyes \--restartalways \-d bitnami/zookeeper:latest2. 启动kafka docker…...

CTP-API开发系列之接口对接准备

CTP-API开发系列之接口对接准备 CTP-API开发系列之接口对接准备CTP-API文件清单CTP-API通用规则命名规则Spi与Api CTP-API通讯模式开发语言选择 CTP-API开发系列之接口对接准备 CTP-API文件清单 文件名说明ThostFtdcTraderApi.h交易接口&#xff0c;C头文件&#xff0c;包括 …...

C++小记 -链表

链表 文章目录 链表链表基础理论链表的类型单链表双链表循环链表 链表的存储方式链表的定义链表的操作添加节点删除节点 性能分析构建链表删除节点&#xff08;内存泄漏的坑&#xff09;1.直接移除2.使用虚拟头结点3.delete指针后&#xff0c;要将指针置为NULL&#xff01;&…...

网络协议学习DAY1

1.网络协议模型: OSI协议模型 应用层 实际发送的数据 表示层 发送的数据是否加密 会话层 是否建立会话连接 传输层 数据传输的方式&#xff08;数据报、流式&#xff09; 网…...

vue3中全局变量的定义和获取

在vue项目中&#xff0c;我们知道vue2定义全局变量是在main.js文件将变量挂载到vue.prototype.name"lisi"&#xff0c;在页面通过this.name去调用。但是在vue3中&#xff0c;这个定义全局变量有所改变&#xff1a; const app createApp(App) app.config.globalProp…...

1.2 数据模型 数据库系统概论

目录 1.2.1 两类数据模型 1.2.2 概念模型 1.信息世界中的基本概念 &#xff08;1&#xff09;实体 &#xff08;2&#xff09;属性 &#xff08;3&#xff09;码 &#xff08;4&#xff09;实体型 &#xff08;5&#xff09;实体集 &#xff08;6&#xff09;联系 2.…...

C#中openFileDialog 对话框不在最顶层,TopMost的异常情况

重点&#xff01;&#xff01;&#xff01;若 当前窗体this的TopMost是false&#xff0c;可以设置为true&#xff0c;这样打开的对话框就是最顶层 /// <summary> /// 设置窗体TopMost&#xff0c;缺点和其他程序ide有冲突。例如VS有断点的调试会卡死 /// </summary&g…...

信息安全与阿里云等保三级方案实践总结

信息安全在当今数字化时代变得至关重要&#xff0c;企业和组织需要采取有效措施来保护其数据和信息资产。阿里云作为中国领先的云服务提供商&#xff0c;提供了等保三级方案&#xff0c;帮助用户满足国家信息安全等级保护的要求。本文将探讨信息安全和阿里云等保三级方案的重要…...

嵌入式学习记录——线程

线程基本概念: 线程:线程是一个轻量级的进程,位于进程空间内部,一个进程中可以创建多个线程 1.线程创建: 线程独占栈空间,文本段、数据段和堆区与进程共享 2.线程调度: 与进程调度是一样的 宏观并行,微观串行 3.线程消亡: 与进程消亡是一样的 4.进程和线程…...

RocketMQ延迟消息机制

两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数&#xff0c;对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后&#xf…...

Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档&#xff09;&#xff0c;如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下&#xff0c;风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...

人机融合智能 | “人智交互”跨学科新领域

本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...

Kafka主题运维全指南:从基础配置到故障处理

#作者&#xff1a;张桐瑞 文章目录 主题日常管理1. 修改主题分区。2. 修改主题级别参数。3. 变更副本数。4. 修改主题限速。5.主题分区迁移。6. 常见主题错误处理常见错误1&#xff1a;主题删除失败。常见错误2&#xff1a;__consumer_offsets占用太多的磁盘。 主题日常管理 …...

【LeetCode】算法详解#6 ---除自身以外数组的乘积

1.题目介绍 给定一个整数数组 nums&#xff0c;返回 数组 answer &#xff0c;其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法&#xff0c;且在 O…...

MyBatis中关于缓存的理解

MyBatis缓存 MyBatis系统当中默认定义两级缓存&#xff1a;一级缓存、二级缓存 默认情况下&#xff0c;只有一级缓存开启&#xff08;sqlSession级别的缓存&#xff09;二级缓存需要手动开启配置&#xff0c;需要局域namespace级别的缓存 一级缓存&#xff08;本地缓存&#…...

ZYNQ学习记录FPGA(一)ZYNQ简介

一、知识准备 1.一些术语,缩写和概念&#xff1a; 1&#xff09;ZYNQ全称&#xff1a;ZYNQ7000 All Pgrammable SoC 2&#xff09;SoC:system on chips(片上系统)&#xff0c;对比集成电路的SoB&#xff08;system on board&#xff09; 3&#xff09;ARM&#xff1a;处理器…...

门静脉高压——表现

一、门静脉高压表现 00:01 1. 门静脉构成 00:13 组成结构&#xff1a;由肠系膜上静脉和脾静脉汇合构成&#xff0c;是肝脏血液供应的主要来源。淤血后果&#xff1a;门静脉淤血会同时导致脾静脉和肠系膜上静脉淤血&#xff0c;引发后续系列症状。 2. 脾大和脾功能亢进 00:46 …...

深度解析云存储:概念、架构与应用实践

在数据爆炸式增长的时代&#xff0c;传统本地存储因容量限制、管理复杂等问题&#xff0c;已难以满足企业和个人的需求。云存储凭借灵活扩展、便捷访问等特性&#xff0c;成为数据存储领域的主流解决方案。从个人照片备份到企业核心数据管理&#xff0c;云存储正重塑数据存储与…...

【笔记】AI Agent 项目 SUNA 部署 之 Docker 构建记录

#工作记录 构建过程记录 Microsoft Windows [Version 10.0.27871.1000] (c) Microsoft Corporation. All rights reserved.(suna-py3.12) F:\PythonProjects\suna>python setup.py --admin███████╗██╗ ██╗███╗ ██╗ █████╗ ██╔════╝…...