使用 Loki、Loki4j、Grafana 和 Spring Boot 搭建一个轻量级、简单、易用的 Java 日志系统

要使用 LokiLoki4jGrafanaSpring Boot 搭建一个轻量级、简单、易用的 Java 日志系统,您可以按以下步骤进行。这个系统将利用 Loki 作为日志存储和聚合系统,Loki4j 作为 Java 的日志插件,Grafana 用于日志的可视化。

1.工具介绍:

  1. Loki:类似于 Prometheus 的日志系统,但 Loki 并不索引日志的内容,而是通过标签(label)进行索引,使用 PromQL 进行查询,轻量级且高效。
  2. Loki4j:一个适用于 Java 项目的日志库,可以将 Spring Boot 项目的日志直接推送到 Loki。
  3. Grafana:用于可视化 Loki 中存储的日志,并提供查询、分析和展示日志的功能。
  4. Spring Boot:作为 Java 应用框架,用于生成日志并集成 Loki4j 进行日志传输。

2.系统架构:

  • Spring Boot 应用程序通过 Loki4j 生成和发送日志。
  • Loki 作为日志聚合和存储系统接收日志。
  • Grafana 用于展示和可视化 Loki 中存储的日志。

3. 使用步骤

3.1.步骤 1:搭建 Loki 和 Grafana

可以通过 Docker 容器运行 Loki 和 Grafana,以便简化配置。

3.1.1.准备Loki和Grafanan的镜像

配置docker-compose.yml信息

version: '3.0'
services:loki:image: loki:latestcontainer_name: base_software_lokiports:- "3100:3100"networks:- monitoringgrafana:image: grafana:latestcontainer_name: base_software_grafanaports:- "3200:3000"networks:- monitoringdepends_on:- loki
volumes:es_data:driver: localnetworks:monitoring:driver: bridge

3.1.2 创建两个容器

docker-compose up -d

启动后,可以通过浏览器访问 http://localhost:3000,默认的用户名和密码均为 admin

3.1.3 配置 Grafana 数据源

  1. 登录 Grafana。
  2. 添加 Loki 作为数据源:
    • 进入 Configuration > Data Sources
    • 点击 Add data source,选择 Loki
    • 在 URL 中填写 http://127.0.0.1:3100
    • 保存并测试连接。

在这里插入图片描述

3.2.步骤 2:配置 Spring Boot 项目

在 Spring Boot 项目中使用 Loki4j 来将日志发送到 Loki。

3.2.1 添加依赖

在 Spring Boot 的 pom.xml 文件中添加 Loki4j 依赖:

<dependency>  <groupId>com.github.loki4j</groupId>  <artifactId>loki-logback-appender-jdk8</artifactId>  <version>1.4.2</version>  
</dependency>

3.2.2 配置 logback-spring.xml

Loki4j 使用 Logback 作为日志框架。你需要配置 src/main/resources/logback-spring.xml 文件来使用 Loki4j 将日志发送到 Loki:

