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盘的使用。本文将探讨…

linux使用dockerCompose脚本化部署镜像

1.根据实际修改docker-compose.yml文件: version: 3.5 services:#mysql数据库脚本mysql:image: 镜像名:版本 #需要查看本地镜像进行填写。同下container_name: mysql #容器名,同下restart: alwaysports:- 3306:3306 #第一个3306为主机…

Java中解决跨域的几种方法

一、 什么是跨域(同源策略) 同源的定义:如果两个页面的协议,端口(如果有指定)和主机都相同,则两个页面具有相同的源 1,用户在浏览器输入的URL中包含的协议、域名、端口都完全相同。如果有一项不同&#xf…

【python005】python批量、动态调参请求接口(已更新)

1.熟悉、梳理、总结项目研发实战中的Python开发日常使用中的问题。随着版本更新,做了一些变动,如商业化限制,取消一些语法等。 2.欢迎点赞、关注、批评、指正,互三走起来,小手动起来! 文章目录 1.背景介绍2.单次接口请求总结代码片3.批量循环接口请求总结代码片4.持久化`…

机器人流程自动化与低代码流程自动化:技术革新的双重驱动

在数字化时代的浪潮中,企业对于高效、智能的工作流程的需求日益增强。在这一背景下,机器人流程自动化(RPA)和低代码流程自动化(Low-Code Automation)应运而生,成为推动企业数字化转型的重要力量…

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/…

一个月速刷leetcodeHOT100 day08 两道DP题 一道子串

和为k的子数组 中等 提示 给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的子数组的个数 。 子数组是数组中元素的连续非空序列。 示例 1: **输入:**nums [1,1,1], k 2 **输出:**2 示例 2: *…

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

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

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

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

RustGUI学习(iced/iced_aw)之扩展小部件(二十五):如何使用tab部件来创建tab多页面切换?

前言 本专栏是学习Rust的GUI库iced的合集,将介绍iced涉及的各个小部件分别介绍,最后会汇总为一个总的程序。 iced是RustGUI中比较强大的一个,目前处于发展中(即版本可能会改变),本专栏基于版本0.12.1. 概述 这是本专栏的第二十五篇,主要讲述tab页面切换部件的使用,会结…

[linux] bash中的单引号(‘)和双引号(“)

在命令行中,单引号()和双引号(")在某些情况下会有不同的效果,尤其是在涉及bash变量和特殊字符的解析时。在你给出的两个命令中: ps -ef|grep "tokenize"|grep -v grep|awk {print $2} 和 ps -ef|grep "tokenize"…

PCL点云边界提取——源码解析

文章目录 一、概述二、PCL边缘检测源码定位过程1、初始化2、检查输入点云是否稠密3、迭代处理每个点4、输出三、修改后的过程调用一、概述 在PCL中集成了一个非常经典的点云边缘检测算法,这个算法也在 PCL点云边界提取这篇博客中讲解了。该文章只介绍了AC算法的原理及接口调用…

hook中useContext到底怎么用

语法: somecontext createContext(defaultValue); 作用: 避免了组件嵌套太深, 顶层变量层层传递的麻烦. 如何消费顶层数据 第一步: 用createContext声明一个context上下文变量 import { createContext } from react;export const GlobalContext createContext({} as any);…

面试问题小结

说说你的项目,从里面学到啥了(随便说) 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服务器的配置信息呢 配置文件中的配置信息是很多很多的... 复制工程 保留工程的基础结构 抹掉原始…