【Loki】最佳实践 - 基于LogQL的Metric

2023-11-11 15:12
文章标签 最佳 实践 loki metric logql

本文主要是介绍【Loki】最佳实践 - 基于LogQL的Metric,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

    • 1. 前言
    • 2. 最佳实践
    • 3. 后记
    • 4. 参考

1. 前言

职业生涯一直都是在传统软件行业里,因此所接触到的系统多以单体为主,规模体量上限低,因此不论是团队技术氛围,还是实际的资源投入上,监控这一块都属于是年三十晚上打的兔子 —— 有你过年,没你也过年。

虽然一直缺乏实际的机会来实际体验监控在大型软件架构上的用途,但在平时的阅读理论熏陶,以及对于实际工作的刻意观察和思考之下,笔者一直也是尝试在推广自己对于监控的理解。

没有监控的应用运行如同闭眼开车,突出一个赌人品。
~
如果你不能测量它,你就无法优化它。所以监控应该是所有改良的起始步骤。
~
更重要的,也是需要不断普及的一个常识 —— 监控工具的使用与监控功能的实现和高效应用之间不是等价关系。监控的主要目:

  1. 问题被报告时,辅助更为快速的定位问题,不断缩短问题的MTTR。(这是一个没有终点的工作)
  2. 问题发生初始阶段,于客户之前先察觉问题,增强自身应对问题的灵活度。
  3. 通过统计分析,料敌于先,为应用优化提供指导和方向。

本文重点关注以上的第三点 —— “通过统计分析,为应用优化提供指导和方向”。就我个人理解,这才是监控的最大价值所在,解决问题只是最初级的CMMI1级,能够预测问题至少也是CMMI4了。

2. 最佳实践

注意:以下功能只是引子,启发思维之用,最重要的是站在系统全局,站在研发和产品的视野上,换位思考之下自主分析总结出更多的指标。不断为了系统优化指明方向,将系统优化方向的指导权牢牢掌握在自己手上,化被动为主动

