인프라/Monitoring

Loki 로 로그 저장하고, Grafana 로 시각화하기

Griotold 2025. 1. 2. 12:30

1. Loki

Loki 는 Grafana Labs 에서 개발한 수평 확장 가능한 로그 집계 시스템이다. 

Grafana와 쉽게 연동 되고, Prometheus, K8s와 궁합이 잘 맞는다.

Elasticsearch과 비교하면, 성능과 제공하는 기능 측면에서 부족하긴 하지만, 

시스템 상황을 확인하는 데는 가성비 있는 도구이다.

 

2. Spring 프로젝트 설정

 

build.gradle

dependencies {
	implementation 'com.github.loki4j:loki-logback-appender:1.5.1' //추가

	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	runtimeOnly 'com.h2database:h2'
	implementation 'org.springframework.boot:spring-boot-starter-actuator'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	compileOnly 'org.projectlombok:lombok'
	annotationProcessor 'org.projectlombok:lombok'

	// prometheus
	runtimeOnly 'io.micrometer:micrometer-registry-prometheus'

	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

맨 위에 loki 의존성을 추가한다.

 

loki-config.yml

# https://grafana.com/docs/loki/latest/setup/install/docker/ 문서를 참고 합니다.
auth_enabled: false

server:
  http_listen_port: 3100
  grpc_listen_port: 9096

common:
  instance_addr: 127.0.0.1
  path_prefix: /tmp/loki
  storage:
    filesystem:
      chunks_directory: /tmp/loki/chunks
      rules_directory: /tmp/loki/rules
  replication_factor: 1
  ring:
    kvstore:
      store: inmemory

query_range:
  results_cache:
    cache:
      embedded_cache:
        enabled: true
        max_size_mb: 100

schema_config:
  configs:
    - from: 2020-10-24
      store: tsdb
      object_store: filesystem
      schema: v13
      index:
        prefix: index_
        period: 24h

ruler:
  alertmanager_url: http://localhost:9093

# By default, Loki will send anonymous, but uniquely-identifiable usage and configuration
# analytics to Grafana Labs. These statistics are sent to https://stats.grafana.org/
#
# Statistics help us better understand how Loki is used, and they show us performance
# levels for most users. This helps us prioritize features and documentation.
# For more information on what's sent, look at
# https://github.com/grafana/loki/blob/main/pkg/analytics/stats.go
# Refer to the buildReport method to see what goes into a report.
#
# If you would like to disable reporting, uncomment the following lines:
#analytics:
#  reporting_enabled: false

 

Loki의 기본 Port는 3100이다.

위와 같이 설정하면 스프링 부트를 실행하면, Console에 더 이상 로그가 쌓이지 않는다.

 

3. Docker에 Loki 설치

docker run --name loki -v C:\dev\monitor\loki:/mnt/config -p 3100:3100 grafana/loki:3.2.1 --config.file=/mnt/config/loki-config.yml

 

loki-config.yml 파일이 있는 곳은 C:\dev\monitor\loki 폴더이다.

이미지 내에 있는 config 연결 시켜준다.

Port는 기본 Port 3100을 연결 시켜준다.

 

Loki 실행 확인

 

 

4. 로그 시뮬레이션

SampleController

package com.monitor.sample;

import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;

@Slf4j
@RestController
public class SampleController {

    @GetMapping("/")
    public String hello(HttpServletResponse response) throws IOException {
        log.info("Attempted access to / endpoint in 403 Forbidden");
        response.sendError(HttpServletResponse.SC_FORBIDDEN, "Access Denied");
        return null;
    }

    @GetMapping("/forbidden-test")
    public String hello2(HttpServletResponse response) throws IOException {
        log.error("Attempted access to / endpoint in 403 Forbidden");
        response.sendError(HttpServletResponse.SC_FORBIDDEN, "Access Denied");
        return null;
    }


}

 

INFO, ERROR 레벨의 로그를 발생시키는 메서드를 만들었다.

위의 메소드들을 여러 번 호출해서 로그를 쌓는다.

 

5. Grafana 에서 로키 연결

 

Loki를 Docker로 띄웠기 때문에 host.docker.internal 로 연결시켜준다.

 

6. 로그 확인해보기

Explore 에서 로그를 확인해보자

SampleController 에서 발생한 INFO를 쿼리했다.

INFO 레벨의 로그가 7개 발생했음을 확인할 수 있다.

아래를 보면, 로그들을 구체적으로 볼 수 있다.

 

7. Loki 대시보드 만들기

 

7 - 1. 로그 메세지 대시보드

오른쪽에 패널 유형을 Logs로 지정하면 구체적인 로그를 확인할 수 있는 대시보드를 만들 수 있다.

 

7 - 2 로그 차트 대시보드

 

패널 유형을 Time Series로 지정

sum(rate({app="my-app} | json | logger_name="com.monitor.sample.* | level="INFO" [1m]))
sum(rate({app="my-app} | json | logger_name="com.monitor.sample.* | level="ERROR" [1m]))

 

INFO 레벨과 ERROR 레벨을 차트로 확인할 수 있도록 쿼리를 작성했다.

ERROR 레벨의 로그가 0.37까지 찍혔는데,

이것의 의미는 초당 0.37개의 로그가 발생했다는 의미가 된다.

 

 

References

https://grafana.com/docs/loki/latest/get-started/overview/

https://devocean.sk.com/blog/techBoardDetail.do?ID=163964

https://www.persistent.com/blogs/log-aggregation-using-grafana-loki-stack/