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_13. 使用 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 文件夾下創建一個 logback-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 服務(使用 loki-logback-appender 配置)推送的。 您可以通過訪問 http://localhost:3000(Grafana 服務部署位置)在瀏覽器中進行可視化。
要查看已存儲和索引在 Loki 中的日誌,我們需要使用 Grafana。 Grafana 數據源提供用於 Loki 的可配置連接參數,其中需要輸入 Loki 端點、身份驗證機制等。
首先,讓我們配置日誌推送到的 Loki 端點:
在成功配置數據源後,讓我們轉到數據頁面以查詢我們的日誌:
我們可以編寫 查詢 以將應用程序日誌導入 Grafana 以進行可視化。 在我們的演示服務中,我們正在推送 INFO 日誌,因此我們需要將其添加到我們的過濾器並運行查詢:
在運行查詢後,您將看到所有匹配搜索的 INFO 日誌:
6. 結論
本文檔介紹瞭如何為 Spring Boot 應用程序設置日誌記錄,並使用 Grafana Loki。我們還通過單元測試和可視化驗證了配置,使用了簡單的邏輯,用於記錄 INFO 級別的日誌,並在 Grafana 中設置 Loki 數據源。