<?xml version="1.0" encoding="UTF-8"?>
<configuration><!-- 彩色控制台控制 --><substitutionProperty name="log.pattern" value="%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(%5p) ${PID:-} %clr(---){faint} %clr(%-80.80logger{79}){cyan} %clr(:){faint} %m%n%wEx"/><substitutionProperty name="log.pattern.no" value="%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(%5p) ${PID:-} %clr(---){faint} %clr(%-80.80logger{79}){cyan} %clr(:){faint} %m%n%wEx"/><conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/><conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/><conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/><springProperty scope="context" name="LOG_FILE_DIR" source="loki.log-file-dir" defaultValue="log"/><!-- 控制台输出 --><appender name="Console" class="ch.qos.logback.core.ConsoleAppender"><encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"><pattern>${log.pattern}</pattern></encoder></appender><springProperty name="lokiEnabled" source="loki.enabled" defaultValue="false"/><springProperty scope="context" name="url" source="loki.url" defaultValue="http://localhost:3100/loki/api/v1/push"/><springProperty scope="context" name="env" source="loki.label.env" defaultValue="dev"/><springProperty scope="context" name="jobName" source="loki.label.job-name" defaultValue="xiaocai"/><springProperty scope="context" name="hostIp" source="loki.label.host-ip" defaultValue="localhost"/><springProperty scope="context" name="orgId" source="loki.org-id" defaultValue="xiaocai"/><appender name="LOKI" class="com.github.loki4j.logback.Loki4jAppender"><http class="com.github.loki4j.logback.ApacheHttpSender"><url>${url}</url><tenantId>${orgId}</tenantId></http><format><label><pattern>application=${jobName},env=${env},host=${hostIp},level=%level,traceid=%X{traceid:-0},urlPath=%X{urlPath:-0}</pattern></label><message><pattern>{"timestamp": "%d{yyyy-MM-dd HH:mm:ss.SSS}", "level": "%level", "logger": "%logger{36}", "thread": "%thread","traceid": "%X{traceid:-0}", "message": "%msg%n"}</pattern></message><sortByTime>true</sortByTime></format></appender><!-- 使用异步方式将日志推送至Loki --><appender name="ASYNC_LOKI" class="ch.qos.logback.classic.AsyncAppender"><!-- 队列大小设置,根据实际需要调整 --><queueSize>512</queueSize><!-- 丢弃策略,当队列满时采取的操作 --><discardingThreshold>0</discardingThreshold><neverBlock>true</neverBlock><!-- 实际的Loki Appender --><appender-ref ref="LOKI" /></appender><appender name="fileInfoLog" class="ch.qos.logback.core.rolling.RollingFileAppender"><filter class="ch.qos.logback.classic.filter.LevelFilter"><level>ERROR</level><onMatch>DENY</onMatch><onMismatch>ACCEPT</onMismatch></filter><encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"><pattern>${log.pattern.no}</pattern></encoder><!--滚动策略--><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><!--路径--><fileNamePattern>${LOG_FILE_DIR}/${jobName}info.%d.log</fileNamePattern><!--保留30天日志--><maxHistory>30</maxHistory></rollingPolicy></appender><appender name="fileErrorLog" class="ch.qos.logback.core.rolling.RollingFileAppender"><filter class="ch.qos.logback.classic.filter.ThresholdFilter"><level>ERROR</level></filter><encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"><pattern>${log.pattern.no}</pattern></encoder><!--滚动策略--><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><!--路径--><fileNamePattern>${LOG_FILE_DIR}/${jobName}error.%d.log</fileNamePattern><!--保留30天日志--><maxHistory>30</maxHistory></rollingPolicy></appender><root level="info"><appender-ref ref="Console" /><appender-ref ref="fileInfoLog" /><appender-ref ref="fileErrorLog" /><if condition='${lokiEnabled} == true'><then><appender-ref ref="ASYNC_LOKI" /></then></if></root>
</configuration>

