Datadog 로그 검색을 위한 자연어 인터페이스

발행: (2025년 12월 3일 오전 04:45 GMT+9)
7 min read
원문: Dev.to

Source: Dev.to

소개

오전 2시. PagerDuty가 울린다. 결제 서비스에 문제가 생겼다.
Log Explorer를 열고 쿼리 바를 바라본다. service:payment이어야 할까 @service:payment이어야 할까? 부정은 NOT을 써야 할까 -를 써야 할까? 인증 실패에 대한 facet은 뭐였지?

로그에 답이 있지만, 구문이 걸림돌이다. 이 글에서는 자연어를 Datadog Log Search 쿼리로 변환하는 도구를 만드는 과정을 살펴보고, 이 문제를 흥미롭게 만드는 Datadog‑특화 함정들을 분석한다.

로그 검색 구문이 사람들을 헷갈리게 하는 이유

@ 접두사 규칙

예약된 속성은 @를 사용하지 않는다. 이것들은 Datadog이 기본 제공하는 핵심 필드다:

service:payment-service
status:error
host:web-server-01

사용자 정의 facet 및 로그 속성은 @가 필요하다. 직접 인덱싱한 모든 항목:

@http.status_code:500
@duration:>2000000000
@error.message:*timeout*

접두사를 뒤바꾸면 결과가 전혀 나오지 않는다—오류는 없고 빈 집합만 반환된다. 압박 속에서 디버깅할 때 특히 답답하다.

Duration 함정

Datadog은 나노초 단위로 duration을 저장한다. 초나 밀리초가 아니다.

2 초 이상 요청을 필터링하려면:

@duration:>2000000000

0을 하나 빼면 200 ms를, 하나 더 추가하면 20 초 요청을 찾게 된다. 사고 발생 시 이 실수는 시간을 낭비한다.

덜 흔한 Facet

Cloud SIEM이나 감사 로그를 다룰 때는 다음과 같은 facet을 마주한다:

@evt.name:authentication
@evt.outcome:failure
@network.client.geoip.country_name:*

일상적으로 사용되지 않기 때문에 기억에 남지 않지만, 의심스러운 활동을 조사할 때 정확히 필요한 쿼리이다.

LLM을 위한 프롬프트 엔지니어링

LLM은 학습 데이터에 Datadog 쿼리를 본 적이 있지만, 신뢰하기엔 충분하지 않다. 겉보기엔 타당해 보이지만 미묘하게 틀린 구문을 생성하곤 한다. 해결책은 시스템 프롬프트에 규칙을 명시적으로 적는 것이다:

예약된 속성 (@ 접두사 없음)

service:payment-service
status:error
host:web-server-01

Facet 및 사용자 정의 속성 (@ 접두사 필요)

@http.status_code:500
@duration:>1000000000   # 나노초
@error.message:*timeout*

피해야 할 흔한 실수

  • @service:payment – 잘못된 사용 (예약된 속성)
  • @duration:>2 – 잘못된 사용 (나노초가 아님)

나노초에 대한 언급은 핵심이다; 모델이 “2초 이상 요청”에 대해 @duration:>2를 생성하면 완전히 틀린 결과가 된다.

보안 패턴

추측하기 어려운 facet에 대해서는 명시적인 예시가 필요하다:

# 인증 실패
@evt.name:authentication @evt.outcome:failure

# CloudTrail 콘솔 로그인
source:cloudtrail @evt.name:ConsoleLogin

# 외부 IP만
NOT @network.client.ip:10.* NOT @network.client.ip:192.168.*

이러한 프롬프트는 대략 80 % 정확도를 제공한다. 남은 20 %는 엣지 케이스, 희귀 facet 이름, 통합‑특화 속성, 그리고 정적 프롬프트로는 다루기 힘든 구문 변형이다.

Retrieval‑Augmented Generation

엣지 케이스를 처리하기 위해 Datadog 문서를 색인하고, 쿼리 시 관련 섹션을 검색해 프롬프트에 삽입한다.

이중 검색 방법

  1. Dense embeddings (예: OpenAI text-embedding-3-large)는 의미적 유사성을 포착한다.
  2. Sparse embeddings (예: SPLADE)는 정확한 키워드 겹침을 포착해 @evt.outcome 같은 문자열을 찾는다.

두 결과 집합을 Reciprocal Rank Fusion (RRF) 으로 병합한다:

results = qdrant_client.query_points(
    collection_name=collection,
    prefetch=[
        Prefetch(query=dense_vector, using="dense", limit=limit * 2),
        Prefetch(query=sparse_vector, using="sparse", limit=limit * 2),
    ],
    query=FusionQuery(fusion=Fusion.RRF),
    limit=limit,
)

이 조합은 개념적 매치와 정확한 구문 매치를 모두 제공해, 정적 프롬프트가 놓친 엣지 케이스를 커버한다.

시스템 사용법

검증 도구로서의 설명

복잡한 쿼리를 가진 대시보드를 인수받을 때:

@evt.name:authentication @evt.outcome:failure \
NOT @network.client.ip:10.* NOT @network.client.ip:192.168.* \
NOT @network.client.ip:172.16.*

어시스턴트에게 “이 쿼리는 무엇을 하는가?”라고 물으면 다음과 같은 답을 얻는다:

“내부 네트워크 범위를 벗어난 IP에서 발생한 인증 실패 시도.”

이를 통해 이해 속도를 높이고, 모델에게 쿼리를 설명하도록 요청해 생성된 쿼리를 검증할 수 있다.

자연어 → 로그 검색 예시

입력 (자연어)생성된 Log Search 쿼리
“결제 서비스에서 발생한 오류”service:payment-service status:error
“2초 이상 걸리는 느린 요청”@duration:>2000000000
“외부 IP에서 발생한 로그인 실패”@evt.name:authentication @evt.outcome:failure NOT @network.client.ip:10.* NOT @network.client.ip:192.168.*

보안 쿼리가 가장 큰 가치를 제공한다: 엔지니어는 관측성 쿼리(service:api status:error)는 외우지만, SIEM‑스타일 facet은 거의 사용하지 않기 때문에 이 도구가 특히 유용하다.

결론

이 프로젝트의 흥미로운 부분은 LLM 통합 자체가 아니라, Datadog‑특화 세부 사항을 충분히 학습해 모델에 가르치는 과정이었다. @ 접두사 규칙, 나노초 함정, 보안 facet 패턴이 작동하는 쿼리와 조용히 실패하는 쿼리를 구분한다. 이러한 지식을 명시적으로 인코딩하고, 문서 검색을 통해 보강하면 도구를 신뢰할 수 있게 만든다.

구현을 살펴보고 싶다면 코드는 GitHub에 있다.

표현된 의견은 개인적인 것이며, 고용주를 대변하지 않는다.

Back to Blog

관련 글

더 보기 »

계정 전환

@blink_c5eb0afe3975https://dev.to/blink_c5eb0afe3975 여러분도 알다시피 저는 다시 제 진행 상황을 기록하기 시작했으니, 이것을 다른…