[Python] 로그 생성기 만들기
본문 바로가기

Python

[Python] 로그 생성기 만들기

728x90
반응형

1) 상단 개요 & 의존성

  • argparse: 커맨드라인 옵션(속도, 지터, 버스트, 로스터 경로 등) 처리.
  • socket, threading, queue: Persistent TCP 송신 스레드 구현용.
  • random, time, datetime: 이벤트 생성 시각/간격, 값 난수화.
  • csv, pathlib.Path: 로스터 CSV 로딩.
  • json은 선택적(필수는 아님, 디버깅 때 유용).

왜 이렇게?

  • 생성(Producer)와 전송(Consumer)을 분리하고, Consumer는 대상별 스레드가 맡도록 설계하면 고EPS에서도 안정적으로 보낼 수 있어(연결 유지 + 재연결 백오프).

2) 전역 설정 블록 (USER CONFIG)

TARGET_IPS = ["192.168.0.50"]
TARGET_PORT = 8514
CONNECT_TIMEOUT = 3.0
SEND_TIMEOUT = 3.0
KEEPALIVE = True
TCP_NODELAY = True

SRC_DST_HOSTS = {"10.10.10.1": "Uclick_DLP_1", "10.10.10.2": "Uclick_DLP_2"}
PRIORITY_LABELS = ["local0.crit", "local1.info", "local2.warn", "local3.err"]
 

왜 이렇게?

  • 운영 파라미터를 한 곳에 모아두면 유지보수·재배포가 쉽다.
  • TCP_NODELAY는 Nagle 끄기 → 짧은 로그 라인 즉시 송신.
  • KEEPALIVE는 장기 연결 안정성 ↑.
  • 헤더 SRC_DST_HOSTS는 DLP 장비처럼 보이게 고정 식별자 제공.

어디 바꾸면?

  • 대상 서버 추가: TARGET_IPS에 줄만 더 넣으면 됨.
  • 포트 변경: TARGET_PORT.

3) 타임스탬프 포매터 iso_ts_with_tz

  • 로컬 타임존 오프셋을 time 모듈로 계산해서 YYYY-MM-DDTHH:MM:SS.mmm+09:00 형태로 만든다.
    왜 이렇게?
  • 장비에 가까운 타임스탬프(로컬 시간 + 오프셋)를 한 줄 로그에 직접 넣어주기 위해.
  • Splunk 등의 수신기가 이 형식을 잘 인지하고, 사람이 봐도 해석이 쉽다.

4) ClientRoster: 로스터(명부) 관리

class ClientRoster:
    @classmethod
    def from_csv(cls, path): ...
    def random_client(self, dept_weight=None): ...
 
  • CSV에서 ip, hostname, os, department, mac, username, userid를 읽어 행 그대로 보관.
  • random_client는 행 하나를 랜덤으로 뽑아 주는데, 필요하면 부서 가중치도 줄 수 있게 설계.

왜 이렇게?

  • “서로 연관된 필드(사용자↔호스트↔부서↔MAC↔OS)”를 항상 일관되게 쓰는 게 목표.
  • 나중에 “특정 부서만 많이 발생” 같은 분포 제어도 쉽게 붙이려는 포석.

어디 바꾸면?

  • 로스터 컬럼을 더 쓰고 싶으면 CSV에 칼럼 추가 + 아래 로그 템플릿에 필드만 더 붙이면 된다.

5) 로그 라인 생성 로직

핵심 함수 3개:

  • build_log_line(log_type_key, dt_local, client)
  • build_filename(client)
  • choose_policy_by_os(os_name)

동작:

  1. 헤더(장비 식별) 선택: SRC_DST_HOSTS에서 하나.
  2. 우선 로스터 기반 필드를 고정 매핑:
    • [Client Computer] ← hostname
    • [IP Address] ← ip
    • [Department] ← department
    • [MAC Address] ← mac
    • [Client User] ← username
    • [Client UserID] ← userid (커스텀)
    • [Client OS] ← os (커스텀)
  3. log_type_key별 추가 필드가 붙는다.
    • Device Control: 장치 VID/PID/Serial, 파일명/사이즈
    • Content Aware: 정책/목적지/파일/매칭 항목
  4. 마지막에 [GeneratedBy] Synthetic SIEM Log - TEST DATA로 테스트 로그임을 표시.

왜 이렇게?

  • 로스터로 현실적인 상관관계를 담고, 나머지는 장비 특성에 맞는 필드를 상황별로 더한다.
  • OS별로 build_filename을 달리 생성 → 경로 형태가 OS와 일관.
  • 정책도 choose_policy_by_os로 OS별로 그럴듯하게 분기.

어디 바꾸면?

  • 특정 필드 제거/이름 변경: client_fields/추가필드 배열에서 수정.
  • OS별 파일 경로 취향: build_filename.
  • 정책 로직: choose_policy_by_os.

6) LOG_TYPES

LOG_TYPES = [
    "Device Control - Blocked",
    "Content Aware Protection - Content Threat Blocked",
]
 

왜 이렇게?

  • “어떤 종류의 이벤트를 만들지”의 분포의 뼈대.
  • 간단히 시작하고, 나중에 원하는 타입을 여기 한 줄 추가 + build_log_line에 분기만 추가하면 확장 가능.

