Skip to content

Commit d571572

Browse files
committed
feat(infra): dev, prod integrate centralized logging with Logstash and Filebeat
1 parent 128ab7e commit d571572

File tree

12 files changed

+748
-27
lines changed

12 files changed

+748
-27
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,4 @@ memory-infra/docker/**/data/*
4242
.env
4343
CLAUDE.md
4444
settings.local.json
45-
./logs
45+
./logs/*

memory-api/src/main/resources/logback-spring.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<configuration>
33
<!-- 공통 변수 정의 -->
4-
<springProfile name="!prod">
4+
<springProfile name="local">
55
<property name="LOG_PATH" value="./logs"/>
66
</springProfile>
7-
<springProfile name="prod">
7+
<springProfile name="prod, dev">
88
<property name="LOG_PATH" value="/app/logs"/>
99
</springProfile>
1010

memory-infra/docker/dev/docker-compose.yml

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ services:
8989
SPRING_ELASTICSEARCH_URIS: http://elasticsearch:9200
9090
SPRING_ELASTICSEARCH_USERNAME: elastic
9191
SPRING_ELASTICSEARCH_PASSWORD: ${ELASTIC_PASSWORD}
92+
volumes:
93+
- ./data/logs:/app/logs
9294
networks:
9395
- memory-network
9496
restart: always
@@ -216,6 +218,54 @@ services:
216218
- ./data/certbot/www:/var/www/certbot
217219
entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"
218220

221+
logstash:
222+
image: docker.elastic.co/logstash/logstash:8.17.4
223+
container_name: memory-logstash-dev
224+
ports:
225+
- "5045:5044"
226+
- "9602:9600"
227+
environment:
228+
- LS_JAVA_OPTS=-Xms256m -Xmx256m
229+
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
230+
- ELASTICSEARCH_USERNAME=elastic
231+
- ELASTICSEARCH_PASSWORD=${ELASTIC_PASSWORD}
232+
volumes:
233+
- ./logstash/pipeline:/usr/share/logstash/pipeline:ro
234+
- ./logstash/config/logstash.yml:/usr/share/logstash/config/logstash.yml:ro
235+
- ./data/logstash-data:/usr/share/logstash/data
236+
depends_on:
237+
elasticsearch:
238+
condition: service_healthy
239+
networks:
240+
- memory-network
241+
restart: always
242+
healthcheck:
243+
test: ["CMD-SHELL", "curl -f http://localhost:9600/_node/stats || exit 1"]
244+
interval: 30s
245+
timeout: 10s
246+
retries: 5
247+
248+
filebeat:
249+
image: docker.elastic.co/beats/filebeat:8.17.4
250+
container_name: memory-filebeat-dev
251+
user: root
252+
environment:
253+
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
254+
- ELASTICSEARCH_USERNAME=elastic
255+
- ELASTICSEARCH_PASSWORD=${ELASTIC_PASSWORD}
256+
- LOGSTASH_HOSTS=logstash:5044
257+
volumes:
258+
- ./filebeat/filebeat.yml:/usr/share/filebeat/filebeat.yml:ro
259+
- ./data/logs:/var/log/memory:ro
260+
- ./data/filebeat-data:/usr/share/filebeat/data
261+
- /var/lib/docker/containers:/var/lib/docker/containers:ro
262+
- /var/run/docker.sock:/var/run/docker.sock:ro
263+
depends_on:
264+
- logstash
265+
networks:
266+
- memory-network
267+
restart: always
268+
219269
networks:
220270
memory-network:
221271
driver: bridge
@@ -224,3 +274,6 @@ volumes:
224274
postgres-data:
225275
minio-data:
226276
elasticsearch-data:
277+
logs:
278+
logstash-data:
279+
filebeat-data:
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
filebeat.inputs:
2+
# Memory Application Logs
3+
- type: log
4+
enabled: true
5+
paths:
6+
- /var/log/memory/application*.log
7+
fields:
8+
service: memory-api
9+
environment: dev
10+
log_type: application
11+
fields_under_root: true
12+
multiline.pattern: '^\\{'
13+
multiline.negate: true
14+
multiline.match: after
15+
exclude_lines: ['^$']
16+
ignore_older: 0
17+
close_inactive: 5m
18+
scan_frequency: 10s
19+
20+
# Memory API Logs
21+
- type: log
22+
enabled: true
23+
paths:
24+
- /var/log/memory/api*.log
25+
fields:
26+
service: memory-api
27+
environment: dev
28+
log_type: api
29+
fields_under_root: true
30+
multiline.pattern: '^\\{'
31+
multiline.negate: true
32+
multiline.match: after
33+
exclude_lines: ['^$']
34+
ignore_older: 0
35+
close_inactive: 5m
36+
scan_frequency: 5s
37+
38+
# Memory Error Logs
39+
- type: log
40+
enabled: true
41+
paths:
42+
- /var/log/memory/error*.log
43+
fields:
44+
service: memory-api
45+
environment: dev
46+
log_type: error
47+
fields_under_root: true
48+
multiline.pattern: '^\\{'
49+
multiline.negate: true
50+
multiline.match: after
51+
exclude_lines: ['^$']
52+
ignore_older: 0
53+
close_inactive: 5m
54+
scan_frequency: 5s
55+
56+
# Memory Business Logs
57+
- type: log
58+
enabled: true
59+
paths:
60+
- /var/log/memory/business*.log
61+
fields:
62+
service: memory-api
63+
environment: dev
64+
log_type: business
65+
fields_under_root: true
66+
multiline.pattern: '^\\{'
67+
multiline.negate: true
68+
multiline.match: after
69+
exclude_lines: ['^$']
70+
ignore_older: 0
71+
close_inactive: 5m
72+
scan_frequency: 10s
73+
74+
# Docker Container Logs (비활성화)
75+
- type: container
76+
enabled: false
77+
paths:
78+
- /var/lib/docker/containers/*/*.log
79+
containers.ids:
80+
- "*"
81+
exclude_containers:
82+
- "memory-postgres-dev"
83+
- "memory-minio-dev"
84+
- "memory-elasticsearch-dev"
85+
- "memory-kibana-dev"
86+
- "memory-prometheus-dev"
87+
- "memory-grafana-dev"
88+
- "memory-logstash-dev"
89+
- "memory-filebeat-dev"
90+
fields:
91+
environment: dev
92+
log_type: container
93+
fields_under_root: true
94+
95+
# Logstash Output 설정
96+
output.logstash:
97+
hosts: ["logstash:5044"]
98+
compression_level: 3
99+
worker: 2
100+
bulk_max_size: 2048
101+
timeout: 30s
102+
pipelining: 2
103+
104+
# Filebeat 자체 로그 설정
105+
logging.level: info
106+
logging.to_files: true
107+
logging.files:
108+
path: /var/log/filebeat
109+
name: filebeat
110+
keepfiles: 7
111+
permissions: 0600
112+
113+
# 성능 설정
114+
filebeat.registry.path: /usr/share/filebeat/data/registry
115+
filebeat.registry.file_permissions: 0600
116+
filebeat.registry.flush: 1s
117+
118+
# 모니터링 설정
119+
monitoring.enabled: true
120+
monitoring.elasticsearch:
121+
hosts: ["elasticsearch:9200"]
122+
username: elastic
123+
password: ${ELASTICSEARCH_PASSWORD}
124+
125+
# 프로세서 설정
126+
processors:
127+
- add_host_metadata:
128+
when.not.contains.tags: forwarded
129+
- add_docker_metadata: ~
130+
- add_kubernetes_metadata: ~
131+
- timestamp:
132+
field: json.timestamp
133+
layouts:
134+
- '2006-01-02 15:04:05.000'
135+
- '2006-01-02T15:04:05.000Z'
136+
test:
137+
- '2023-12-01 12:34:56.789'
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
http.host: "0.0.0.0"
2+
xpack.monitoring.elasticsearch.hosts: ["http://elasticsearch:9200"]
3+
xpack.monitoring.elasticsearch.username: "elastic"
4+
xpack.monitoring.elasticsearch.password: "${ELASTICSEARCH_PASSWORD}"
5+
xpack.monitoring.enabled: true
6+
path.config: /usr/share/logstash/pipeline
7+
path.data: /usr/share/logstash/data
8+
pipeline.workers: 2
9+
pipeline.batch.size: 125
10+
pipeline.batch.delay: 50
11+
queue.type: memory
12+
log.level: info
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
input {
2+
beats {
3+
port => 5044
4+
}
5+
}
6+
7+
filter {
8+
# FileBeat에서 오는 메타데이터 처리
9+
if [agent][type] == "filebeat" {
10+
mutate {
11+
add_field => { "log_source" => "filebeat" }
12+
}
13+
}
14+
15+
# JSON 로그 파싱
16+
if [message] =~ /^\{.*\}$/ {
17+
json {
18+
source => "message"
19+
target => "parsed_json"
20+
}
21+
22+
# 파싱된 JSON 데이터를 최상위로 이동
23+
if [parsed_json] {
24+
ruby {
25+
code => "
26+
parsed = event.get('parsed_json')
27+
if parsed.is_a?(Hash)
28+
parsed.each { |k, v| event.set(k, v) }
29+
event.remove('parsed_json')
30+
end
31+
"
32+
}
33+
}
34+
}
35+
36+
# API 로그 특별 처리
37+
if [type] == "API_REQUEST" or [type] == "API_RESPONSE" {
38+
mutate {
39+
add_field => { "log_category" => "api_access" }
40+
}
41+
42+
# 응답 시간 기반 성능 태그
43+
if [processingTime] {
44+
if [processingTime] > 5000 {
45+
mutate { add_tag => ["slow_api"] }
46+
} else if [processingTime] > 1000 {
47+
mutate { add_tag => ["medium_api"] }
48+
} else {
49+
mutate { add_tag => ["fast_api"] }
50+
}
51+
}
52+
53+
# HTTP 상태 코드 기반 태그
54+
if [statusCode] {
55+
if [statusCode] >= 500 {
56+
mutate { add_tag => ["server_error"] }
57+
} else if [statusCode] >= 400 {
58+
mutate { add_tag => ["client_error"] }
59+
} else if [statusCode] >= 200 {
60+
mutate { add_tag => ["success"] }
61+
}
62+
}
63+
}
64+
65+
# 에러 로그 특별 처리
66+
if [level] == "ERROR" {
67+
mutate {
68+
add_field => { "log_category" => "error" }
69+
add_tag => ["error"]
70+
}
71+
}
72+
73+
# 타임스탬프 처리
74+
if [timestamp] {
75+
date {
76+
match => [ "timestamp", "yyyy-MM-dd HH:mm:ss.SSS", "ISO8601" ]
77+
target => "@timestamp"
78+
}
79+
}
80+
81+
# 불필요한 필드 제거
82+
mutate {
83+
remove_field => [ "ecs", "host", "agent", "input", "log" ]
84+
}
85+
}
86+
87+
output {
88+
# 로그 타입별로 다른 인덱스에 저장 (dev 환경 prefix)
89+
if [log_type] == "api" {
90+
elasticsearch {
91+
hosts => ["elasticsearch:9200"]
92+
user => "elastic"
93+
password => "${ELASTICSEARCH_PASSWORD}"
94+
index => "memory-dev-api-logs-%{+YYYY.MM.dd}"
95+
}
96+
} else if [log_type] == "error" {
97+
elasticsearch {
98+
hosts => ["elasticsearch:9200"]
99+
user => "elastic"
100+
password => "${ELASTICSEARCH_PASSWORD}"
101+
index => "memory-dev-error-logs-%{+YYYY.MM.dd}"
102+
}
103+
} else if [log_type] == "business" {
104+
elasticsearch {
105+
hosts => ["elasticsearch:9200"]
106+
user => "elastic"
107+
password => "${ELASTICSEARCH_PASSWORD}"
108+
index => "memory-dev-business-logs-%{+YYYY.MM.dd}"
109+
}
110+
} else if [log_type] == "container" {
111+
elasticsearch {
112+
hosts => ["elasticsearch:9200"]
113+
user => "elastic"
114+
password => "${ELASTICSEARCH_PASSWORD}"
115+
index => "memory-dev-container-logs-%{+YYYY.MM.dd}"
116+
}
117+
} else {
118+
elasticsearch {
119+
hosts => ["elasticsearch:9200"]
120+
user => "elastic"
121+
password => "${ELASTICSEARCH_PASSWORD}"
122+
index => "memory-dev-application-logs-%{+YYYY.MM.dd}"
123+
}
124+
}
125+
126+
# 개발환경에서는 콘솔 출력도 활성화
127+
stdout { codec => rubydebug }
128+
}

0 commit comments

Comments
 (0)