1. 摘要
Grafana Labs开发了[Loki],这是一个受Prometheus启发的开源日志聚合系统。其目的是存储和索引日志数据,以便对由各种应用程序和系统生成的日志进行高效查询和分析。
在本文中,我们将为Spring Boot应用程序设置使用Grafana Loki的日志记录。Loki将收集并聚合应用程序日志,而Grafana将负责显示这些日志。
2.运行Loki和Grafana服务
首先,我们将启动Loki和Grafana服务,以便我们可以收集和观察日志。Docker容器将帮助我们更轻松地配置和运行这些服务。
首先,让我们在[docker-compose]文件中组合Loki和Grafana服务:
version: "3"
networks:
loki:
services:
loki:
image: grafana/loki:2.9.0
ports:
- "3100:3100"
command: -config.file=/etc/loki/local-config.yaml
networks:
- loki
grafana:
environment:
- GF_PATHS_PROVISIONING=/etc/grafana/provisioning
- GF_AUTH_ANONYMOUS_ENABLED=true
- GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
entrypoint:
- sh
- -euc
- | mkdir -p /etc/grafana/provisioning/datasources cat <<EOF > /etc/grafana/provisioning/datasources/ds.yaml apiVersion: 1 datasources: - name: Loki type: loki access: proxy orgId: 1 url: http://loki:3100 basicAuth: false isDefault: true version: 1 editable: false EOF /run.sh
image: grafana/grafana:latest
ports:
- "3000:3000"
networks:
- loki
接下来,我们需要使用 docker-compose 命令来启动服务:
docker-compose up
最后,我们确认一下这两个服务是否都已启动:
docker ps
211c526ea384 grafana/loki:2.9.0 "/usr/bin/loki -conf…" 4 days ago Up 56 seconds 0.0.0.0:3100->3100/tcp surajmishra_loki_1
a1b3b4a4995f grafana/grafana:latest "sh -euc 'mkdir -p /…" 4 days ago Up 56 seconds 0.0.0.0:3000->3000/tcp surajmishra_grafana_1
3. 使用Spring Boot配置Loki
启动Grafana和Loki服务后,我们需要配置应用程序将日志发送到它们。我们将使用[loki-logback-appender],它负责将日志发送到Loki聚合器以存储和索引日志。
首先,我们需要在pom.xml文件中添加loki-logback-appender:
<dependency>
<groupId>com.github.loki4j</groupId>
<artifactId>loki-logback-appender</artifactId>
<version>1.4.1</version>
</dependency>
其次,我们需要在src/main/resources文件夹下创建一个logging-spring.xml文件。该文件将控制日志记录行为,例如日志的格式、Loki服务的端点等,以适用于我们的Spring Boot应用程序:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="LOKI" class="com.github.loki4j.logback.Loki4jAppender">
<http>
<url>http://localhost:3100/loki/api/v1/push</url>
</http>
<format>
<label>
<pattern>app=${name},host=${HOSTNAME},level=%level</pattern>
<readMarkers>true</readMarkers>
</label>
<message>
<pattern>
{
"level":"%level",
"class":"%logger{36}",
"thread":"%thread",
"message": "%message",
"requestId": "%X{X-Request-ID}"
}
</pattern>
</message>
</format>
</appender>
<root level="INFO">
<appender-ref ref="LOKI" />
</root>
</configuration>
完成设置后,我们来编写一个简单的服务,该服务将以INFO级别记录数据:
@Service
class DemoService{
private final Logger LOG = LoggerFactory.getLogger(DemoService.class);
public void log(){
LOG.info("DemoService.log invoked");
}
}
4. 测试验证
让我们通过启动Grafana和Loki容器来进行实时测试,然后执行服务方法将日志推送到Loki。之后,我们将使用HTTP API查询Loki,以确认日志是否确实已被推送。关于如何启动Grafana和Loki容器,请参见前面的部分。
首先,我们执行DemoService.log()方法,它将调用Logger.info()。这将使用loki-logback-appender发送一条消息,Loki将收集这条消息:
DemoService service = new DemoService();
service.log();
接下来,我们将创建一个请求来调用Loki HTTP API提供的REST端点。这个GET API接受代表query(查询)、start(开始时间)和end(结束时间)的查询参数。我们将把这些参数添加到我们的请求对象中:
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
String query = "{level=\"INFO\"} |= `DemoService.log invoked`";
// Get time in UTC
LocalDateTime currentDateTime = LocalDateTime.now(ZoneOffset.UTC);
String current_time_utc = currentDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'"));
LocalDateTime tenMinsAgo = currentDateTime.minusMinutes(10);
String start_time_utc = tenMinsAgo.format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'"));
URI uri = UriComponentsBuilder.fromUriString(baseUrl)
.queryParam("query", query)
.queryParam("start", start_time_utc)
.queryParam("end", current_time_utc)
.build()
.toUri();
接下来,我们将使用请求对象来执行REST请求:
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.exchange(uri, HttpMethod.GET, new HttpEntity<>(headers), String.class);
现在我们需要处理响应并提取我们感兴趣的日志消息。我们将使用ObjectMapper来读取JSON响应并提取日志消息:
ObjectMapper objectMapper = new ObjectMapper();
List<String> messages = new ArrayList<>();
String responseBody = response.getBody();
JsonNode jsonNode = objectMapper.readTree(responseBody);
JsonNode result = jsonNode.get("data")
.get("result")
.get(0)
.get("values");
result.iterator()
.forEachRemaining(e -> {
Iterator<JsonNode> elements = e.elements();
elements.forEachRemaining(f -> messages.add(f.toString()));
});
最后,我们来断言响应中接收到的消息包含DemoService记录的消息:
assertThat(messages).anyMatch(e -> e.contains(expected));
5. 日志聚合与可视化
我们的服务日志由于配置了loki-logback-appender而被推送到Loki服务。我们可以通过在浏览器中访问http://localhost:3000(Grafana服务部署的位置)来可视化这些日志。
要查看已存储在Loki中并已建立索引的日志,我们需要使用Grafana。Grafana的数据源提供了可配置的连接参数用于Loki,我们需要在这里输入Loki的端点、认证机制等更多信息。
首先,我们来配置日志被推送的Loki端点:
一旦我们成功配置了数据源,让我们转到数据探索页面来查询我们的日志:
我们可以在Grafana中编写查询,以获取应用程序日志进行可视化。在我们的演示服务中,我们正在推送INFO日志,所以我们需要将其添加到过滤器中并运行查询:
一旦我们运行查询,我们将看到所有与我们搜索匹配的INFO日志:
6. 总结
在本文中,我们为Spring Boot应用程序设置了使用Grafana Loki的日志记录。我们还使用简单的逻辑(记录INFO日志和在Grafana中设置Loki数据源)通过单元测试和可视化验证了我们的设置。