Go微服务: 日志系统ELK的应用

概述

  • 基于前文,我们已经了解并搭建完成ELK的所有环境了,现在我们来结合应用程序来使用ELK
  • 参考前文:https://active.blog.csdn.net/article/details/138898538

封装日志模块

  • 在通用工具模块: gitee.com/go-micro-services/common 这个包是通用的工具包
  • 新增 zap.go
    package commonimport ("fmt""os""path/filepath""go.uber.org/zap""go.uber.org/zap/zapcore""gopkg.in/natefinch/lumberjack.v2"
    )// MyType 是我们想要提供的类型
    type ZapLogger struct {logger *zap.SugaredLogger
    }// NewMyType 是一个构造函数,用于创建 MyType 的新实例
    func NewZapLogger(filePath string, FilePerms int) *ZapLogger {log, _ := ZapLoggerInit(filePath, FilePerms)return &ZapLogger{logger: log}
    }func (zl *ZapLogger) Debug(args ...interface{}) {zl.logger.Debug(args)
    }func (zl *ZapLogger) Debugf(template string, args ...interface{}) {zl.logger.Debugf(template, args...)
    }func (zl *ZapLogger) Info(args ...interface{}) {zl.logger.Info(args...)
    }func (zl *ZapLogger) Infof(template string, args ...interface{}) {zl.logger.Infof(template, args...)
    }func (zl *ZapLogger) Warn(args ...interface{}) {zl.logger.Warn(args...)
    }func (zl *ZapLogger) Warnf(template string, args ...interface{}) {zl.logger.Warnf(template, args...)
    }func (zl *ZapLogger) Error(args ...interface{}) {zl.logger.Error(args...)
    }func (zl *ZapLogger) Errorf(template string, args ...interface{}) {zl.logger.Errorf(template, args...)
    }func (zl *ZapLogger) DPanic(args ...interface{}) {zl.logger.DPanic(args...)
    }func (zl *ZapLogger) DPanicf(template string, args ...interface{}) {zl.logger.DPanicf(template, args...)
    }func (zl *ZapLogger) Panic(args ...interface{}) {zl.logger.Panic(args...)
    }func (zl *ZapLogger) Panicf(template string, args ...interface{}) {zl.logger.Panicf(template, args...)
    }func (zl *ZapLogger) Fatal(args ...interface{}) {zl.logger.Fatal(args...)
    }func (zl *ZapLogger) Fatalf(template string, args ...interface{}) {zl.logger.Fatalf(template, args...)
    }// 这个供外部调用
    func ZapLoggerInit(filePath string, FilePerms int) (*zap.SugaredLogger, error) {fileName, fileErr := createFileWithPerms(filePath, os.FileMode(FilePerms))if fileErr != nil {// 使用 %v 来打印 error 类型的变量fmt.Printf("Error: %v\n", fileErr)return nil, fileErr}fmt.Printf("日志文件路径: %s\n", fileName)syncWriter := zapcore.AddSync(&lumberjack.Logger{Filename: fileName, //文件名称MaxSize:  521,      // MB// MaxAge:     0,MaxBackups: 0, //最大备份LocalTime:  true,Compress:   true, //是否启用压缩})// 编码encoder := zap.NewProductionEncoderConfig()// 时间格式encoder.EncodeTime = zapcore.ISO8601TimeEncodercore := zapcore.NewCore(// 编码器zapcore.NewJSONEncoder(encoder),syncWriter,zap.NewAtomicLevelAt(zap.DebugLevel))log := zap.New(core,zap.AddCaller(),zap.AddCallerSkip(1))return log.Sugar(), nil
    }// 创建一个多层目录下的文件,并设置权限, 如果文件已存在,则返回文件的路径;如果不存在,则创建并返回文件路径
    func createFileWithPerms(filePath string, perms os.FileMode) (string, error) {// 检查文件或目录是否存在fileInfo, err := os.Lstat(filePath)if err == nil {// 文件或目录已存在if fileInfo.IsDir() {// 如果已存在的是目录,返回错误return "", fmt.Errorf("无法创建文件 '%s',因为该路径已存在且是一个目录", filePath)}// 如果已存在的是文件,则返回文件路径return filePath, nil}if !os.IsNotExist(err) {// 如果发生其他错误(如权限问题),则返回错误return "", fmt.Errorf("检查文件 '%s' 时发生错误: %v", filePath, err)}// 创建多级目录err = os.MkdirAll(filepath.Dir(filePath), os.ModePerm) // 使用 os.ModePerm 允许所有权限,但文件权限会由下面的 os.Create 设置if err != nil {return "", fmt.Errorf("无法创建目录 '%s': %v", filepath.Dir(filePath), err)}// 创建文件file, err := os.Create(filePath)if err != nil {return "", fmt.Errorf("无法创建文件 '%s': %v", filePath, err)}// 关闭文件,因为我们只需要路径defer file.Close()// 设置文件权限err = file.Chmod(perms)if err != nil {return "", fmt.Errorf("无法设置文件 '%s' 的权限: %v", filePath, err)}// 返回新创建文件的路径return filePath, nil
    }
    
  • 上面这个模块,封装了打印日志的各种方法,基于其定义可以看出是基于实例的
  • 也就是说,不是静态的方法,而是可以 new 出很多实例的,这样可以让我们使用场景更加丰富
  • 代码仓库:https://gitee.com/go-micro-services/common

网关和服务应用程序准备


1 )概述
  • ELK简单来说,一般部署在我们的网关上,还是和之前一样,基于 gin 框架
  • 如果部署在各个微服务中,那环节就比较麻烦,而且资源耗费较多
  • 在我们的网关上来使用,还用之前的场景,基于网关上的某一个api来获取购物车中的数据
  • 例如:
    • /api/findAllTestElk1?user_id=1 基于这个路由,使用默认日志实例来输出, 正确日志
    • /api/findAllTestElk1?user_id=x 同上,触发错误日志
    • /api/findAllTestElk2?user_id=1 基于这个路由,自定义新的日志实例来输出
    • /api/findAllTestElk2?user_id=x 同上,触发错误日志
  • 基于以上,可以在不同模块实例化不同的日志文件

