ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 하네스 엔지니어링 9단계 베스트 프랙티스 #7. 감사 로그
    IT 2026. 4. 27. 22:10
    하네스 엔지니어링 9단계 베스트 프랙티스 #7. 감사 로그

    시리즈 맥락 복기

    #6 HITL 정책에서 Allow/Ask/Deny 등급을 세웠습니다. 이제 그 정책이 실제로 어떻게 동작했는가를 남겨야 합니다. 감사 로그가 없으면 "한 달 전 왜 이 명령이 차단됐는가", "이 파이프라인이 하루에 몇 번 돌았는가" 같은 질문에 답할 수 없습니다.

    1. #0 ~ #6 (이전 단계)
    2. #7 감사 로그 ← 이번 편
    3. #8 측정 + 회고

    이 단계의 의미 — 감사 로그는 하네스의 '블랙박스'

    감사 로그는 말 그대로 항공기의 블랙박스에 해당합니다. 평소엔 존재감이 없지만, 사고가 나거나 개선 포인트를 찾을 때 결정적인 단서가 됩니다. 하네스 엔지니어링에서도 마찬가지로, 에이전트가 무엇을 했는지 쿼리 가능한 형태로 축적해야 이후의 측정·회고가 가능해집니다.

    해결하려는 문제

    • "이 명령이 언제 실행됐지?": 며칠 전 사고 원인을 추적할 수 없으면 재발 방지가 불가합니다.
    • "이 차단은 뭐였지?": Deny가 걸린 이력이 없으면 정책 개선 자료가 사라집니다.
    • "어떤 파이프라인이 제일 많이 도는가?": 실제 사용 빈도를 모르면 최적화 우선순위를 못 정합니다.

    효과 — 로그 하나로 바뀌는 것

    감사 로그가 있으면 "감"에 의존하던 의사결정이 숫자 기반으로 바뀝니다. "자주 쓰는 Skill 상위 3개는 무엇인가", "차단된 명령 중 오탐은 몇 %인가" 같은 질문에 jq 한 줄로 답할 수 있게 됩니다.

    베스트 프랙티스 — JSONL + 자동 태깅

    감사 로그의 형식은 JSONL(JSON Lines)을 권합니다. 파일에 한 줄 = 한 이벤트 구조라서, 중간에 append만 해도 파일이 망가지지 않고 jq 같은 도구로 쉽게 조회할 수 있습니다.

    {
      "ts": "2026-04-15T14:23:11+09:00",
      "tool": "Bash",
      "hook": "PostToolUse",
      "session": 129677,
      "cmd": "git push origin main",
      "exit_code": 0,
      "tags": ["git", "push"],
      "pipeline": "calendar_sync"
    }
    

    필드 설계 원칙

    • ts: ISO 8601 타임존 포함. 나중에 시간별 분석을 위해 필수.
    • tool + hook: 어느 훅에서 발생한 이벤트인지 구분.
    • session: 세션 PID로, 같은 세션의 이벤트를 묶어서 볼 수 있음.
    • cmd: 500자까지만 저장. 긴 스크립트로 로그가 오염되는 걸 방지.
    • exit_code: 성공/실패 판별.
    • tags: 자동 분류 키워드 (아래 참조).
    • pipeline: 어떤 Skill/파이프라인에 속한 작업인지.

    자동 태깅 규칙 — 명령어를 분류하는 9가지 패턴

    명령어에 정규식을 매칭해 태그를 자동 부여합니다. 한 번 정의해 두면 이후 쿼리가 엄청나게 쉬워집니다.

    명령 패턴 태그
    git commit ["git", "commit"]
    git push ["git", "push"]
    gh pr / gh issue ["github", "api"]
    curl / wget ["network"]
    pip install / npm install ["install"]
    rm ["delete"]
    sudo ["sudo"]
    차단된 명령 ["blocked"]
    특정 파이프라인 스크립트 ["pipeline"]

    실제 쿼리 예시

    JSONL이면 jq로 이런 질문을 한 줄씩 바로 답할 수 있습니다.

    # 차단된 명령만 조회
    jq 'select(.tags[] == "blocked")' audit.jsonl
    
    # 특정 날짜의 git 작업
    jq 'select(.ts | startswith("2026-04-15")) | select(.tags[] == "git")' audit.jsonl
    
    # 파이프라인별 실행 횟수 상위
    jq -r '.pipeline // "none"' audit.jsonl | sort | uniq -c | sort -rn
    
    # 세션별 명령 수
    jq -r '.session' audit.jsonl | sort | uniq -c
    

    Hook이 감사 로그를 남기는 구조

    diagram

    설계 3원칙

    1. 실패해도 본 작업을 막지 않는다: 로깅은 try / except: pass로 감싸세요. 로그 실패가 사용자 작업을 중단시키면 안 됩니다.
    2. 명령어는 500자까지만 기록: 긴 스크립트가 파이프로 들어와도 로그가 비대해지지 않도록.
    3. 로그 파일은 .gitignore에 포함: 민감한 명령어가 들어갈 수 있으므로 Git에 올라가지 않게.

    로깅 코드 — 핵심 로직은 20줄

    def log_event(*, tool: str, hook: str, session=None, cmd: str = "",
                  exit_code=None, pipeline=None, extra=None):
        """감사 이벤트를 JSONL로 기록. 실패해도 본 작업을 막지 않는다."""
        AUDIT_LOG.parent.mkdir(parents=True, exist_ok=True)
        entry = {
            "ts": datetime.now().astimezone().isoformat(),
            "tool": tool,
            "hook": hook,
            "session": session,
            "cmd": cmd[:500],          # ← 500자 제한
            "exit_code": exit_code,
            "tags": _classify_tags(cmd),
            "pipeline": pipeline,
        }
        if extra:
            entry.update(extra)
        try:
            with open(AUDIT_LOG, "a") as f:
                f.write(json.dumps(entry, ensure_ascii=False) + "\n")
        except Exception:
            pass                        # ← 로그 실패 무시
    

    이 20줄이 운영 중인 모든 Hook에서 공유됩니다. 한 번 만들어 두면 새 파이프라인을 추가할 때 태그 규칙 한 줄만 더하면 됩니다.

    자주 빠지는 실수

    • JSON 배열로 로그 만들기: 파일 전체가 배열이면 append 할 때마다 끝 괄호를 고쳐야 합니다. 반드시 JSONL을 쓰세요.
    • 타임스탬프에 타임존 누락: 나중에 분석할 때 UTC/KST 혼동이 생깁니다. ISO 8601 타임존 포함 형식이 안전합니다.
    • 민감 명령어를 그대로 저장: 비밀번호·API 키가 포함된 명령은 마스킹하거나 태그로만 기록.

    다음 단계 예고 — Step 8 측정 + 회고

    감사 로그가 쌓이면 드디어 정량 측정이 가능해집니다. 다음 편이자 마지막 편에서는 파이프라인 상태 추적, 세션 격리, 감사 로그 기반의 Before/After 측정 방법을 다룹니다.

    한 줄 요약: "측정 가능한 로그 없이 개선 가능한 자동화는 없다."


    이 글은 생성형 AI의 도움을 받아 작성되었습니다. 원본 자료를 기반으로 AI가 초안을 생성하고, 작성자가 검토·편집하였습니다.

Designed by Tistory.