在这个配置中,日志会同时输出到控制台和 Loki。Loki4j 的 Loki4jHttpSender 会将日志推送到 Loki 的 API(在此例中是 http://localhost:3100)。

3.2.3. 配置application.yml设置开关以及配置项目相关信息

# Loki 日志配置  
loki:  # Loki 服务的 URL,用于推送日志数据  url: http://127.0.0.1:3100/loki/api/v1/push  log-file-dir: E:\xiaocaiDemo\log\1  enabled: true  # 标签配置,用于标识日志来源的额外信息  label:  # 环境标签,标识当前运行的环境,例如开发环境  env: dev  # 服务名称标签,标识日志来源的服务名称  job-name: xiaocaiDemo  # 主机 IP 标签,标识日志来源的主机 IP 地址  host-ip: localhost  # 组织 ID,用于多租户环境中标识日志所属的组织  org-id: Demo

3.2.4. 写一个Filter将所有的请求均生成一个唯一id

import org.apache.logging.log4j.ThreadContext;  
import org.springframework.stereotype.Component;  import javax.servlet.Filter;  
import javax.servlet.FilterChain;  
import javax.servlet.FilterConfig;  
import javax.servlet.ServletException;  
import javax.servlet.ServletRequest;  
import javax.servlet.ServletResponse;  
import javax.servlet.http.HttpServletRequest;  
import java.io.IOException;  
import java.util.UUID;  @Component  
public class LogTraceIdFilter implements Filter {  public static final String LogTrace_ID = "traceid";  public static final String UrlPath_Key = "urlPath";  @Override  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {  try {  // 生成唯一的日志ID  String logId = UUID.randomUUID().toString().replace("-","");  // 将日志ID放入MDC  ThreadContext.put(LogTrace_ID, logId);  ThreadContext.put(UrlPath_Key, ((HttpServletRequest) request).getRequestURI());  // 打印日志信息  HttpServletRequest httpRequest = (HttpServletRequest) request;  // 继续处理请求  chain.doFilter(request, response);  } finally {  // 清除MDC中的日志ID  ThreadContext.clearMap();  }  }  @Override  public void init(FilterConfig filterConfig) {  }  @Override  public void destroy() {  }  
}
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.boot.web.servlet.FilterRegistrationBean;  
import org.springframework.context.annotation.Bean;  
import org.springframework.context.annotation.Configuration;  import javax.servlet.Filter;  */@Configuration  
public class Xiaocai_FilterConfig {  @Autowired  private LogTraceIdFilter logTraceIdFilter;  @Bean  public FilterRegistrationBean<Filter> logTraceIdFilterRegistrationBean() {  FilterRegistrationBean<Filter> registrationBean = new FilterRegistrationBean<>();  registrationBean.setFilter(logTraceIdFilter);  registrationBean.addUrlPatterns("*");  // 设置顺序,数值越小优先级越高  registrationBean.setOrder(1);  return registrationBean;  }  
}

3.2.5. 代码中使用

import org.slf4j.Logger;  
import org.slf4j.LoggerFactory;private final static Logger logger = LoggerFactory.getLogger(XiaocaiClass.class);logger.info(String.format("接口请求信息为:%s", strBuilder.toString()));

3.3.步骤 3:启动应用程序并生成日志

现在,您可以运行 Spring Boot 应用程序。应用程序启动后,生成的日志将通过 Loki4j 发送到 Loki。

使用以下命令启动 Spring Boot 应用:

mvn spring-boot:run

Spring Boot 应用程序运行后,日志会自动推送到 Loki,并通过 Grafana 可视化查看。

3.4.步骤 4:在 Grafana 中可视化日志

  1. 登录 Grafana。

  2. 进入 Explore 页面。

  3. 选择数据源为 Loki,然后在查询框中输入查询语句:

    {urlPath="/login/loginToken2LoginUserInfo"}
    

    你可以根据 Loki 的查询语法筛选和查看日志。

在这里插入图片描述

4.总结

  • Loki 提供日志的存储和索引,轻量高效。
  • Loki4j 使得 Java 应用可以轻松将日志推送到 Loki。
  • Grafana 用于查询和展示日志。
  • Spring Boot 作为 Java 应用框架,生成并发送日志。

通过这些步骤,您就搭建了一个轻量级且易于使用的 Java 日志系统。

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

CF1994 F. Stardew Valley [欧拉回路+树上差分]

传送门 [前题提要]:自模板题以后,很少遇到欧拉回路的题目,正好这道题结合了多种经典算法,故写一篇题解记录一下 读完题面,因为需要经过所有1边,所以很显然会想到应该将所有的1边拿出来建一个新图,然后在新图上添加0边,使得新图是一个欧拉图. 让我们来回忆一下什么是欧拉图.对…

【秋招笔试题】多多的平均值

解法&#xff1a;抽掉的两个数字之和为2倍的平均数&#xff0c;那么判断一下2倍的平均数是不是整数。然后在搞一个哈希表存取过的值即可。 package com.sky;import java.util.*;public class Test1 {public static void main(String[] args) {Scanner scanner new Scanner(Sy…

计算机研一规划2024/9/22

系列文章目录 文章目录 系列文章目录前言一、两条腿走路二、编程语言能力提升1.廖雪峰的python课2.Leetcode&#xff08;数据结构题&#xff09; 三、机器学习能力提升1.统计学习方法 李航2.kaggle竞赛 四、神经网络能力提升1.神经网络与深度学习 邱锡鹏2.一套自己的万金油模板…

openai最新o1上线(2024年09月12日)

gpt-4o-2024-08-06输出文本价格 10美元/M o1-preview输出价格 60美元/M https://lmarena.ai/?leaderboard 数字9.11和9.8谁大些 人工智能学习网站 https://chat.xutongbao.top/

Vue(16)——Vue3.3新特性

defineOptions 在 Vue 3.3 之前&#xff0c;如果需要在 <script setup> 中设置组件名&#xff0c;通常需要在额外的 <script> 标签中使用 Options API 进行配置。defineOptions 是 Vue 3.3 版本中引入的一个宏&#xff08;macro&#xff09;&#xff0c;它主要用于…

C++ bitset(位图)的介绍和使用

文章目录 一、bitset的介绍1. 位图的引入2. 位图的概念3. 位图的应用场景 二、bitset的使用1. 定义方式2. 成员函数3. 运算符重载 一、bitset的介绍 1. 位图的引入 面试题 给40亿个不重复的无符号整数&#xff0c;没排过序。给一个无符号整数&#xff0c;如何快速判断一个数是…

2024年蓝牙网关市场热门产品选购宝典

在本文中&#xff0c;我们将探讨不同类型的蓝牙网关及其分类&#xff0c;并提供一份指南&#xff0c;帮助您筛选出最适合的物联网网关。 室内蓝牙网关 室内网关通常用于智能建筑解决方案&#xff0c;如智能家居、零售店、购物中心和办公室。 这些网关的覆盖范围较短&#xff…

人工智能代表——无人驾驶:萝卜快跑

人工智能如何改变我们的出行&#xff1a;以“萝卜快跑”无人驾驶为例 随着科技的飞速发展&#xff0c;人工智能&#xff08;AI&#xff09;正以前所未有的方式渗透并改变着我们的日常生活&#xff0c;其中出行方式的变革尤为显著。在众多AI驱动的出行创新中&#xff0c;“萝卜…

【hot100-java】【缺失的第一个正数】

R9-普通数组篇 class Solution {public int firstMissingPositive(int[] nums) {int nnums.length;for (int i0;i<n;i){while(nums[i]>0&&nums[i]<n&&nums[nums[i]-1]!nums[i]){//交换nums[i]和nums[nums[i]-1]int temp nums[nums[i]-1];nums[nums[i]…

C语言课程设计题目(24个选题)

C语言课程设计题目 一、设计要求与设计报告二、检查要求三、打分标准四、提交时间五、选题要求题目列表题目一&#xff1a;职工信息管理系统设计题目二&#xff1a;图书信息管理系统设计题目三&#xff1a;图书管理系统设计题目四&#xff1a;实验设备管理系统设计题目五&#…

回答网友的一个SQL问题

网友问&#xff1a; CODE NAME 1 A 1 B 如何得到下面的值&#xff0c;该如何写SQL CODE NAME 1 AB 1 AB 俺的回答&#xff1a; declare t table(code varchar(50),name varchar(50)) insert into t(code,name) select 1,A union select…

python脚本程序怎么写更优雅?argparse模块巧妙应用

前言 命令行程序&#xff0c;也称CLI程序&#xff0c;另一个直观的名字是脚本程序&#xff0c;简称脚本&#xff0c;由于没有图形用户界面&#xff08;GUI&#xff09;&#xff0c;所以脚本程序常见的交互方式有3种&#xff1a; 1、脚本程序中读取环境变量&#xff0c;比如env…

Spring Security学习

系列文章目录 第一章 基础知识、数据类型学习 第二章 万年历项目 第三章 代码逻辑训练习题 第四章 方法、数组学习 第五章 图书管理系统项目 第六章 面向对象编程&#xff1a;封装、继承、多态学习 第七章 封装继承多态习题 第八章 常用类、包装类、异常处理机制学习 第九章 集…

【深度学习】深度卷积神经网络(AlexNet)

在 LeNet 提出后&#xff0c;卷积神经网络在计算机视觉和机器学习领域中很有名气&#xff0c;但并未起到主导作用。 这是因为 LeNet 在更大、更真实的数据集上训练的性能和可行性还有待研究。 事实上&#xff0c;在 20 世纪 90 年代到 2012 年之间的大部分时间里&#xff0c;…

电线杆上电气组件检测系统源码分享

电线杆上电气组件检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Comp…

视频怎么制作成二维码?视频轻松生成二维码的3步操作

现在很多人为了能够更快捷的实现视频内容的分享&#xff0c;会通过将视频生成二维码的方式&#xff0c;让其他人可以通过扫描二维码来查看视频内容。这种方式不需要用户存储视频&#xff0c;扫码就能够在设备上查看视频&#xff0c;有利于提升查看视频的便捷性&#xff0c;可以…

【秋招笔试题】多多排序

解法&#xff1a;简单语法题 package com.sky;import java.util.*;public class Test1 {public static void main(String[] args) {Scanner sc new Scanner(System.in);int N sc.nextInt();int M sc.nextInt();List<String> words new ArrayList<>(N);for (in…

关于TrustedInstaller权限

前言 我们在在删除某些文件时会发现权限不够的情况&#xff0c;那是因为自从 Windows Vista 以来&#xff0c;为了提升安全性&#xff0c;微软对于权限的把控越来越紧。为了对抗恶意软件随意修改系统文件&#xff0c;Trustedinstaller 应运而生。 各权限之间的关系 普通人:Us…

C++STL--------string

文章目录 一、STL介绍二、string1、constructor构造函数2、operator[]方括号运算符重载3、iterator迭代器4、reverse_iterator反向迭代器5、size和length6、capacity7、clear8、shrink_to_fit9、at10、push_back11、append 二、auto类型(C11)1、使用2、真正的价值 三、范围for(…

python全栈学习记录(十八)re、os和sys、subprocess

re、os和sys、subprocess 文章目录 re、os和sys、subprocess一、re1.正则字符2.正则表达式的使用3.group的使用4.贪婪匹配与惰性匹配5.其他注意事项 二、os和sys1.os2.sys 三、subprocess 一、re python中的re模块用来使用正则表达式&#xff0c;正则就是用一系列具有特殊含义…