2 )utils包

  • utils/log.go

    package utilsimport ("gitee.com/go-micro-services/common"
    )// 通用配置
    var ZapLogger *common.ZapLogger// 日志默认设置
    func initLog() {ZapLogger = common.NewZapLogger("logs/app.log", 0777) // 这里两个参数可以配置到 conf/app.ini 中
    }
    
    • 这里使用 common 工具包,来实例化一个默认的日志实例
  • utils/common.go

    func init() {initLog() // 添加这个
    }
    
    • 可见,在 init 函数中添加 initLog 函数

3 ) 定义路由

package routersimport ("gitee.com/go-micro-services/api/controllers/api""github.com/gin-gonic/gin"
)func RoutersInit(r *gin.Engine) {rr := r.Group("/api"){rr.GET("/findAll", api.ApiController{}.FindAll)rr.GET("/findAllTestElk1", api.Log1Controller{}.FindAllTestElk)rr.GET("/findAllTestElk2", api.Log2Controller{}.FindAllTestElk)}
}
  • 可见这里定义了三个路由,我们主要关注后面两个
  • 下面来看对应的控制器

4 )控制器

  • controllers/api/log1.go

    package apiimport ("context""fmt""strconv""gitee.com/go-micro-services/api/utils"cart "gitee.com/go-micro-services/cart/proto/cart""github.com/gin-gonic/gin""github.com/prometheus/common/log"
    )type Log1Controller struct{}// 这个方法用于测试ELK
    func (con Log1Controller) FindAllTestElk(c *gin.Context) {log.Info("接受到 /api/findAllTestElk1 访问请求")// 1. 获取参数user_id_str := c.Query("user_id")userId, err := strconv.ParseInt(user_id_str, 10, 64)if err != nil {utils.ZapLogger.Error("参数异常")c.JSON(200, gin.H{"message": "参数异常","success": false,})return}fmt.Println(userId)// 2. rpc 远程调用:获取购物车所有商品cartClient := cart.NewCartService(utils.CartServices, utils.SrvClient)cartAll, err := cartClient.GetAll(context.TODO(), &cart.CartFindAll{UserId: userId})utils.ZapLogger.Info(cartAll)fmt.Println("-----")c.JSON(200, gin.H{"data":    cartAll,"success": true,})
    }
    
  • controllers/api/log2.go

    package apiimport ("context""fmt""strconv""gitee.com/go-micro-services/api/utils"cart "gitee.com/go-micro-services/cart/proto/cart""gitee.com/go-micro-services/common""github.com/gin-gonic/gin""github.com/prometheus/common/log"
    )type Log2Controller struct{}// 通用配置
    var ZapLogger *common.ZapLoggerfunc init() {ZapLogger = common.NewZapLogger("logs/app2.log", 0777) // 这里相关参数可以配置到 conf/app.ini 中
    }// 这个方法用于测试ELK
    func (con Log2Controller) FindAllTestElk(c *gin.Context) {log.Info("接受到 /api/findAllTestElk2 访问请求")// 1. 获取参数user_id_str := c.Query("user_id")userId, err := strconv.ParseInt(user_id_str, 10, 64)if err != nil {ZapLogger.Error("参数异常")c.JSON(200, gin.H{"message": "参数异常","success": false,})return}fmt.Println(userId)// 2. rpc 远程调用:获取购物车所有商品cartClient := cart.NewCartService(utils.CartServices, utils.SrvClient)cartAll, err := cartClient.GetAll(context.TODO(), &cart.CartFindAll{UserId: userId})ZapLogger.Info(cartAll)fmt.Println("-----")c.JSON(200, gin.H{"data":    cartAll,"success": true,})
    }
    
  • 上面两个控制器的内容,基本一致,我们的目的是用来测试不同的日志实例的测试

    • 第一个控制器使用的是默认初始化时的日志对象
    • 第二个控制器使用的是重新初始化后的日志实例,这样我们可以自定义不同的日志生成路径
  • 代码仓库

    • 网关: https://gitee.com/go-micro-services/api
    • 购物车服务: https://gitee.com/go-micro-services/cart