正式开始前,先交代下背景。

  1. 背景项目为微服务架构,其日志格式整体分两类:access log(访问日志)和 business log(业务日志),具体格式如下:

    # access log(系统访问日志,使用logback-access组件自动实现)
    [%t{yy-MM-dd HH:mm:ss.SSS}][%tid][%clientHost][%requestURL,%statusCode][%elapsedTime,%i{Referer}][%reqAttribute{client}][%i{User-Agent}][%reqAttribute{userId}][%reqAttribute{serviceName}][%reqAttribute{serviceSourceType}][%reqAttribute{serviceType}][%reqAttribute{serviceOwner}][#%requestContent#][#%responseContent#]# business log(业务代码中采用log.xxx()方式输出的日志)
    [%d{yy-MM-dd HH:mm:ss.SSS}][%X{tid}][pid:${PID:-}][tid:%15.15t][%-40.40logger:%line][%5p] %msg%n
    
  2. promtail采集时,对日志进行了必须label标记:module(日志所属模块)jobfilename。(遵从最佳实践,我们尽量减少了label的使用)
    2.1 对于module label,我们简单地按照既有模块进行标记。分为:api-gateway,xxx等。
    2.2 对于job label,我们则是将其划分为gatewayLog(网关模块的access log,独立出来是为了方面专门的统计),accessLog(其它微服务模块的access log),normalLog(info/warn级别日志),errorLog(error级别日志)。

以上背景下,截至目前我们总结了如下的Metric指标:

######################### 系统QPS-以api-gateway作为切入点(过去五分钟)
rate({module="api-gateway", job="gatewayLog"}  | drop filename[5m])######################### 系统总访问量-以api-gateway作为切入点(过去2天)
count_over_time({job="gatewayLog"} | drop filename[2d])######################### 系统错误率-以api-gateway作为切入点(过去五分钟)
rate({module="api-gateway", job="errorLog"} | drop filename[5m])######################### 系统错误总数-以api-gateway作为切入点(过去五分钟)
count_over_time({module="api-gateway", job="errorLog"} | drop filename[5m])######################### 系统各模块的错误总数(过去两天)
# 这个结果里反馈得很有意思,主要错误都发生在api-gateway和serve-manager两个模块
count_over_time({job="errorLog"} | drop filename[2d])########################## 系统各模块的普通日志总数(过去两天)
# 搭配上面的"错误总数",很容易发现一些有意思的统计信息:
# server-manager模块在过去的两天里: 错误日志数量42981, 普通日志数量117
# api-gateway依然是日志产生的最大源头,存在三个数量级的差异
count_over_time({job="normalLog"} | drop filename[2d])########################## 系统各模块的所有日志总数(过去两天) ---- 以下两个任选其一
sum (count_over_time({module=~".+"}  | drop filename[2d])) by (module)count_over_time({module=~".+"}  | drop filename,job [2d])######################### url请求耗时的顺序排列
# 筛选出系统里请求最耗时的前十类url, 分析是否有进一步地优化空间
sort_desc(topk(10,quantile_over_time(0.99,{module="api-gateway", job="gatewayLog"}| json| __error__ = ""| level = "ACCESS"| label_format requestUrl=`{{regexReplaceAll  "(.*)\\?.*" .requestUrl "${1}"}}`| requestUrl !~ ".*-proxy/.*"| unwrap elapsedTime [1h]) by (requestUrl)) by (elapsedTime))sort_desc(topk(10,avg_over_time({module="api-gateway", job="gatewayLog"}| json| __error__ = ""| level = "ACCESS"| label_format requestUrl=`{{regexReplaceAll  "(.*)\\?.*" .requestUrl "${1}"}}`| drop clientIp,filename,job,level,logtime,method,module,msg,protocol,referer,serviceName,serviceOwner,serviceSourceType,serviceType,statusCode,tid,userAgent,userName| unwrap elapsedTime [1h]) by (requestUrl)))######################### 某个URL的请求耗时P99线
quantile_over_time(0.99,{module="api-gateway", job="gatewayLog"}| json| __error__ = ""| level = "ACCESS"| label_format requestUrl=`{{regexReplaceAll  "(.*)\\?.*" .requestUrl "${1}"}}`| requestUrl = "/api/server-manager/xxx/yyy/zzz"| unwrap elapsedTime [1h]) by (requestUrl)######################### 某个URL的平均请求耗时(过去一小时内)
# 将 avg_over_time 切换为 max_over_time, min_over_time可获得过去一小时内该请求的最大耗时与最小耗时
avg_over_time({module="api-gateway", job="gatewayLog"}| json| __error__ = ""| level = "ACCESS"| label_format requestUrl=`{{regexReplaceAll  "(.*)\\?.*" .requestUrl "${1}"}}`| requestUrl = "/api/server-manager/xxx/yyy/zzz"| drop clientIp,filename,job,level,logtime,method,module,msg,protocol,referer,serviceName,serviceOwner,serviceSourceType,serviceType,statusCode,tid,userAgent,userName| unwrap elapsedTime [1h])######################### 监控指标serviceName为空的情况排查
sum(count_over_time({module="api-gateway", job="gatewayLog"}| json| __error__ = ""| label_format requestUrl=`{{regexReplaceAll  "(.*)\\?.*" .requestUrl "${1}"}}`| drop clientIp,filename,job,level,logtime,method,module,msg,protocol,referer,serviceOwner,serviceSourceType,serviceType,statusCode,tid,userAgent,userName|serviceName = ""[2d])) by (requestUrl)######################### 某个接口是否存在被调用过,被调用的次数: 以筛选过期接口。
{module="gis-manager", job="accessLog"}| json| __error__ = ""#| level = "ACCESS"| label_format requestUrl=`{{regexReplaceAll "(.*)\\?.*" .requestUrl "${1}"}}`| requestUrl =~ ".*/services.*"#====================== 非人工访问带来的请求量
sum(count_over_time({module="api-gateway", job="gatewayLog"}| json| __error__ = ""|userAgent = "fasthttp" or userAgent = "Apache-HttpClient/4.5.13 (Java/1.8.0_332)"[2d]))# api-gateway异常日志统计 —— 统计每类异常的总数,对应的url,分析到底是哪些链接到底的问题数量最多,找出优化点。
sum(count_over_time({module="api-gateway", job="errorLog"}| drop filename!~ "(?s).*PreAuthFilter.*"|= "Exception"| json| __error__ = ""| label_format exceptionType=`{{regexReplaceAll  "(?s).+?\\s(.*?)Exception:.*" .msg "${1}Exception"}}`| drop msg [2h])) by (exceptionType)

3. 后记

可以看出,以上其实都是在熟悉LogQL之后根据需求马上就能写出来的表达式,所以本文意为总结并且抛砖引玉,希望不断完全系统实时Metric库,延缓系统的腐坏速度。

过往很多时候的优化,虽然我们也是试图做全局通盘考虑,但确实全局视野的情况下,实际效果上看更多还是单点优化。

但在引入可观测性的Metric之后,情况就能发生根本性的改变 —— 现在有了一个时刻就绪的全局检验方法,随时验证/检查自己的思路是否发生偏移;用客观的全局视野和数据来判定当前系统的主要矛盾,而不是靠"感觉"来决定应该先去做哪方面的优化。

4. 参考

  1. Office Site - LogQL: Log query language

这篇关于【Loki】最佳实践 - 基于LogQL的Metric的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/390960

相关文章

基于MySQL Binlog的Elasticsearch数据同步实践

一、为什么要做 随着马蜂窝的逐渐发展,我们的业务数据越来越多,单纯使用 MySQL 已经不能满足我们的数据查询需求,例如对于商品、订单等数据的多维度检索。 使用 Elasticsearch 存储业务数据可以很好的解决我们业务中的搜索需求。而数据进行异构存储后,随之而来的就是数据同步的问题。 二、现有方法及问题 对于数据同步,我们目前的解决方案是建立数据中间表。把需要检索的业务数据,统一放到一张M

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

如何确定 Go 语言中 HTTP 连接池的最佳参数?

确定 Go 语言中 HTTP 连接池的最佳参数可以通过以下几种方式: 一、分析应用场景和需求 并发请求量: 确定应用程序在特定时间段内可能同时发起的 HTTP 请求数量。如果并发请求量很高,需要设置较大的连接池参数以满足需求。例如,对于一个高并发的 Web 服务,可能同时有数百个请求在处理,此时需要较大的连接池大小。可以通过压力测试工具模拟高并发场景,观察系统在不同并发请求下的性能表现,从而

Prometheus与Grafana在DevOps中的应用与最佳实践

Prometheus 与 Grafana 在 DevOps 中的应用与最佳实践 随着 DevOps 文化和实践的普及,监控和可视化工具已成为 DevOps 工具链中不可或缺的部分。Prometheus 和 Grafana 是其中最受欢迎的开源监控解决方案之一,它们的结合能够为系统和应用程序提供全面的监控、告警和可视化展示。本篇文章将详细探讨 Prometheus 和 Grafana 在 DevO

springboot整合swagger2之最佳实践

来源:https://blog.lqdev.cn/2018/07/21/springboot/chapter-ten/ Swagger是一款RESTful接口的文档在线自动生成、功能测试功能框架。 一个规范和完整的框架,用于生成、描述、调用和可视化RESTful风格的Web服务,加上swagger-ui,可以有很好的呈现。 SpringBoot集成 pom <!--swagge

vue2实践:el-table实现由用户自己控制行数的动态表格

需求 项目中需要提供一个动态表单,如图: 当我点击添加时,便添加一行;点击右边的删除时,便删除这一行。 至少要有一行数据,但是没有上限。 思路 这种每一行的数据固定,但是不定行数的,很容易想到使用el-table来实现,它可以循环读取:data所绑定的数组,来生成行数据,不同的是: 1、table里面的每一个cell,需要放置一个input来支持用户编辑。 2、最后一列放置两个b

【HarmonyOS】-TaskPool和Worker的对比实践

ArkTS提供了TaskPool与Worker两种多线程并发方案,下面我们将从其工作原理、使用效果对比两种方案的差异,进而选择适用于ArkTS图片编辑场景的并发方案。 TaskPool与Worker工作原理 TaskPool与Worker两种多线程并发能力均是基于 Actor并发模型实现的。Worker主、子线程通过收发消息进行通信;TaskPool基于Worker做了更多场景化的功能封装,例

vue2实践:第一个非正规的自定义组件-动态表单对话框

前言 vue一个很重要的概念就是组件,作为一个没有经历过前几代前端开发的我来说,不太能理解它所带来的“进步”,但是,将它与后端c++、java类比,我感觉,组件就像是这些语言中的类和对象的概念,通过封装好的组件(类),可以通过挂载的方式,非常方便的调用其提供的功能,而不必重新写一遍实现逻辑。 我们常用的element UI就是由饿了么所提供的组件库,但是在项目开发中,我们可能还需要额外地定义一

《C++中的移动构造函数与移动赋值运算符:解锁高效编程的最佳实践》

在 C++的编程世界中,移动构造函数和移动赋值运算符是提升程序性能和效率的重要工具。理解并正确运用它们,可以让我们的代码更加高效、简洁和优雅。 一、引言 随着现代软件系统的日益复杂和对性能要求的不断提高,C++程序员需要不断探索新的技术和方法来优化代码。移动构造函数和移动赋值运算符的出现,为解决资源管理和性能优化问题提供了有力的手段。它们允许我们在不进行不必要的复制操作的情况下,高效地转移资源

Vue3+elementplus实现图片上传下载(最强实践)

图片上传子组件: 实现照片的上传,预览,以及转成以逗号隔开的图片地址,即时监听,并发送消息到父组件。 <!-- ImageUploader.vue --> <template><div><el-upload class="avatar-uploader" :http-request="customUpload" :before-upload="beforeUpload":show-fil