Skip to content

모니터링 인프라 구축: ELK + Prometheus/Grafana + Slack 알림#193

Merged
unam98 merged 4 commits into
devfrom
feature/monitoring-infra
Jul 21, 2026
Merged

모니터링 인프라 구축: ELK + Prometheus/Grafana + Slack 알림#193
unam98 merged 4 commits into
devfrom
feature/monitoring-infra

Conversation

@unam98

@unam98 unam98 commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

작업 배경

  • 백엔드 모니터링/관측 가능성(Observability) 학습 및 실무 적용 목적으로, 로그 중앙화·메트릭 모니터링·장애 알림 체계가 전무했던 상태를 개선

변경 사항

영역 내용
Dockerfile, docker-compose.yml Gradle 멀티스테이지 빌드로 앱 컨테이너화, postgres/redis 포함 로컬 스택 구성
config/logging/MdcLoggingFilter 요청마다 traceId(UUID) 발급 후 MDC 저장, access log 라인 추가
logback-spring.xml 콘솔+파일(날짜별 롤링, gzip 압축, 30일 보관) 이중 appender, traceId 포함 로그 패턴
docker-compose.yml (ELK) Elasticsearch/Logstash/Kibana 추가, logstash.conf로 로그 grok 파싱 후 날짜별 인덱스 색인
application.yml(관리형, 외부) Actuator base-path 변경, 노출 엔드포인트를 health/info/metrics/prometheus로 제한
build.gradle micrometer-registry-prometheus 추가
prometheus.yml, docker-compose.yml(Prometheus/Grafana) 5초 간격 메트릭 스크랩, Grafana 데이터소스/대시보드/Alert Rule을 코드로 provisioning
grafana/provisioning/alerting/* 서버 다운 감지(up==0) Alert Rule, Slack Contact Point(웹훅은 커밋하지 않고 .env+런타임 sed 치환으로 주입)

영향 범위

  • 신규 인프라 구성 요소 추가이며 기존 API 로직/엔드포인트 동작에는 변경 없음
  • Actuator 엔드포인트 경로 및 노출 범위가 변경되어, 기존에 /actuator/*를 직접 참조하는 외부 연동(있다면)은 확인 필요
  • 로컬 docker-compose up으로 실행 시에만 영향, 실제 배포 파이프라인(CI/CD, EC2)은 이번 PR에서 변경하지 않음

검증 매트릭스

(테스트 코드 변경 없음 — 아래는 로컬 docker-compose 환경에서 수행한 수동 검증 내역)

검증 항목 결과
Actuator 헬스체크 (/internal-monitor/health) DB/Redis/디스크/ping 전부 UP 확인
Prometheus 스크랩 Target runnect-serverup 상태 확인
ELK 파이프라인 Logstash가 로그를 Elasticsearch에 정상 색인(1,000건 이상), Kibana Data View 생성 확인
Grafana 대시보드/데이터소스 Prometheus 연동 및 커스텀 대시보드 provisioning 확인
traceId 실동작 자체 서명 테스트 JWT로 인증 요청 실행 → 로그에 요청별 고유 traceId 기록 확인
Slack 알림 End-to-End docker stop으로 장애 시뮬레이션 → Prometheus up=0 감지 → Grafana Alert pendingfiring 전환 → Slack 채널 실제 수신 확인

Test Plan

  • 로컬 docker-compose up으로 전체 스택(8개 컨테이너) 기동 확인
  • Actuator/Prometheus/Grafana/ELK 파이프라인 개별 동작 확인
  • 장애 시뮬레이션을 통한 Slack 알림 End-to-End 확인
  • 실제 배포 환경(EC2) 반영 여부 및 방식 결정 (별도 논의 필요)

alh0409 added 2 commits July 21, 2026 05:14
- MDC traceId 필터 + Logback 롤링/gzip 압축 설정 추가
- Docker화: Dockerfile, docker-compose.yml (app+postgres+redis)
- ELK 스택 추가 (Elasticsearch/Logstash/Kibana) - 로그 중앙 검색
- Actuator 보안설정(base-path 변경, 노출 엔드포인트 제한) + Micrometer/Prometheus 연동
- Grafana 대시보드 및 서버 다운 감지 Alert Rule 프로비저닝 (Discord 웹훅 연동, URL은 플레이스홀더)

로컬 docker-compose 검증 완료. 실제 배포/CI-CD 반영 전 리뷰 필요.
- contact-points.yml.tpl(플레이스홀더만 포함)을 커밋, 실제 URL은 .env(gitignore)로 로컬 주입
- docker-compose grafana 서비스에서 컨테이너 기동 시 sed로 치환 후 contact-points.yml 생성(비커밋)
- .env.example로 필요한 환경변수 문서화
- 이전 커밋에 있던 실제 웹훅 URL은 push 전에 브랜치에서 제거됨(GitHub push protection에 의해 원격 유출은 없었음)
@unam98 unam98 self-assigned this Jul 21, 2026
@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@unam98, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 44 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a362b711-a064-407d-a45e-c6a3d5fc6c3a

📥 Commits

Reviewing files that changed from the base of the PR and between 3df77b3 and b76ffc4.

📒 Files selected for processing (9)
  • .github/CODEOWNERS
  • .github/workflows/dev-cd.yml
  • .github/workflows/dev-ci.yml
  • .github/workflows/prod-cd.yml
  • .github/workflows/prod-ci.yml
  • .gitignore
  • docker-compose.yml
  • grafana/provisioning/alerting/contact-points.yml
  • logstash.conf
📝 Walkthrough

Walkthrough

Adds a Docker Compose monitoring stack around the Spring Boot application, including Prometheus metrics, Grafana dashboards and alerts, Logstash/Elasticsearch log aggregation, MDC request tracing, rolling logs, and local Slack webhook configuration.

Changes

Runnect observability stack

Layer / File(s) Summary
Application logging and metrics
build.gradle, src/main/java/.../MdcLoggingFilter.java, src/main/resources/logback-spring.xml
Adds Micrometer Prometheus support, per-request MDC trace IDs, console logging, and rolling file logs.
Containerized runtime and services
Dockerfile, docker-compose.yml, .gitignore
Adds a multi-stage application image and Compose services for the application, PostgreSQL, Redis, shared networking, and persistent storage.
Metrics collection and dashboards
prometheus.yml, grafana/provisioning/datasources/*, grafana/provisioning/dashboards/*
Configures Prometheus scraping and provisions Grafana dashboards for JVM, CPU, HTTP, and server availability metrics.
Log aggregation pipeline
logstash.conf, docker-compose.yml
Adds Logstash file parsing and Elasticsearch indexing with Kibana connected to Elasticsearch.
Grafana alerting and Slack delivery
.env.example, docker-compose.yml, grafana/provisioning/alerting/*
Adds a server-down alert, notification policy, Slack contact-point templating, and webhook environment configuration.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant RunnectApp
  participant Prometheus
  participant Grafana
  participant Slack
  Prometheus->>RunnectApp: Scrape /internal-monitor/prometheus
  Grafana->>Prometheus: Evaluate server availability
  Prometheus-->>Grafana: Return up metric
  Grafana->>Slack: Deliver server-down notification
Loading

Suggested reviewers: yusuhwa-ve, rinrinpark, funnysunny08

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: building a monitoring stack with ELK, Prometheus/Grafana, and Slack alerts.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/monitoring-infra

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

- src/main/resources 디렉토리가 이미 존재할 때 mkdir이 실패하던 문제 수정 (mkdir -p로 변경)
  - 이번 PR에서 logback-spring.xml을 추가하며 resources 디렉토리가 최초로 생겨서 노출된 버그
- CODEOWNERS에서 더 이상 팀에 참여하지 않는 인원(funnysunny08, RinRinPARK, YuSuhwa-ve) 제거

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
grafana/provisioning/alerting/contact-points.yml (1)

1-12: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Remove or update this leftover Discord configuration.

The PR objectives indicate this stack uses Slack alerts, and your notification-policies.yml is correctly configured to route alerts to slack-alert. However, this static file defines a discord-alert with a dummy URL.

If this file is dynamically overwritten at runtime by a script using contact-points.yml.tpl, it should be removed from version control and added to .gitignore. Otherwise, Grafana provisioning will fail if it expects slack-alert but only finds this Discord configuration.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@grafana/provisioning/alerting/contact-points.yml` around lines 1 - 12, Remove
the static discord-alert contact point from contact-points.yml and ensure the
runtime-generated contact-points.yml is ignored by version control if
contact-points.yml.tpl overwrites it. Otherwise, replace the Discord receiver
with the expected slack-alert configuration so it matches
notification-policies.yml.
🧹 Nitpick comments (6)
build.gradle (1)

34-36: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Omit the explicit Micrometer version.

Spring Boot's dependency management automatically resolves the correct, compatible version of Micrometer for your specific Spring Boot version. Hardcoding the version can lead to compatibility conflicts during future Spring Boot upgrades.

♻️ Proposed fix
 	// Monitoring - Prometheus
-	implementation 'io.micrometer:micrometer-registry-prometheus:1.9.14'
+	implementation 'io.micrometer:micrometer-registry-prometheus'
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@build.gradle` around lines 34 - 36, Remove the explicit version from the
Micrometer Prometheus dependency in the build configuration, leaving Spring Boot
dependency management to select the compatible version.
.gitignore (1)

47-48: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Stop ignoring contact-points.yml if migrating to native variable interpolation.

If you adopt Grafana's native $__env{SLACK_WEBHOOK_URL} interpolation in contact-points.yml (as suggested in the docker-compose.yml review), the file will no longer contain the actual webhook secret. It can then be safely versioned and committed, eliminating the need to ignore it.

♻️ Proposed fix
 .env
-grafana/provisioning/alerting/contact-points.yml
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.gitignore around lines 47 - 48, Remove
grafana/provisioning/alerting/contact-points.yml from .gitignore so the
configuration can be versioned when it uses Grafana’s native
$__env{SLACK_WEBHOOK_URL} interpolation instead of storing the webhook secret.
docker-compose.yml (1)

11-26: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Add a healthcheck to Postgres to prevent the app from crashing on initial startup.

Using depends_on simply waits for the container to start, but not for the database inside it to be ready. If Postgres takes longer to initialize (e.g., on first run creating tables), the Spring Boot application will crash trying to connect.

Adding a healthcheck to Postgres and tying depends_on to service_healthy ensures reliable startup orchestration.

♻️ Proposed modifications
     depends_on:
-      - postgres
+      postgres:
+        condition: service_healthy
       - redis
     volumes:
       - ./logs:/app/logs
     networks:
       - runnect-net
 
   postgres:
     image: postgres:15
     container_name: runnect-postgres
     environment:
       POSTGRES_DB: runnect
       POSTGRES_USER: runnect
       POSTGRES_PASSWORD: runnect_local_password
+    healthcheck:
+      test: ["CMD-SHELL", "pg_isready -U runnect -d runnect"]
+      interval: 5s
+      timeout: 5s
+      retries: 5
     ports:
       - "5432:5432"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docker-compose.yml` around lines 11 - 26, Add a Postgres healthcheck under
the postgres service using pg_isready with the configured database and user,
including suitable interval, timeout, and retry settings. Update the application
service’s depends_on entry for postgres to require the service_healthy condition
while preserving its existing Redis dependency.
Dockerfile (2)

21-21: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Prevent potential Docker build failures due to multiple JAR files.

Using the *.jar wildcard to copy a source into a specific filename (app.jar) will cause a Docker build error (When using COPY with more than one source file, the destination must be a directory and end with a /) if the directory contains multiple .jar files. While safe here because only bootJar is executed, if standard build is run later (producing a -plain.jar), this step will break.

Consider being explicit with the target JAR name or excluding the plain jar.

♻️ Proposed fix to restrict the wildcard
-COPY --from=build /app/build/libs/*.jar app.jar
+COPY --from=build /app/build/libs/*-SNAPSHOT.jar app.jar
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Dockerfile` at line 21, Update the Dockerfile COPY source to select exactly
one executable JAR from the build output, excluding the plain JAR or using the
explicit boot JAR name, while preserving the destination filename app.jar.

17-19: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Run the container as a non-root user.

By default, the eclipse-temurin:11-jre image runs as the root user. Running the application as a non-root user is a security best practice that limits the potential impact of vulnerabilities within the container.

🛡️ Proposed refactor to use a restricted user
 FROM eclipse-temurin:11-jre
+
+RUN groupadd -r spring && useradd -r -g spring spring
+USER spring:spring
+
 WORKDIR /app
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Dockerfile` around lines 17 - 19, Configure the Dockerfile run stage to
execute the application as a dedicated non-root user instead of the image
default root user. Create or reuse a restricted user with ownership of /app,
then set the Docker USER before the application starts while preserving the
existing WORKDIR.

Source: Linters/SAST tools

src/main/java/org/runnect/server/config/logging/MdcLoggingFilter.java (1)

7-40: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use Spring's OncePerRequestFilter instead of standard Filter.

Extending OncePerRequestFilter guarantees that the filter is only executed once per request, properly handles asynchronous dispatches, and eliminates the need to manually cast the ServletRequest to HttpServletRequest (preventing a potential ClassCastException).

♻️ Proposed refactor
-import javax.servlet.Filter;
 import javax.servlet.FilterChain;
 import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.util.UUID;
+import org.springframework.web.filter.OncePerRequestFilter;
 
 /**
  * 요청마다 traceId를 발급해 MDC에 담아둔다.
  * 로그 패턴에 %X{traceId}를 포함시키면(logback-spring.xml), 여러 요청이 뒤섞인 로그에서도
  * 같은 traceId로 특정 요청의 흐름만 추적할 수 있다.
  */
 `@Slf4j`
 `@Component`
