Linux Logging Architecture
Why Logging Architecture Matters for Security and Operations
Logs are the primary evidence source in incident response. When a breach happens, the quality of your logging determines whether you can answer: what happened, when, in what sequence, and from where? Most engineers know how to read logs. Far fewer understand how logs are generated, routed, and stored — which means they don’t know where the gaps are. Logs that are only stored locally can be deleted by an attacker. Logs that aren’t structured are hard to correlate. Logs that aren’t retained long enough fail compliance requirements. Understanding the Linux logging stack — from kernel through syslog to SIEM — is how you turn raw events into security intelligence.
Core Concept: The Linux Logging Stack
Three Parallel Systems
Modern Linux systems have three logging mechanisms operating simultaneously:
syslog protocol (RFC 5424): The traditional UNIX logging standard. Applications call syslog() or write to /dev/log. The syslog daemon (rsyslog or syslog-ng) receives these, applies filtering and routing rules, and writes to files in /var/log/.
systemd journal (journald): systemd’s native logging daemon. Captures structured binary logs including service stdout/stderr, kernel messages, and syslog messages. Stored in /var/log/journal/ (persistent) or /run/log/journal/ (volatile). Queried with journalctl.
Kernel ring buffer (dmesg): Low-level kernel messages. In-memory circular buffer accessible via dmesg. Also forwarded to syslog/journald.
On a modern systemd-based distro, journald is the primary sink — it receives from all sources — while rsyslog/syslog-ng can forward journald messages to files, remote syslog, or other destinations.
Log Severity Levels
The syslog severity levels (RFC 5424) from most to least critical:
| Level | Name | Value | Meaning |
|---|---|---|---|
| 0 | EMERG | emerg | System is unusable |
| 1 | ALERT | alert | Action must be taken immediately |
| 2 | CRIT | crit | Critical conditions |
| 3 | ERR | err | Error conditions |
| 4 | WARNING | warning | Warning conditions |
| 5 | NOTICE | notice | Normal but significant event |
| 6 | INFO | info | Informational |
| 7 | DEBUG | debug | Debug-level messages |
Combined with a facility (source category: kernel=0, user=1, mail=2, daemon=3, auth=4, syslog=5, etc.), this creates a priority value: facility * 8 + severity. auth.err means authentication errors; kern.crit means critical kernel messages.
How It Works: The Components in Detail
rsyslog vs syslog-ng vs journald
rsyslog is the default on RHEL/CentOS/Ubuntu. It’s high-performance, supports structured logging, has a module system, and can forward to remote destinations including Elasticsearch.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# /etc/rsyslog.conf and /etc/rsyslog.d/*.conf
# Basic rule syntax: facility.severity destination
# Log all auth messages to auth.log
auth,authpriv.* /var/log/auth.log
# Log all WARN and above to syslog
*.warn /var/log/syslog
# Forward to remote syslog server (UDP)
*.* @192.168.1.100:514
# Forward to remote syslog server (TCP, more reliable)
*.* @@192.168.1.100:514
# Log specific program
:programname, isequal, "nginx" /var/log/nginx/error.log
syslog-ng is more expressive with its configuration language. Better for complex filtering and transformation pipelines. More commonly found in enterprise deployments.
1
2
3
4
5
6
7
# syslog-ng uses source/filter/destination/log constructs
# /etc/syslog-ng/syslog-ng.conf
source s_sys { system(); };
filter f_auth { facility(auth, authpriv); };
destination d_auth { file("/var/log/auth.log"); };
log { source(s_sys); filter(f_auth); destination(d_auth); };
journald is configured via /etc/systemd/journald.conf:
1
2
3
4
5
6
7
8
9
[Journal]
Storage=persistent # persistent (disk) vs volatile (memory) vs auto
Compress=yes
RateLimitBurst=10000 # max messages per RateLimitInterval
RateLimitInterval=30s
SystemMaxUse=1G # max total disk space for journal
SystemKeepFree=100M # min free space to keep
MaxRetentionSec=1month # delete logs older than this
ForwardToSyslog=yes # forward to rsyslog
The /var/log Structure
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/var/log/
├── auth.log # Authentication events (sudo, su, ssh) — Debian/Ubuntu
├── secure # Same, RHEL/CentOS
├── syslog # General system messages — Ubuntu
├── messages # Same, RHEL/CentOS
├── kern.log # Kernel messages
├── cron # Cron job execution
├── dmesg # Boot kernel messages (static snapshot)
├── lastlog # Last login per user (binary format)
├── wtmp # Login/logout history (binary, read with `last`)
├── btmp # Failed login attempts (binary, read with `lastb`)
├── faillog # Failed login counts per user (binary)
├── mail.log # Mail server messages
├── nginx/
│ ├── access.log # HTTP access log
│ └── error.log # HTTP error log
└── journal/ # systemd binary journal (directory)
journalctl Usage
journalctl is the query interface for the systemd journal. It’s more powerful than grepping log files.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# View all logs (most recent last)
journalctl
# Follow (like tail -f)
journalctl -f
# Show logs for a specific service
journalctl -u nginx
journalctl -u nginx -f # follow
journalctl -u nginx --since "1 hour ago"
# Time-based filtering
journalctl --since "2024-01-15 10:00:00"
journalctl --since "2024-01-15" --until "2024-01-16"
journalctl --since yesterday
journalctl --since "1 hour ago"
# By priority
journalctl -p err # error and above (err, crit, alert, emerg)
journalctl -p warning..err # between warning and err
# By PID or UID
journalctl _PID=1234
journalctl _UID=1000
# Kernel messages
journalctl -k # kernel messages only
journalctl -k --since "today"
# JSON output (for structured parsing)
journalctl -o json-pretty -u nginx | head -50
journalctl -o json --since "1 hour ago" | jq '.SYSLOG_IDENTIFIER,.MESSAGE'
# Disk usage
journalctl --disk-usage
# Vacuum old logs
journalctl --vacuum-time=30d # delete logs older than 30 days
journalctl --vacuum-size=500M # keep only latest 500MB
Log Rotation with logrotate
logrotate prevents log files from growing unbounded. Configured in /etc/logrotate.conf and /etc/logrotate.d/.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# /etc/logrotate.d/nginx
/var/log/nginx/*.log {
daily # rotate daily
missingok # don't error if log file missing
rotate 14 # keep 14 rotated files
compress # gzip old logs
delaycompress # compress previous rotation, not current
notifempty # don't rotate if empty
create 0640 www-data adm # new file permissions and owner
sharedscripts # run postrotate once, not per file
postrotate
# Signal nginx to reopen log files
if [ -f /var/run/nginx.pid ]; then
kill -USR1 $(cat /var/run/nginx.pid)
fi
endscript
}
1
2
3
4
5
6
7
8
# Test logrotate config (dry run)
logrotate -d /etc/logrotate.d/nginx
# Force rotation (for testing)
logrotate -f /etc/logrotate.d/nginx
# Check logrotate status
cat /var/lib/logrotate/status
Practical Application: Security-Relevant Log Sources
Authentication and Access Logs
These are the first logs to review in an incident:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Failed SSH login attempts
grep "Failed password" /var/log/auth.log | \
awk '{print $11}' | sort | uniq -c | sort -rn | head -20
# Successful logins
grep "Accepted password\|Accepted publickey" /var/log/auth.log
# Sudo usage
grep "sudo:" /var/log/auth.log | grep "COMMAND"
# Su attempts
grep "su:" /var/log/auth.log
# Using journalctl for auth events
journalctl _COMM=sudo --since "24 hours ago"
journalctl _COMM=sshd -p warning --since "today"
Process and System Events
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# New processes started (requires auditd)
ausearch -m EXECVE --start today
# Cron execution history
grep CRON /var/log/syslog | tail -50
# System reboots
last reboot
journalctl --list-boots # all boot sessions
journalctl -b -1 # previous boot's logs
journalctl -b 0 # current boot's logs
# Service start/stop events
journalctl -u '*' --grep="Started\|Stopped\|Failed" --since "today"
Centralized Logging Setup
For any non-trivial environment, ship logs to a central system:
1
2
3
4
5
6
7
8
9
10
# rsyslog forwarding to central server (TLS)
# /etc/rsyslog.d/50-forward.conf
$DefaultNetstreamDriverCAFile /etc/rsyslog.d/ca.pem
$DefaultNetstreamDriver gtls
$ActionSendStreamDriverMode 1
$ActionSendStreamDriverAuthMode x509/name
$ActionSendStreamDriverPermittedPeer logserver.example.com
*.* @@logserver.example.com:6514
For modern environments, consider Filebeat + Elasticsearch/OpenSearch or Vector (higher performance, more flexible) instead of rsyslog forwarding.
Structured Logging with systemd
Applications should log in structured formats. systemd journal natively supports key=value fields:
1
2
3
4
5
6
# Log with structured fields (systemd-cat)
echo "transaction completed" | systemd-cat -t myapp -p info
logger -t myapp --sd-id txn@1 --sd-param id="abc123" "Transaction completed"
# Query structured fields
journalctl -t myapp PRIORITY=6
Gotchas: What Experts Know
Journald is volatile by default on many distros. Logs in /run/log/journal/ vanish on reboot. Check /etc/systemd/journald.conf for Storage=persistent. On Ubuntu, create the directory to enable persistence: mkdir -p /var/log/journal && systemd-tmpfiles --create --prefix /var/log/journal.
Log files store timestamps without timezone offset by default. Jan 15 10:00:00 in /var/log/syslog has no timezone indicator. For incident correlation across systems in different timezones, you need either centralized logs that normalize to UTC, or structured logging with RFC 3339 timestamps (2024-01-15T10:00:00+09:00). journald stores in UTC internally.
Rate limiting hides attack patterns. rsyslog and journald both have rate limiting. During a brute-force attack, you may see “… messages suppressed” in logs — the actual number of attempts is higher than logged. Set RateLimitBurst appropriately or use imuxsock rate limiting in rsyslog.
logrotate postrotate must signal the daemon to reopen files. If nginx is writing to a file, and logrotate renames/removes it, nginx keeps writing to the old inode. The postrotate script must send SIGUSR1 to nginx (or the appropriate signal for your daemon). Without this, the new log file sits empty while the old inode keeps growing.
/var/log/wtmp and /var/log/btmp are binary. Don’t cat them. Use last and lastb respectively. These files are evidence in incident response — they can be tampered with (utmpdump for reading, then modify, then utmpdump -r to rewrite).
auditd for comprehensive process logging. The standard logs don’t capture all process execution. auditd with appropriate rules captures every execve() system call, file access, network connection, and privilege escalation — the full audit trail needed for security investigations.
1
2
3
4
5
6
7
8
9
10
11
12
13
# Install and configure auditd
apt install auditd # or dnf install audit
# Watch for execve (process execution)
auditctl -a always,exit -F arch=b64 -S execve -k exec_log
# Watch specific file changes
auditctl -w /etc/passwd -p wa -k passwd_changes
auditctl -w /etc/sudoers -p wa -k sudoers_changes
# Query audit log
ausearch -k exec_log --start today
ausearch -m USER_LOGIN --start today
Quick Reference
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# journalctl
journalctl -f # follow all logs
journalctl -u SERVICE # service logs
journalctl -u SERVICE -f # follow service
journalctl -p err # error+ priority
journalctl --since "1 hour ago" # time filter
journalctl -k # kernel logs
journalctl -o json -u SERVICE # JSON output
journalctl --disk-usage # journal disk usage
journalctl --vacuum-time=30d # cleanup old logs
# Reading system logs
tail -f /var/log/auth.log # auth events (Debian)
tail -f /var/log/secure # auth events (RHEL)
tail -f /var/log/syslog # system (Debian)
tail -f /var/log/messages # system (RHEL)
dmesg -T # kernel ring buffer with timestamps
dmesg -T -l err,crit # kernel errors only
# Auth analysis
grep "Failed password" /var/log/auth.log | awk '{print $11}' | sort | uniq -c | sort -rn
grep "Accepted" /var/log/auth.log
last # login history
lastb # failed login history
# logrotate
logrotate -d /etc/logrotate.d/nginx # dry run
logrotate -f /etc/logrotate.d/nginx # force rotation
1
2
3
4
5
6
7
8
LOG RETENTION GUIDELINES (common compliance baselines)
────────────────────────────────────────────────────────────
Auth/access logs: 90 days minimum (PCI-DSS), 1 year recommended
System logs: 30 days minimum
Security events: 1 year (SOC2, ISO 27001)
Network flow logs: 90 days
Application logs: 30-90 days (varies by sensitivity)
Audit logs (auditd): 1 year minimum for regulated environments
왜 로깅 아키텍처가 보안과 운영에서 중요한가
로그는 인시던트 대응의 주요 증거 소스다. 침해가 발생하면 로깅의 품질이 무슨 일이 일어났는지, 언제, 어떤 순서로, 어디서인지 대답할 수 있는지를 결정한다. 대부분의 엔지니어는 로그 읽는 방법을 안다. 로그가 어떻게 생성되고, 라우팅되고, 저장되는지를 이해하는 사람은 훨씬 적다 — 이는 갭이 어디에 있는지 모른다는 것을 의미한다. 로컬에만 저장된 로그는 공격자가 삭제할 수 있다. 구조화되지 않은 로그는 상관관계 분석이 어렵다. 충분히 오래 보존되지 않은 로그는 컴플라이언스 요구사항을 충족하지 못한다.
핵심 개념: Linux 로깅 스택
세 가지 병렬 시스템
현대 Linux 시스템은 세 가지 로깅 메커니즘이 동시에 작동한다:
syslog 프로토콜 (RFC 5424): 전통적인 UNIX 로깅 표준. 애플리케이션이 syslog()를 호출하거나 /dev/log에 쓴다. syslog 데몬(rsyslog 또는 syslog-ng)이 이를 받아 필터링 및 라우팅 규칙을 적용하고 /var/log/의 파일에 쓴다.
systemd 저널 (journald): systemd의 네이티브 로깅 데몬. 서비스 stdout/stderr, 커널 메시지, syslog 메시지를 포함한 구조화된 바이너리 로그를 캡처한다. /var/log/journal/(영구) 또는 /run/log/journal/(휘발성)에 저장된다.
커널 링 버퍼 (dmesg): 저수준 커널 메시지. dmesg를 통해 액세스 가능한 메모리 내 순환 버퍼. syslog/journald로도 전달된다.
로그 심각도 수준
RFC 5424 syslog 심각도 수준 (가장 심각한 것부터):
| 레벨 | 이름 | 값 | 의미 |
|---|---|---|---|
| 0 | EMERG | emerg | 시스템 사용 불가 |
| 1 | ALERT | alert | 즉각적인 조치 필요 |
| 2 | CRIT | crit | 심각한 조건 |
| 3 | ERR | err | 오류 조건 |
| 4 | WARNING | warning | 경고 조건 |
| 5 | NOTICE | notice | 정상이지만 중요한 이벤트 |
| 6 | INFO | info | 정보 |
| 7 | DEBUG | debug | 디버그 수준 메시지 |
facility(소스 카테고리)와 결합되어 우선순위 값을 만든다: facility * 8 + severity. auth.err는 인증 오류, kern.crit는 심각한 커널 메시지를 의미한다.
동작 원리: 컴포넌트 상세
rsyslog vs syslog-ng vs journald
rsyslog는 RHEL/CentOS/Ubuntu의 기본이다. 고성능이며, 구조화된 로깅을 지원하고, 모듈 시스템을 가지며, Elasticsearch를 포함한 원격 대상으로 전달할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# /etc/rsyslog.conf와 /etc/rsyslog.d/*.conf
# 기본 규칙 구문: facility.severity 대상
# 모든 auth 메시지를 auth.log로
auth,authpriv.* /var/log/auth.log
# WARN 이상 모두 syslog로
*.warn /var/log/syslog
# 원격 syslog 서버로 전달 (UDP)
*.* @192.168.1.100:514
# 원격 syslog 서버로 전달 (TCP, 더 신뢰할 수 있음)
*.* @@192.168.1.100:514
syslog-ng는 설정 언어가 더 표현력이 풍부하다. 복잡한 필터링과 변환 파이프라인에 더 좋다.
journald는 /etc/systemd/journald.conf로 구성된다:
1
2
3
4
5
6
7
8
[Journal]
Storage=persistent # persistent (디스크) vs volatile (메모리) vs auto
Compress=yes
RateLimitBurst=10000
RateLimitInterval=30s
SystemMaxUse=1G
MaxRetentionSec=1month
ForwardToSyslog=yes # rsyslog로 전달
/var/log 구조
1
2
3
4
5
6
7
8
9
10
11
/var/log/
├── auth.log # 인증 이벤트 (sudo, su, ssh) — Debian/Ubuntu
├── secure # 동일, RHEL/CentOS
├── syslog # 일반 시스템 메시지 — Ubuntu
├── messages # 동일, RHEL/CentOS
├── kern.log # 커널 메시지
├── cron # Cron 작업 실행
├── lastlog # 사용자당 마지막 로그인 (바이너리 형식)
├── wtmp # 로그인/로그아웃 기록 (바이너리, `last`로 읽기)
├── btmp # 실패한 로그인 시도 (바이너리, `lastb`로 읽기)
└── journal/ # systemd 바이너리 저널 (디렉토리)
journalctl 사용법
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 모든 로그 보기
journalctl
# 실시간 추적
journalctl -f
# 특정 서비스 로그
journalctl -u nginx
journalctl -u nginx -f
journalctl -u nginx --since "1 hour ago"
# 시간 기반 필터링
journalctl --since "2024-01-15 10:00:00"
journalctl --since yesterday
journalctl --since "1 hour ago"
# 우선순위별
journalctl -p err # error 이상
journalctl -p warning..err # warning과 err 사이
# 커널 메시지
journalctl -k
# JSON 출력 (구조화된 파싱용)
journalctl -o json-pretty -u nginx | head -50
# 디스크 사용량
journalctl --disk-usage
# 오래된 로그 정리
journalctl --vacuum-time=30d
journalctl --vacuum-size=500M
logrotate를 사용한 로그 순환
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# /etc/logrotate.d/nginx
/var/log/nginx/*.log {
daily # 매일 순환
missingok # 로그 파일 없어도 오류 없음
rotate 14 # 순환된 파일 14개 유지
compress # 오래된 로그 gzip 압축
delaycompress # 현재가 아닌 이전 순환 압축
notifempty # 비어 있으면 순환하지 않음
create 0640 www-data adm
sharedscripts
postrotate
if [ -f /var/run/nginx.pid ]; then
kill -USR1 $(cat /var/run/nginx.pid)
fi
endscript
}
1
2
3
4
5
# 구성 테스트 (드라이 런)
logrotate -d /etc/logrotate.d/nginx
# 강제 순환 (테스트용)
logrotate -f /etc/logrotate.d/nginx
실전 적용: 보안 관련 로그 소스
인증 및 접근 로그
인시던트 시 가장 먼저 검토해야 할 로그:
1
2
3
4
5
6
7
8
9
10
11
12
13
# SSH 로그인 실패 시도
grep "Failed password" /var/log/auth.log | \
awk '{print $11}' | sort | uniq -c | sort -rn | head -20
# 성공한 로그인
grep "Accepted password\|Accepted publickey" /var/log/auth.log
# Sudo 사용
grep "sudo:" /var/log/auth.log | grep "COMMAND"
# journalctl로 인증 이벤트
journalctl _COMM=sudo --since "24 hours ago"
journalctl _COMM=sshd -p warning --since "today"
프로세스 및 시스템 이벤트
1
2
3
4
5
6
7
8
# Cron 실행 기록
grep CRON /var/log/syslog | tail -50
# 시스템 재부팅
last reboot
journalctl --list-boots # 모든 부팅 세션
journalctl -b -1 # 이전 부팅의 로그
journalctl -b 0 # 현재 부팅의 로그
중앙 집중식 로깅 설정
1
2
3
4
5
6
7
8
9
10
# rsyslog TLS 전달
# /etc/rsyslog.d/50-forward.conf
$DefaultNetstreamDriverCAFile /etc/rsyslog.d/ca.pem
$DefaultNetstreamDriver gtls
$ActionSendStreamDriverMode 1
$ActionSendStreamDriverAuthMode x509/name
$ActionSendStreamDriverPermittedPeer logserver.example.com
*.* @@logserver.example.com:6514
현대 환경에서는 rsyslog 전달 대신 Filebeat + Elasticsearch/OpenSearch 또는 Vector를 고려하라.
함정: 전문가들이 아는 것
많은 배포판에서 journald는 기본적으로 휘발성이다. /run/log/journal/의 로그는 재부팅 시 사라진다. /etc/systemd/journald.conf에서 Storage=persistent를 확인하라. Ubuntu에서는 디렉토리를 생성해 영구성을 활성화하라: mkdir -p /var/log/journal.
로그 파일은 기본적으로 타임스탬프에 시간대 오프셋 없이 저장한다. /var/log/syslog의 Jan 15 10:00:00에는 시간대 표시가 없다. 다른 시간대의 시스템 간 인시던트 상관관계를 위해 UTC로 정규화된 중앙 집중식 로그나 RFC 3339 타임스탬프가 있는 구조화된 로깅이 필요하다.
속도 제한은 공격 패턴을 숨긴다. rsyslog와 journald 모두 속도 제한이 있다. 브루트포스 공격 중에 로그에서 “… 메시지 억제됨”을 볼 수 있다 — 실제 시도 횟수는 기록된 것보다 높다.
logrotate postrotate는 데몬에게 파일을 다시 열라고 신호해야 한다. nginx가 파일에 쓰고 있고 logrotate가 이름을 바꾸거나 제거하면 nginx는 이전 아이노드에 계속 쓴다. postrotate 스크립트는 nginx에 SIGUSR1을 보내야 한다.
/var/log/wtmp와 /var/log/btmp는 바이너리다. cat하지 마라. 각각 last와 lastb를 사용하라. 이 파일들은 인시던트 대응의 증거다.
포괄적인 프로세스 로깅을 위한 auditd. 표준 로그는 모든 프로세스 실행을 캡처하지 않는다. auditd와 적절한 규칙은 모든 execve() 시스템 호출, 파일 접근, 네트워크 연결, 권한 상승을 캡처한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
# auditd 설치 및 구성
apt install auditd
# 프로세스 실행 감시
auditctl -a always,exit -F arch=b64 -S execve -k exec_log
# 특정 파일 변경 감시
auditctl -w /etc/passwd -p wa -k passwd_changes
auditctl -w /etc/sudoers -p wa -k sudoers_changes
# 감사 로그 조회
ausearch -k exec_log --start today
ausearch -m USER_LOGIN --start today
빠른 참조
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# journalctl
journalctl -f # 모든 로그 추적
journalctl -u SERVICE # 서비스 로그
journalctl -p err # error+ 우선순위
journalctl --since "1 hour ago" # 시간 필터
journalctl -k # 커널 로그
journalctl -o json -u SERVICE # JSON 출력
journalctl --disk-usage # 저널 디스크 사용량
journalctl --vacuum-time=30d # 오래된 로그 정리
# 시스템 로그 읽기
tail -f /var/log/auth.log # 인증 이벤트 (Debian)
tail -f /var/log/secure # 인증 이벤트 (RHEL)
dmesg -T # 타임스탬프와 함께 커널 링 버퍼
dmesg -T -l err,crit # 커널 오류만
# 인증 분석
grep "Failed password" /var/log/auth.log | awk '{print $11}' | sort | uniq -c | sort -rn
grep "Accepted" /var/log/auth.log
last # 로그인 기록
lastb # 실패한 로그인 기록
1
2
3
4
5
6
7
8
로그 보존 지침 (일반 컴플라이언스 기준)
────────────────────────────────────────────────────────────
인증/접근 로그: 최소 90일 (PCI-DSS), 1년 권장
시스템 로그: 최소 30일
보안 이벤트: 1년 (SOC2, ISO 27001)
네트워크 플로우 로그: 90일
애플리케이션 로그: 30-90일 (민감도에 따라 다름)
감사 로그 (auditd): 규제 환경에서 최소 1년