어디 바꾸면?

  • 로그 타입 추가/비율 조정: 리스트에 항목 추가 후 build_log_line의 elif 블록 추가.
  • 타입별 가중치를 두고 싶으면 “타입 선택” 부분에서 random.choices로 가중치 적용.

7) SenderThread: 대상별 영구 TCP 송신 스레드

핵심 포인트:

  • 대상별로 큐(Queue)와 스레드 한 개씩.
  • _connect()가 연결을 만들고, 실패하면 지수 백오프(0.5→1→2→… 최대 10초) 재시도.
  • run()에서 큐에서 메시지를 꺼내 sendall. 실패 시 재연결하고 한 번 재시도, 그래도 안 되면 드랍(생산자 보호).
  • 종료 시 stop_flag로 루프 탈출, 소켓 닫음.

왜 이렇게?

  • 고EPS에서 “연결을 매번 만들었다 끊는” 방식은 TIME_WAIT 폭증오버헤드가 큼.
  • Persistent connection은 효율적이고, 재연결 실패 시에도 전체를 멈추지 않고 독립적으로 복구된다.

어디 바꾸면?

  • 드랍 대신 대기(백프레셔): 큐 처리부와 생산자 측 put_nowait를 put(timeout=...)으로 바꾸거나, 생산 루프에서 큐 길이를 보고 sleep을 넣으면 됨.
  • 백오프 한도/초기값: self.backoff와 상한(10.0) 수정.

8) 속도 제어: compute_interval (지터/버스트)

def compute_interval(base_rate, jitter_frac, now_sec, burst_rate=None, burst_period=None, burst_duration=None):
    # 버스트 기간에는 rate = burst_rate, 그 외에는 base_rate
    # interval = 1 / rate
    # interval에 ±(interval*jitter_frac) 범위에서 랜덤 지터
  • 평균 EPS(base_rate) 유지하면서 현실적인 변동성(jitter) 부여.
  • 주기적 버스트로 스파이크 패턴 재현(예: 60초마다 10초 급증).

어디 바꾸면?

  • 지터를 절대값으로 쓰고 싶으면 interval * jitter_frac 대신 고정 초로.
  • 버스트를 요일/시간표 기반으로 하고 싶으면 now_sec 대신 datetime.now()로 캘린더 로직을 적용.

9) main() 프로듀서 루프

  • --roster 로 CSV 로딩 → ClientRoster 인스턴스 생성.
  • 타깃 개수만큼 큐/스레드 생성(SenderThread).
  • 무한 루프:
    1. 현재 시각(datetime.now()), 로그 타입(무작위), 로스터에서 client 1건 선택
    2. build_log_line으로 한 줄 생성
    3. 모든 큐에 put_nowait (가득 차면 드랍)
    4. 옵션 --print면 표준출력 복제
    5. compute_interval로 다음 이벤트까지 슬립

왜 이렇게?

  • 생성(한 줄)모든 타깃에 동시에 보내기를 최우선.
  • 큐 드랍 전략은 고EPS에서 프로듀서가 막히지 않도록 하기 위함(손실 허용형).
  • 손실 불가 시나리오는 “백프레셔 모드”로 바꿔야 함(스루풋 대신 보장).

어디 바꾸면?

  • 손실 방지: put_nowait→put(timeout=...)로 바꾸고, 큐가 차면 생산을 늦춤.
  • 특정 부서/사용자 집중: roster.random_client(dept_weight=...)로 가중치 추가(함수 시그니처 이미 준비돼 있음).

10) 흔한 커스터마이즈 포인트 정리

  • 대상 서버 추가/변경: 상단 TARGET_IPS, TARGET_PORT.
  • 로스터 스키마 확장: CSV 칼럼 추가 → ClientRoster.from_csv에서 읽고 → build_log_line에 필드 추가.
  • 로그 타입 확대: LOG_TYPES에 타입 추가 + build_log_line에 분기/필드 정의.
  • 필드 분포 제어: OS별 목적지/브라우저 확률, 파일 확장자 분포 등은 해당 함수/리스트에서 가중치로 조절.
  • 손실/지연 트레이드오프: 지금은 “속도 우선, 드랍 허용”. 손실 금지면 백프레셔로 전환.
  • 관측성: 초당 전송/실패 카운트 주기 출력(간단한 카운터 + time.monotonic() 으로 1초마다 로그) 추가 가능.

Q: “왜 이렇게 모듈화를 했나?”

  • 생성부송신부를 분리(큐로 decouple)해서, 고EPS에서도 송신 지연/재연결이 생성부를 멈추지 않게 하기 위해서.
  • 대상 서버가 여러 개여도, 각 서버의 문제는 그 서버 스레드 안에서만 고립적으로 처리(회복)되게 하려고.

Q: “실서비스 흉내를 더 내려면?”

  • 버스트 패턴 다양화(예: 근무시간/점심시간/야간)
  • 이벤트 상관규칙(같은 사용자가 n분 내 재시도 → Block)
  • 부서별 정책 차등(재무/법무에 더 빡센 룰/파일형태)
  • 정책/목적지-OS 매핑 강화(Windows↔Outlook, macOS↔Safari 확률 ↑)
728x90
반응형