使用 Loki 登入 Spring Boot
一、簡介
Grafana Labs 開發了 Loki,這是一個受Prometheus啟發的開源日誌聚合系統。其目的是儲存和索引日誌數據,以便於對不同應用程式和系統產生的日誌進行高效查詢和分析。
在本文中,我們將使用 Grafana Loki 為 Spring Boot 應用程式設定日誌記錄。 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
檔案。該檔案將控制 Spring Boot 應用程式的記錄行為,例如日誌格式、Loki 服務的端點等:
<?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();
接下來,我們使用 request 物件來執行 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
日誌:
六,結論
在本文中,我們使用 Grafana Loki 為 Spring Boot 應用程式設定日誌記錄。我們還透過單元測試和視覺化驗證了我們的設置,使用簡單的邏輯記錄INFO
日誌並在 Grafana 中設置 Loki 資料來源。
與往常一樣,範例程式碼可以在 GitHub 上取得。