前言

日志在任何一个web应用中都是不可忽视的存在,它已经成为大部分系统的标准组成部分。搭建日志可视化的主要目的是为了更好地理解和管理应用程序产生的大量日志数据。日志数据对于了解系统的运行状态、诊断问题以及优化性能至关重要。笔者之前有一篇文章有介绍搭建ELK日志可视化系统,但是这个日志系统太重太麻烦,安装的东西很多,本文介绍一款新的轻量级日志系统,基本组成部分为这几个组件——springboot+Loki+Loki4j+Grafana,下面笔者分别介绍Loki、Loki4j、Grafana这几个组件,然后创建一个简单的springboot项目,来演示如何集成使用这几个组件搭建一个轻量级的日志系统。

本文源码仓库地址:Gitee

本文涉及到的组件资源已全部上传,请于文章顶部下载


一、日志组件介绍

1.1 Loki组件

Loki 是一个开源的日志聚合系统,由 Grafana Labs 开发和维护。Loki 的设计目标是提供一种高效、可扩展的方式来收集、索引和查询结构化日志数据。Loki 专注于日志数据,与传统的日志管理系统(如 ELK Stack)相比,它采用了不同的方法来处理日志数据。

Loki 的特点

  • 标签查询

    • Loki 使用类似于 Prometheus 的标签查询模型,允许用户通过标签来过滤和聚合日志数据。
    • 这种模型非常适合大规模的日志数据查询和分析。
  • 无索引日志存储

    • Loki 不对原始日志数据进行全文索引,而是将日志数据按流存储,并使用标签来标识每个流。
    • 这种方法降低了存储成本,并提高了查询性能。
  • 水平可扩展性

    • Loki 能够水平扩展,通过增加更多的节点来处理更多的日志数据。
    • 这使得 Loki 非常适合处理大规模的日志数据集。
  • 高可用性

    • Loki 支持高可用性部署,可以配置多个实例来确保数据的持久性和服务的连续性。
  • 多租户支持

    • Loki 支持多租户部署,不同的组织或团队可以在同一套基础设施上独立管理自己的日志数据。
  • 与 Grafana 集成

    • Loki 与 Grafana 紧密集成,可以直接在 Grafana 中查询和可视化 Loki 中的日志数据。
    • 这使得 Loki 成为了 Grafana 生态系统的一个重要组成部分。
  1. 低资源消耗
    • 相比于 ELK Stack,Loki 对资源的需求较低,更适合中小规模的团队使用。

Loki 的架构

Loki 的架构主要包括以下几个组件:

  1. Loki Server

    • 主服务器,负责接收日志数据、存储数据和处理查询。
  2. Promtail

    • 一个轻量级的日志代理,用于收集日志文件并将日志数据发送给 Loki。
  3. Grafana

    • 用于查询和显示 Loki 中的日志数据的界面。
  4. Storage

    • Loki 使用对象存储(如 S3 或 GCS)来持久化日志数据,以降低存储成本。

使用场景

Loki 特别适用于以下场景:

  • 大规模日志收集:适用于需要处理大量日志数据的环境。
  • 实时日志查询:需要快速查询和分析日志数据的场景。
  • 低成本日志存储:希望降低日志存储成本的项目。

总之,Loki 是一个现代的日志管理系统,它简化了日志数据的收集、存储和查询过程,特别适合那些需要高性能、可扩展性和成本效益的日志管理解决方案。

1.2 Loki4j组件

Loki4j 是一个基于 Java 的日志框架,它提供了一种简单的方式来将日志数据发送到 Loki。Loki4j 旨在与 Loki 日志聚合系统无缝集成,使得 Java 应用程序能够轻松地将日志数据发送到 Loki 以进行集中管理和分析。

Loki4j 的特点

  1. 易于集成

    • Loki4j 提供了一个简单的 API,可以很容易地与现有的 Java 应用程序集成。
    • 支持 SLF4J 和 Logback 等流行的 Java 日志框架。
  2. 标签支持

    • Loki4j 支持 Loki 的标签查询模型,允许用户在日志消息中添加标签,以便更好地组织和查询日志数据。
  3. 异步日志记录

    • Loki4j 支持异步日志记录,可以提高应用程序的性能,因为它不会阻塞应用程序线程。
  4. 配置灵活

    • Loki4j 提供了灵活的配置选项,可以根据需要定制日志级别、日志格式等。
  5. 错误处理

    • Loki4j 包含了错误处理机制,可以处理网络问题或其他异常情况,确保日志数据能够可靠地发送到 Loki。

1.3 Grafana

Grafana 是一款开源的数据可视化和分析平台,主要用于可视化时间和序列数据,如监控指标、日志文件、应用程序跟踪等。它支持多种数据源,包括Prometheus、InfluxDB、MySQL、PostgreSQL、Elasticsearch、CloudWatch、Graphite等,以及自定义数据源。Grafana被广泛应用于IT基础架构监控、应用性能监控(APM)、物联网(IoT)数据分析、业务指标监控等多个领域。

主要特点

  1. 直观的界面:Grafana提供了一个用户友好的界面,用于创建和编辑仪表盘,使得数据可视化变得非常简单,无需编程知识。

  2. 丰富的图表类型:支持多种图表类型,如折线图、柱状图、饼图、热力图、仪表盘、表格等,适合不同类型的视觉展示需求。

  3. 动态和交互式面板:面板可以配置为具有时间选择器、下拉菜单、查询编辑器等交互元素,使用户能更灵活地探索数据。

  4. 报警功能:Grafana支持基于规则的报警系统,当数据达到预设的阈值时,可以通过电子邮件、Slack、PagerDuty等多种方式发送通知。
    注释和事件:用户可以在图表上添加注释,记录特定时间点发生的重要事件,有助于理解数据波动的原因。

  5. 模板变量:通过使用模板变量,可以创建动态仪表盘,允许用户在不修改仪表盘的情况下,快速切换查看不同数据源或视角。

  6. 插件生态:Grafana拥有强大的社区支持,提供了大量的数据源插件、面板插件和应用插件,大大扩展了其功能和适用场景。

  7. API和脚本支持:Grafana提供了完善的API接口,允许用户通过脚本和外部应用自动化创建和管理仪表盘、数据源和用户权限。

  8. 团队协作和权限管理:支持多用户访问,可以为不同用户或团队分配不同的权限,促进团队间的协作。

应用场景

  • IT运维监控:实时监控服务器性能、网络流量、应用程序健康状况等。

  • 业务分析:展现用户行为、销售数据、营销活动效果等业务指标。

  • 物联网数据可视化:分析设备传感器数据,监控物联网系统运行状态。

  • 日志分析:结合Loki等日志管理系统,进行日志数据的实时查询和可视化展示。

Grafana凭借其灵活性和易用性,成为了数据分析和监控领域的首选工具之一,帮助企业更好地理解数据、发现问题和制定决策。

二、组件下载安装运行

笔者是在windows系统下演示,所用的组件都是windows系统下的,linux系统中安装配置自行网上搜索教程即可。

Loki下载安装运行

下载地址:Loki下载地址

在这里插入图片描述

把下载的loki-windows-amd64.exe.zip压缩包解压,得到loki-windows-amd64.exe文件,进入这个文件中可以看到有一个可执行的程序(不用去点,点了也会闪退),创建一个config文件夹

在这里插入图片描述
再进入config文件夹中,创建loki-config.yaml配置文件,文件内容如下:

server:
  # Loki 服务监听的 HTTP 端口号
  http_listen_port: 3100

schema_config:
  configs:
    - from: 2024-07-01
      # 使用 BoltDB 作为索引存储
      store: boltdb
      # 使用文件系统作为对象存储
      object_store: filesystem
      # 使用 v11 版本的 schema
      schema: v11
      index:
        # 索引前缀
        prefix: index_
        # 索引周期为 24 小时
        period: 24h

ingester:
  lifecycler:
    # 设置本地 IP 地址
    address: 127.0.0.1
    ring:
      kvstore:
        # 使用内存作为 kvstore
        store: inmemory
      # 复制因子设置为 1
      replication_factor: 1
    # 生命周期结束后的休眠时间
    final_sleep: 0s
  # chunk 的空闲期为 5 分钟
  chunk_idle_period: 5m
  # chunk 的保留期为 30 秒
  chunk_retain_period: 30s

storage_config:
  boltdb:
    # BoltDB 的存储路径
    directory: D:\software\loki-windows-amd64.exe\BoltDB
  filesystem:
    # 文件系统的存储路径
    directory: D:\software\loki-windows-amd64.exe\fileStore

limits_config:
  # 不强制执行指标名称
  enforce_metric_name: false
  # 拒绝旧样本
  reject_old_samples: true
  # 最大拒绝旧样本的年龄为 168 小时
  reject_old_samples_max_age: 168h
  # 每个用户每秒的采样率限制为 32 MB
  ingestion_rate_mb: 32
  # 每个用户允许的采样突发大小为 64 MB
  ingestion_burst_size_mb: 64

chunk_store_config:
  # 最大可查询历史日期为 28 天(672 小时),这个时间必须是 schema_config 中 period 的倍数,否则会报错
  max_look_back_period: 672h