5 )启动网关和购物车服务

  • 可见,两个服务已经启动起来了

下载和运行 FileBeat 程序

  • 访问:https://www.elastic.co/cn/downloads/past-releases/filebeat-7-9-3/
  • 注意:这里的版本要和之前ELK环境搭建时的版本对应
  • 选择合适的版本,在服务器要选择服务器版本,这里我选择Mac版本来测试
  • 下载完成后,里面有很多配置好的文件,我们主要关注两个: 二进制文件 filebeatfilebeat.yml
  • 将这两个文件拷贝进入网关项目
  • 编辑 filebeat.yml
    # 输入
    filebeat.inputs:- type: logenabled: truepaths:- ./logs/*.log
    #输出
    output.logstash:hosts: ["localhost:5044"]
    
  • 这里,可以配置不同的文件来区分各个部署环境,因为环境不同,参数也不同
  • 也可以使用环境变量,部署时进行注入,我这里只是做了一个演示
  • 启动命令 $ ./filebeat -e -c ./filebeat.yml
    • -e: 启动在终端输出采集信息
    • -c: 指定yml启动配置文件
  • 当然这些个启动命令,后期也可以在Dockerfile, DockerCompose 或 K8s中定义,不再赘述

登录并配置 Kibana 来查看日志


1 ) 概述

  • 如果是本机搭建的,访问: http://localhost:5601
    • 这里的 5601 就是之前搭建ELK时暴露出来的
  • 基于前文配置的用户名和密码进行登录

2 ) 登录之后,点击右侧的 Discover

3 ) 创建和配置索引

4 )回到 Discover 查看日志

截止目前为止,所有ELK环境已经全部打通 ~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/14662.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CTFHUB技能树——SSRF(三)

目录 URL Bypass 数字IP Bypass 302跳转 Bypass DNS重绑定 Bypass SSRF绕过方法: (1) http://abc.com127.0.0.1 (2)添加端口号 http://127.0.0.1:8080 (3)短地址 htt…

限制U盘使用:企业数据安全的软件解决方案

在当今数字化办公环境中,U盘作为一种便捷的数据传输工具,其使用在企业内部非常普遍。然而,U盘的不当使用也给企业数据安全带来了巨大风险。为了防止数据泄露和病毒传播,企业需要采取有效的软件解决方案来限制U盘的使用。本文将探讨…

docker容器安装mysql

linux: centOS-7 hadoop: 3.3.6 前置章节: (图文并茂)基于CentOS-7搭建hadoop3.3.6大数据集群-CSDN博客 可选:zookeeper安装教程-CSDN博客 1.安装docker 1.1 添加docker的repo源 sudo yum-config-manager --add-repo http://mirrors.aliyun.com/…

集合、Collection接口特点和常用方法

1、集合介绍 对于保存多个数据使用的是数组,那么数组有不足的地方。比如, 长度开始时必须指定,而且一旦制定,不能更改。 保存的必须为同一类型的元素。 使用数组进行增加/删除元素的示意代码,也就是比较麻烦。 为…

一种简单实用的ollvm反混淆的方案与源码

我是一名从事反欺诈&风控&设备指纹相关的工作,最近对ollvm的如何逆向的问题进行了学习与思考。 ollvm是一个开源免费的so混淆工具,对于逆向的小白来说简直是灾难性的存在。 这个例子是超简单,我想每个人都可以学会跟掌握,…

面试问题小结

说说你的项目,从里面学到啥了(随便说) CAS 线程池 的各个方面 线程咋创建(4种方式) 说一下聚集索引和非聚集索引 50w男 50w女 ,在B树中咋存储的(类似下面的图,变通一下就行了&a…

本是梦中人,常作花下客。心中自往来,知我有几个。

我们总是喜欢拿“顺其自然”来敷衍人生道路上的荆棘坎坷,却很少承认,真正的顺其自然, 其实是竭尽所能之后的不强求, 而非两手一摊的不作为。 一花凋零荒芜不了整个春天, 一次挫折也荒废不了整个人生。 多年后&#x…

近临算法(个人总结版)

背景 近邻算法(Nearest Neighbor Algorithm)是一种基本但非常有效的分类和回归方法。最早由Fix和Hodges在1951年提出,经过几十年的发展和改进,已成为数据挖掘、模式识别和机器学习领域的重要工具。近邻算法基于相似性原则&#x…

通过el-tree自定义渲染网页版工作目录,实现鼠标悬浮显示完整名称、用icon区分文件和文件夹等需求

目录 一、通过el-tree自定义渲染网页版工作目录 1.1、需求介绍 1.2、使用el-tree生成文档目录 1.2.1、官方基础用法 ①效果 ②代码: 1.2.2、自定义文档目录(实现鼠标悬浮显示完整名称、用icon区分文件和文件夹) ①效果(直接效…

find 几招在 Linux 中高效地查找目录

1. 介绍 在 Linux 操作系统中,查找目录是一项常见的任务。无论是系统管理员还是普通用户,都可能需要查找特定的目录以执行各种操作,如导航文件系统、备份数据、删除文件等。Linux 提供了多种命令和工具来帮助我们在文件系统中快速找到目标目…

浅谈后端整合Springboot框架后操作基础配置

boot基础配置 现在不访问端口8080 可以吗 我们在默认启动的时候访问的是端口号8080 基于属性配置的 现在boot整合导致Tomcat服务器的配置文件没了 我们怎么去修改Tomcat服务器的配置信息呢 配置文件中的配置信息是很多很多的... 复制工程 保留工程的基础结构 抹掉原始…

朴素贝叶斯+SMSSpamCollections

1. 打开 Jupyter 后,在工作目录中,新建一个文件夹命名为 Test01 ,并且在文件夹中导入数据 集。在网页端界面点击 “upload” 按钮,在弹出的界面中选择要导入的数据集。然后数据集出现 在 jupyter 文件目录中,此时…

初识Qt:从Hello world到对象树的深度解析

Qt中的对象树深度解析 Hello world1.图形化界面创建命令行式创建在栈上创建在堆上创建为什么传文本需要QString,std::string不行吗?那为什么要传入this指针?为什么new后不用显示调用delete函数呢,不会造成内存泄漏问题吗&#xff…

python:__class_getitem__使用以及cached_property源码分析

python:__class_getitem__使用以及cached_property源码分析 1 前言 Python中如何模拟泛型类型? 当使用类型标注时,使用 Python 的方括号标记来形参化一个 generic type 往往会很有用处。 例如,list[int] 这样的标注可以被用来表…

K8S集群再搭建

前述:总体是非常简单的,就是过程繁琐,不过都是些重复的操作 master成员: [controller-manager, scheduler, api-server, etcd, proxy,kubelet] node成员: [kubelet, proxy] master要修改的配置文件有 1. vi /etc/etcd/etcd.conf # 数…

wordpress教程视频 wordpress教程网盘 wordpress教程推荐wordpress教程网

WordPress,作为一款强大且灵活的开源内容管理系统,已成为许多网站开发者与运营者的首选。其强大的功能、丰富的插件以及易于上手的特点,使得无论是初学者还是专业开发者都能轻松构建出个性化的网站。然而,对于初学者来说&#xff…

JUnit5标记测试用例

使用场景: 通过Tag对用例分组: 环境分组:测试环境、预发布环境阶段分组:冒烟用例版本分组:V1.1、V1.2 Tag标记用例: 设置标签根据标签执行 结合Maven执行结合测试套件执行 设置标签: 通过T…

【Spring Cloud】全面解析服务容错中间件 Sentinel 持久化两种模式

文章目录 推送模式本地文件持久化(拉模式)配置yml编写处理类添加配置演示 配置中心持久化(推模式)修改nacos在sentinel中生效引入依赖配置文件 修改sentinel在nacos中生效下载源码更改代码演示 总结 推送模式 Sentinel 规则的推送…

allegro 无法删除Xnet

allegro 无法删除Xnet Orcad中打开Constraint Manager之后,再生成网表,导入PCB后就会出现一堆Xnet网络。无法去除Xnet。 解决办法 在原理图ORCAD中, 1、打开Edit Object properties 2、选择Filter by:Capture 3、点击New Property 4、设置…

火山引擎边缘云亮相 Force 原动力大会,探索 AI 应用新范式

5月15日,2024 春季火山引擎 FORCE 原动力大会在北京正式举办。大会聚焦 AI 主题,以大模型应用为核心、以 AI 落地为导向,展示了火山引擎在大模型、云计算领域的实践应用,携手汽车、手机终端、金融、消费、互联网等领域的专家和企业…