本文基于实际踩坑经历详细记录在Ubuntu系统上部署LokiGrafana并通过Spring Boot应用将日志直接推送至Loki的全过程。文中包含完整的配置示例、常见错误及解决方案以及如何在Grafana中利用上下文功能快速定位问题。1. 为什么选择Loki相比传统的ELKElasticsearchLogstashKibana栈Loki采用标签索引而非全文索引存储成本更低查询速度更快尤其适合Kubernetes环境和微服务架构。配合Grafana的可视化能力可以轻松实现日志的聚合、检索和监控。本文方案特点使用Loki4j作为Logback AppenderSpring Boot应用直接推送日志无需额外部署Promtail。所有组件Loki、Grafana通过Docker Compose一键启动。覆盖从部署到排错的全流程尤其针对权限、配置文件等常见坑点给出明确解决办法。2. 环境准备操作系统Ubuntu 26.04其他版本同理Docker及Docker Compose已安装Spring Boot项目推荐3.x, 2/3/4均可JDK 8检查Docker版本docker --version docker compose version若未安装可参考官方文档安装Docker Engine和Compose。3. 部署Loki和Grafana3.1 创建项目目录mkdir -p ~/loki-stack cd ~/loki-stack3.2 编写docker-compose.yml经过多次调整最终使用不含配置文件挂载的简洁版本避免因配置文件缺失或权限问题导致容器无法启动。关键点数据目录权限必须正确。创建docker-compose.ymlnanodocker-compose.yml(ctrlo save, press enter comfirm , ctrlx exit)networks: loki-network: services: loki: image: grafana/loki:latest container_name: loki ports: - 3100:3100 volumes: - ./loki/data:/loki # 数据持久化目录 networks: - loki-network # 不指定command使用镜像默认配置 grafana: image: grafana/grafana:latest container_name: grafana ports: - 3000:3000 environment: - GF_SECURITY_ADMIN_PASSWORDadmin - GF_AUTH_ANONYMOUS_ENABLEDfalse volumes: - ./grafana/data:/var/lib/grafana networks: - loki-network depends_on: - loki注意此处去掉了version字段避免警告并未挂载loki/conf让Loki使用内置默认配置。数据目录./loki/data和./grafana/data需提前创建并赋予正确权限。3.3 准备数据目录并解决权限问题Loki和Grafana容器内均以非root用户运行Loki用户uid约为10001Grafana用户uid为472需要宿主机目录具有写权限。快速解决测试环境# 创建目录 mkdir -p ./loki/data ./grafana/data # 赋予所有用户读写执行权限777 chmod 777 ./loki/data ./grafana/data更安全的做法生产推荐# 为Loki目录设置uid 10001可通过docker run --rm grafana/loki id -u确认 sudo chown -R 10001:10001 ./loki/data # 为Grafana目录设置uid 472 sudo chown -R 472:472 ./grafana/data3.4 启动服务docker compose up -d检查容器状态docker ps验证Loki是否就绪curl http://localhost:3100/ready返回ready表示成功。也可查看日志docker logs loki docker logs grafana若遇到权限错误如mkdir /loki/rules: permission denied或GF_PATHS_DATA is not writable请按上述权限调整步骤重新执行。4. Spring Boot集成Loki4j4.1 添加Maven依赖在pom.xml中加入dependency groupIdcom.github.loki4j/groupId artifactIdloki-logback-appender/artifactId version1.4.1/version /dependency?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd modelVersion4.0.0/modelVersion parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version3.5.0/version relativePath/ /parent groupIdcom.example/groupId artifactIdspringboot-loki/artifactId version1.0.0/version nameSpring Boot Loki Integration/name descriptionSpring Boot 3.5 project with Loki4j integration/description properties java.version17/java.version maven.compiler.source17/maven.compiler.source maven.compiler.target17/maven.compiler.target project.build.sourceEncodingUTF-8/project.build.sourceEncoding /properties dependencies !-- Spring Boot Web Starter -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- Loki4j Logback Appender -- dependency groupIdcom.github.loki4j/groupId artifactIdloki-logback-appender/artifactId version2.0.3/version scopecompile/scope /dependency !-- Lombok (可选简化代码) -- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency /dependencies build plugins plugin groupIdorg.springframework.boot/groupId artifactIdspring-boot-maven-plugin/artifactId configuration excludes exclude groupIdorg.projectlombok/groupId artifactIdlombok/artifactId /exclude /excludes /configuration /plugin /plugins /build /project4.2 配置Logback在src/main/resources下创建或修改logback-spring.xml?xml version1.0 encodingUTF-8? configuration !-- 控制台输出配置 -- appender nameCONSOLE classch.qos.logback.core.ConsoleAppender encoder pattern%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n/pattern charsetUTF-8/charset /encoder /appender !-- Loki4j Appender 配置 -- appender nameLOKI classcom.github.loki4j.logback.Loki4jAppender http urlhttp://localhost:3100/loki/api/v1/push/url connectionTimeoutMs5000/connectionTimeoutMs requestTimeoutMs10000/requestTimeoutMs /http format label patternappspringboot-loki,envlocal/pattern readMarkerstrue/readMarkers /label message pattern${FILE_LOG_PATTERN:-%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n}/pattern /message sortByTimetrue/sortByTime /format batch maxItems1000/maxItems maxWaitMs5000/maxWaitMs maxBytes10485760/maxBytes /batch send minLevelINFO/minLevel retry maxRetries3/maxRetries backoffMs1000/backoffMs /retry /send /appender !-- 异步Loki Appender -- appender nameASYNC_LOKI classch.qos.logback.classic.AsyncAppender appender-ref refLOKI/ queueSize512/queueSize discardingThreshold0/discardingThreshold neverBlocktrue/neverBlock /appender !-- 根日志配置 -- root levelINFO appender-ref refCONSOLE/ appender-ref refASYNC_LOKI/ /root !-- 应用特定日志配置 -- logger namecom.example.springbootloki levelDEBUG additivityfalse appender-ref refCONSOLE/ appender-ref refASYNC_LOKI/ /logger /configuration配置要点url指向Loki的推送端点若Loki不在本机替换为实际IP。标签application和level是查询的核心建议包含应用名、环境等。消息体可包含自定义字段便于后续解析。在application.properties中定义应用名spring: application: name: springboot-loki4.3 编写测试接口创建测试Controller验证日志推送package com.example.springbootloki.controller; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Map; RestController RequestMapping(/api/logs) public class LogTestController { //https://zhengkai.blog.csdn.net/ private static final Logger logger LoggerFactory.getLogger(LogTestController.class); /** * 基本日志测试接口 */ GetMapping(/test) public MapString, String testLogs() { logger.info(这是一条INFO级别日志 - 测试Loki集成); logger.debug(这是一条DEBUG级别日志 - 调试信息); logger.warn(这是一条WARN级别日志 - 警告信息); logger.error(这是一条ERROR级别日志 - 错误信息); return Map.of( status, success, message, 日志已写入Loki, timestamp, String.valueOf(System.currentTimeMillis()) ); } }启动Spring Boot应用访问/test日志便会推送至Loki。http://localhost:8080/api/logs/test5. 在Grafana中配置Loki数据源浏览器访问http://你的Ubuntu IP:3000登录admin/admin。点击左侧齿轮图标Configuration→Data Sources→Add data source。选择Loki。URL 填写http://loki:3100因为Grafana与Loki在同一Docker网络可使用服务名。点击Save Test显示成功即可。6. 查询日志与查看上下文6.1 基本查询进入Explore页面选择Loki数据源。使用标签过滤器例如{appdefault}查看该应用所有日志。加上levelWARN过滤警告级别。使用| 警告信息进行文本搜索。6.2 查看某条日志的上下文关键功能当你在日志列表中发现一条感兴趣的日志如WARN想查看它之前和之后的日志以了解触发背景时可以使用Grafana的上下文功能鼠标悬停在目标日志行上右侧会出现操作图标。点击“显示上下文”按钮两条横线带箭头英文版是Content。默认会展示该条日志前后各10行日志你可以调整行数例如20行点击加载。这样就能快速分析出WARN发生前的DEBUG或INFO日志从而定位问题根源。原理上下文查询基于相同标签流和时间顺序因此务必确保标签组合能唯一标识一个日志流例如包含application、host、container等。7. 常见问题与排错7.1 Loki容器启动失败日志显示config file not found原因未正确挂载配置文件或镜像默认路径不存在。解决移除volumes中挂载配置目录的行让容器使用内置默认配置。7.2 权限错误permission denied原因宿主机数据目录权限不足。解决chmod 777或chown为容器内用户uidLoki uid≈10001Grafana uid472。7.3 Spring Boot日志未出现在Loki检查Loki URL是否正确确保网络可达。查看Spring Boot日志中是否有连接异常。检查Loki容器是否正常运行docker logs loki。7.4 Grafana无法连接Loki数据源确保Grafana和Loki在同一网络URL使用服务名http://loki:3100。检查Loki端口是否暴露可用curl http://localhost:3100/ready测试。8. 总结通过本文的实战步骤你已成功在Ubuntu上部署LokiGrafana并让Spring Boot应用通过Loki4j直接将结构化日志推送至Loki。整个过程解决了配置文件缺失、权限不足等典型问题并掌握了利用Grafana上下文功能快速回溯日志的技巧。这套轻量级日志方案适合开发和测试环境也可平滑迁移至生产配合Promtail收集文件日志、使用对象存储等。希望本文能帮助你高效构建日志系统提升问题排查效率。附录清理所有容器和数据cd ~/loki-stack docker compose down #只是shutdown docker compose down -v # -v 会删除数据卷谨慎使用扩展阅读Loki官方文档Loki4j GitHub​ # ----------------------------------------------------- # - Powered by Moshow郑锴 # - Might the holy code be with you! # ----------------------------------------------------- # 公众号 软件开发大百科 # CSDN https://zhengkai.blog.csdn.net # Github https://github.com/moshowgame本文基于Ubuntu 26.04实践其他Linux发行版类似。如有问题欢迎留言交流。