table_manager:
  # 启用表的保留期删除功能
  retention_deletes_enabled: true
  # 表的保留期为 28 天(672 小时)
  retention_period: 672h

注意yaml文件中的 BoltDB 的存储路径和 文件系统的存储路径,这个自行替换为自己的路径

yaml文件内容保存好后,使用cmd进入 loki-windows-amd64.exe可执行程序所在目录下,执行下面的启动命令(这个启动命令是以后启动Loki组件的专用命令,指定了配置文件

loki-windows-amd64.exe --config.file=config/loki-config.yaml

执行结果如下
在这里插入图片描述

下面的这几个文件夹都是根据配置自动生成的

在这里插入图片描述

注意:yaml配置文件配置的Loki监听端口号如下,这个记好,后面集成时需要使用

 # Loki 服务监听的 HTTP 端口号
 http_listen_port: 3100

Grafana下载安装运行

下载地址:Grafana下载

在这里插入图片描述

把下载后的压缩文件夹grafana-enterprise-11.1.0.windows-amd64.zip解压得到grafana-v11.1.0文件,进入bin目录找到grafana-server.exe程序双击运行

在这里插入图片描述
会自动弹出一个cmd窗口,打印出花花绿绿的日志信息,很好看

在这里插入图片描述
程序运行成功后访问:http://localhost:3000,初始账户: admin/admin 登录后需重置密码,笔者没有改变密码,还是使用原来的,下面是登录进去后的操作

添加Loki数据源

在这里插入图片描述
选择添加数据源后,出现了很多数据源,找到Loki点击即可

设置名称和监听的数据源地址

之前安装Loki配置文件里设置的:http://localhost:3100

在这里插入图片描述

设置Header和Value

Header:X-Scope-OrgID
Value:user1

在这里插入图片描述

save & test

在这里插入图片描述

注意:如果save&test报错可能是Loki服务初始化还未完成,验证Loki初始化是否完成访问:http://localhost:3100/ready,如果显示ready即初始化完成

在这里插入图片描述


三、创建springboot项目

在这里插入图片描述
添加pom依赖

 <!--Loki 日志收集-->
        <dependency>
            <groupId>com.github.loki4j</groupId>
            <artifactId>loki-logback-appender</artifactId>
            <version>1.5.1</version>
        </dependency>
        <!--Loki 日志发送http请求和响应工具-->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.13</version>
        </dependency>

在Resources文件夹下创建**logback-spring.xml**文件

<?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="logback.log-file-dir" defaultValue="log"/>

    <!-- 控制台输出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${log.pattern}</pattern>
        </encoder>
    </appender>

    <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="my-app"/>
    <springProperty scope="context" name="hostIp" source="loki.label.host-ip" defaultValue="localhost"/>
    <springProperty scope="context" name="orgId" source="loki.org-id" defaultValue="default-org"/>

    <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</pattern>
            </label>
            <message>
                <pattern>
                    {"timestamp": "%d{yyyy-MM-dd HH:mm:ss.SSS}", "level": "%level", "logger": "%logger{36}", "thread": "%thread", "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}/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}/error.%d.log</fileNamePattern>
            <!--保留30天日志-->
            <maxHistory>30</maxHistory>
        </rollingPolicy>
    </appender>

    <root level="info">
        <appender-ref ref="STDOUT" />
        <appender-ref ref="fileInfoLog" />
        <appender-ref ref="fileErrorLog" />
        <appender-ref ref="ASYNC_LOKI" />
    </root>

</configuration>

application.yml配置文件内容如下:

spring:
  application:
     name: Grafana-Project

# Loki 日志配置
loki:
  # Loki 服务的 URL,用于推送日志数据
  url: http://localhost:3100/loki/api/v1/push

  # 标签配置,用于标识日志来源的额外信息
  label:
    # 环境标签,标识当前运行的环境,例如开发环境
    env: dev
    # 服务名称标签,标识日志来源的服务名称
    job-name: my-service
    # 主机 IP 标签,标识日志来源的主机 IP 地址
    host-ip: localhost

  # 组织 ID,用于多租户环境中标识日志所属的组织
  org-id: user1


启动类加上如下日志
在这里插入图片描述
启动后去Grafana查询日志信息

在这里插入图片描述

由上图可以看到日志已经发送到Grafana了,但是只有log方式记录的才会,控制台输出的不会记录

注意:刚启动组件时,后台需要一定时间加载,这时候是无法记录日志的。

总结

本篇文章详细介绍了使用springboot+Loki+Loki4j+Grafana搭建轻量级日志系统的步骤,整体环境为windows环境,Linux环境下的组件安装配置也类似。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