-public class MdcLoggingFilter implements Filter {
+public class MdcLoggingFilter extends OncePerRequestFilter {
 
     private static final String TRACE_ID_KEY = "traceId";
     private static final int TRACE_ID_LENGTH = 8;
 
     `@Override`
-    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
             throws IOException, ServletException {
         String traceId = UUID.randomUUID().toString().substring(0, TRACE_ID_LENGTH);
         try {
             MDC.put(TRACE_ID_KEY, traceId);
-            HttpServletRequest httpRequest = (HttpServletRequest) request;
-            log.info("{} {}", httpRequest.getMethod(), httpRequest.getRequestURI());
+            log.info("{} {}", request.getMethod(), request.getRequestURI());
             chain.doFilter(request, response);
         } finally {
             MDC.remove(TRACE_ID_KEY);
         }
     }
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/org/runnect/server/config/logging/MdcLoggingFilter.java` around
lines 7 - 40, Update MdcLoggingFilter to extend Spring’s OncePerRequestFilter
and move its trace-ID, request logging, and MDC cleanup logic into the
framework’s HTTP request method without manually casting ServletRequest.
Preserve the existing TRACE_ID_KEY handling, generated ID length, logging, and
finally-based cleanup.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docker-compose.yml`:
- Around line 96-101: Remove the Grafana service’s custom entrypoint and command
that run sed and overwrite the provisioning file. Update the contact-points
provisioning configuration to reference $__env{SLACK_WEBHOOK_URL} directly,
rename or use it as contact-points.yml instead of the template, and delete the
obsolete .tpl file if it is tracked.

In `@logstash.conf`:
- Around line 1-6: Update the file input configuration to add a multiline codec
that groups stack-trace continuation lines with each leading timestamped log
entry, ensuring exception records are passed as complete events to the existing
parsing pipeline.
- Around line 23-25: Update the index format in the Elasticsearch output
configuration to use calendar-year formatting by replacing the week-based YYYY
token with yyyy in the runnect-logs date pattern, while preserving the existing
month and day components.
- Around line 4-5: Update the file input configuration using start_position and
sincedb_path so Logstash stores the file offset on persistent storage instead of
/dev/null, preserving offsets across restarts and preventing old events from
being reindexed.
- Around line 16-18: Update the date filter parsing log_timestamp to explicitly
use the Asia/Seoul timezone by adding the timezone setting alongside match and
target, preserving the existing timestamp format and `@timestamp` target.

---

Outside diff comments:
In `@grafana/provisioning/alerting/contact-points.yml`:
- Around line 1-12: Remove the static discord-alert contact point from
contact-points.yml and ensure the runtime-generated contact-points.yml is
ignored by version control if contact-points.yml.tpl overwrites it. Otherwise,
replace the Discord receiver with the expected slack-alert configuration so it
matches notification-policies.yml.

---

Nitpick comments:
In @.gitignore:
- Around line 47-48: Remove grafana/provisioning/alerting/contact-points.yml
from .gitignore so the configuration can be versioned when it uses Grafana’s
native $__env{SLACK_WEBHOOK_URL} interpolation instead of storing the webhook
secret.

In `@build.gradle`:
- Around line 34-36: Remove the explicit version from the Micrometer Prometheus
dependency in the build configuration, leaving Spring Boot dependency management
to select the compatible version.

In `@docker-compose.yml`:
- Around line 11-26: Add a Postgres healthcheck under the postgres service using
pg_isready with the configured database and user, including suitable interval,
timeout, and retry settings. Update the application service’s depends_on entry
for postgres to require the service_healthy condition while preserving its
existing Redis dependency.

In `@Dockerfile`:
- Line 21: Update the Dockerfile COPY source to select exactly one executable
JAR from the build output, excluding the plain JAR or using the explicit boot
JAR name, while preserving the destination filename app.jar.
- Around line 17-19: Configure the Dockerfile run stage to execute the
application as a dedicated non-root user instead of the image default root user.
Create or reuse a restricted user with ownership of /app, then set the Docker
USER before the application starts while preserving the existing WORKDIR.

In `@src/main/java/org/runnect/server/config/logging/MdcLoggingFilter.java`:
- Around line 7-40: Update MdcLoggingFilter to extend Spring’s
OncePerRequestFilter and move its trace-ID, request logging, and MDC cleanup
logic into the framework’s HTTP request method without manually casting
ServletRequest. Preserve the existing TRACE_ID_KEY handling, generated ID
length, logging, and finally-based cleanup.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c13f36a8-c991-418a-a529-8b967f50d444

📥 Commits

Reviewing files that changed from the base of the PR and between bd9216a and 3df77b3.

📒 Files selected for processing (16)
  • .env.example
  • .gitignore
  • Dockerfile
  • build.gradle
  • docker-compose.yml
  • grafana/provisioning/alerting/contact-points.yml
  • grafana/provisioning/alerting/contact-points.yml.tpl
  • grafana/provisioning/alerting/notification-policies.yml
  • grafana/provisioning/alerting/rules.yml
  • grafana/provisioning/dashboards/dashboard.yml
  • grafana/provisioning/dashboards/json/runnect-overview.json
  • grafana/provisioning/datasources/prometheus.yml
  • logstash.conf
  • prometheus.yml
  • src/main/java/org/runnect/server/config/logging/MdcLoggingFilter.java
  • src/main/resources/logback-spring.xml

Comment thread docker-compose.yml Outdated
Comment thread logstash.conf
Comment thread logstash.conf Outdated
Comment thread logstash.conf
Comment thread logstash.conf Outdated
- Grafana 웹훅 주입을 커스텀 entrypoint(sed) 대신 네이티브 $__env{} 문법으로 단순화 (bind mount 권한 이슈 회피)
- Logstash: 멀티라인 코덱 추가로 스택트레이스가 leading 로그 라인과 분리되지 않도록 수정
- Logstash: sincedb_path를 /dev/null에서 기본 영구 경로로 변경 + 볼륨 추가 (재시작 시 전체 재색인 방지)
- Logstash: date filter에 timezone => Asia/Seoul 명시 (Logback이 KST naive 타임스탬프를 기록하므로)
- Logstash: 인덱스 패턴을 YYYY(week-based year)에서 yyyy(calendar year)로 수정 (연말/연초 오분류 방지)

모든 수정사항은 로컬 docker-compose로 재검증 완료:
- $__env{} 방식으로도 실제 장애 시뮬레이션 → Slack 알림 firing 확인
- 스택트레이스 포함 500 에러가 하나의 이벤트(6868자)로 정상 색인됨 확인
- 인덱스명이 runnect-logs-2026.07.21 형식(소문자)으로 생성됨 확인
@unam98
unam98 merged commit 86fe2f8 into dev Jul 21, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants