🔥 Logs Insights: 로그에 쿼리를 발행하는 법
강의 목차

한 쿼리가 500 GB를 스캔하면 CloudWatch 가격표 위에서 $2.50을 청구서에 적는다. 결과가 1만 건이든 0건이든 같다. Logs Insights의 청구는 스캔한 GB 단위이지 결과 줄 단위가 아니다.
Logs: 구조화 로그와 인덱싱 관점에서 "자유 텍스트는 인덱스가 아니라 Logs Insights 스캔 대상"이라는 한 줄을 정의해 두었다. 그 한 줄이 쿼리의 형태, 한 번에 받는 한도, 청구서가 자라는 메커니즘까지 다 정한다.
파이프라인 한 줄로 시작한다
Logs Insights 쿼리 문법의 형태가 단순하다. 파이프 |로 명령을 잇는 한 줄. 가장 자주 쓰는 쿼리는 다섯 명령을 잇는다.
fields @timestamp, @message, @logStream
| filter status >= 500
| parse @message /user_id=(?<user>\S+)/
| stats count() as errors by user
| sort errors desc
| limit 20fields @timestamp, @message, @logStream
| filter status >= 500
| parse @message /user_id=(?<user>\S+)/
| stats count() as errors by user
| sort errors desc
| limit 20fields로 보고 싶은 컬럼을 정하고, filter로 줄을 거르고, parse로 자유 텍스트에서 필드를 꺼내고, stats로 모으고, sort와 limit로 마무리한다. 여기까지가 90%다. 나머지는 공식 명령 18종 안에서 골라 쓴다. display, dedup, unmask, unnest, lookup, pattern(반복 텍스트 구조 찾기), diff(직전 같은 길이 윈도와 비교), anomaly(ML 기반 이상 탐지), filterIndex(아래에서 다시), SOURCE, join, subqueries. 마지막 둘은 2025년에 들어왔다.

쿼리를 짜기 전에 자동으로 채우는 필드가 다섯 개 있다. CloudWatch Logs는 모든 이벤트에 다섯 개 system 필드를 자동 발행한다. @timestamp(이벤트 타임스탬프), @message(원문), @logStream(스트림 이름), @log(account-id:log-group-name 식별자), @ingestionTime(CloudWatch가 받은 시각). 이 다섯이 모든 쿼리의 출발점이다. 거기에 Lambda Log Group은 @requestId, @duration, @billedDuration, @memorySize 같은 필드를 자동으로 더 풀어 준다. 그래서 Lambda에서 한 함수의 cold start 비용을 보고 싶을 때 parse 한 줄 없이 바로 filter @type = "REPORT" | stats max(@billedDuration) by @logStream처럼 들어갈 수 있다. 자동으로 풀린 필드는 콘솔의 Fields 패널에 그대로 떠 있어 클릭으로도 쿼리에 추가한다.

콘솔에서 쿼리를 처음 띄우면 시간 윈도가 직전 1시간으로, limit은 20으로 들어간다. 이게 기본값이다. 그 설정은 콘솔의 시간 선택기에서 직접 옮기거나, StartQuery API의 startTime/endTime으로 정한다. Logs Insights는 2018년 11월 27일 re:Invent에서 GA 발표했고, 그 이후 AWS는 명령을 위처럼 쌓아 왔다.
자유 텍스트는 스캔이다: parse와 field indexes
쿼리가 빠르거나 느린 이유가 여기에 있다. Logs Insights는 검색 인덱스 데이터베이스가 아니라 스캔 엔진이다. CloudWatch란 무엇인가: AWS 모니터링의 중심 도구에서 "풀텍스트 검색은 OpenSearch와 Elasticsearch가 맡는다"고 짚은 그 사실이 여기서 가격으로 직결한다. 스캔이라는 말은 시간 윈도와 LogGroup 목록 안의 모든 이벤트를 한 번 읽는다는 뜻이다. 더 정확히 말하면, 한 번 읽고 메모리에서 파이프라인 명령을 적용한다.

그래서 메시지가 자유 텍스트면 매 쿼리마다 parse 한 줄이 따라붙는다. parse @message /status=(?<status>\d+)/ 같은 정규식 캡처가 한 단계, 거기서 만든 status를 filter가 한 단계, 그렇게 쌓이는 단계 수가 그대로 시간이 된다. parse는 정규식 외에 글로브 패턴도 받아서 잘 정해진 형식의 줄에는 정규식보다 더 빨리 들어간다. Logs: 구조화 로그와 인덱싱 관점에서 본 옵션, 메시지를 JSON으로 적으면 Logs Insights가 자동으로 평탄화해 filter status >= 500을 바로 받는다. 같은 데이터의 운영 가능성이 그래서 한쪽에서만 산다. 파싱이 쿼리 시점이 아니라 운영 시작 시점에 한 번으로 끝나기 때문이다.
스캔 자체를 줄이는 옵션이 2024년 11월에 들어왔다. CloudWatch Logs Insights에 field indexes와 filterIndex 명령을 추가한 것. 자주 쓰는 동등 비교 필드(status, customerId 같은)에 인덱스를 만들어 두면 쿼리는 두 단계로 스캔 범위가 더 좁다. (a) 인덱스가 있는 LogGroup만 스캔에 포함, (b) 그 안에서도 인덱스된 필드가 있는 이벤트만 읽는다. filterIndex는 한 번에 LogGroup name prefix를 다섯 개까지 받아 최대 10,000개 LogGroup을 한 쿼리로 스캔할 수 있다. 단, @message, @timestamp, @log는 인덱스를 못 만든다(그 셋은 본디 자유 텍스트이거나 메타). 그리고 인덱스가 만들어진 시점 이후 들어온 이벤트에만 효과가 붙는다. 과거 로그는 그대로 풀스캔이다.
한 쿼리가 받는 한도
쿼리가 한 번에 받는 분량에 숫자가 명확하게 적혀 있다.

StartQuery API 한 호출에 최대 50개 LogGroup이 들어간다. 결과는 limit을 안 정하면 10,000줄에서 끊고, limit 50000처럼 그보다 큰 숫자를 두면 Row limit exceeded. Maximum: 10000 에러로 거부한다. 결과가 더 필요한 경우는 시간 윈도를 좁혀 여러 번 부르거나, stats로 집계해서 줄 수를 줄인 뒤 받는다.
동시 쿼리 슬롯이 그 다음이다. Standard log class는 region당 Logs Insights QL 동시 100개까지로 2026년 3월에 늘었다(이전은 30). IA log class는 5다. 한 가지 함정. 콘솔에서 사람이 띄운 interactive 쿼리와 dashboard widget, scheduled query, alarm-triggered query가 같은 슬롯을 나눠 쓴다. 자동화 쿼리가 슬롯을 다 채우면 사람이 콘솔에서 쿼리를 못 시작하고 MaxConcurrentLimitExceededException 에러가 돌아온다. dashboard 위젯 한 페이지에 8개를 박아 두고 30초마다 자동 갱신을 켜 두는 흔한 구성에서 그 일이 자주 일어난다. API 자체의 호출 빈도도 따로 제한한다. StartQuery와 GetQueryResults 각각 region당 초당 10회.
저장된 쿼리(saved queries)는 region당 1,000개, 한 쿼리 안 파라미터 20개까지가 firm 한도다. 자주 쓰는 쿼리는 콘솔의 Queries 패널에 저장하면 되는데, 1,000개 한도는 한 사람이 도달하기 힘들고 팀 전체로 누적될 때만 신경 쓴다.
청구서가 결정되는 지점
여기서 한 줄이 살아남는다. Logs Insights 가격은 스캔한 GB다. US East 기준 $0.005/GB. 같은 데이터에 같은 결과를 얻어도 파이프라인 순서로 청구서가 다르다.

# 비싸다. 모든 줄에 stats를 한 번씩
fields @timestamp, status
| stats count() by status
| filter status >= 500
# 싸다. filter가 먼저 줄 수를 줄인다
fields @timestamp, status
| filter status >= 500
| stats count() by status# 비싸다. 모든 줄에 stats를 한 번씩
fields @timestamp, status
| stats count() by status
| filter status >= 500
# 싸다. filter가 먼저 줄 수를 줄인다
fields @timestamp, status
| filter status >= 500
| stats count() by statusLogs Insights 엔진은 명령을 쓴 순서로 적용한다. filter를 가장 앞에 두는 한 줄이 가장 큰 절감이다. 그 다음 절감이 filterIndex. 인덱스 있는 필드가 있다면 filter 부분에 filterIndex를 둔다. 둘 다 못 쓰는 경우(자유 텍스트와 인덱스 없는 그룹)에서는 시간 윈도를 좁히는 게 마지막 카드다. 스캔은 시간 × LogGroup 크기에 정확히 비례한다. 한 시간 윈도로 안 끝나는 경우는 24시간을 한 번 보지 말고 여섯 번에 나눠 보는 식으로 가는 게 비용도 시간도 같이 잡아 준다.
IA log class는 같은 가격으로 Logs Insights 쿼리가 들어가지만 pattern, diff, unmask 세 명령은 못 쓴다. 2026년 3월에 IA에서도 OpenSearch PPL과 SQL이 가능해진 부분은 그 다음. 사후 분석 로그를 IA에 두고도 같은 쿼리 도구가 닿는다.
자연어로 쿼리를 만드는 Query generator는 2024년 6월에 AWS가 GA로 풀었다(2023-11 preview의 후속). 2025년 8월에는 OpenSearch PPL과 SQL까지 자연어로 받게 범위를 한 단계 더 넓혔다. "지난 1시간 동안 5xx 에러를 가장 많이 낸 사용자 상위 10명" 같은 한국어, 영어 자연어를 받아 콘솔이 쿼리 한 줄을 채워 넣는다. 도구 사용은 무료이고, 만들어진 쿼리를 실제 실행할 때만 위 스캔 GB를 청구한다. 쿼리 짜는 게 막히는 상황에서 쓸 만하다.
Insights가 못 하는 영역
같은 콘솔 안에서 쿼리 언어가 셋이 산다. Logs Insights QL 옆에 OpenSearch PPL과 OpenSearch SQL이 같이 떠 있다는 점이 작은 단서다. SQL 익숙한 팀은 PPL/SQL로 들어가고, AWS 표준 쪽이 익숙한 팀은 QL로 들어간다. PPL은 JOIN, Subquery, Cidrmatch, Flatten, JSON 함수 같은 명령을 같은 콘솔에서 받게 2025년 6월에 확장됐다. Logs Insights QL의 join과 subqueries도 같은 시기에 같이 들어와서, 두 언어 모두 관계형 쿼리 형식을 한 단계 더 받는다.
그런데 여기서 한 가지 한계가 드러난다. 수 TB와 수십억 줄 규모의 풀텍스트 인덱스 검색이 필요한 경우는 Logs Insights가 맡는 영역이 아니라 OpenSearch Service가 맡는 영역이다. 인덱스 없이 스캔만으로는 그 규모의 자유 텍스트 쿼리가 비용도 시간도 안 맞는다. OpenSearch는 ingest 단계에서 색인을 만들어 두는 대신 클러스터 비용을 매월 고정으로 받는다. 같은 데이터, 다른 청구 형식. 실시간 라이브 스트림은 또 다른 영역. CloudWatch Live Tail이 시간 단위 고정 단가로 서 있다. Logs Insights가 받는 건 윈도 기준의 지난 데이터다. 콘솔의 다이얼이 "지난 1시간 / 직전 24시간 / 사용자 윈도"인 이유가 거기 있다.
filter를 가장 먼저 둔다
프로덕션에서 매번 같이 두는 결심 묶음을 적는다. filter를 가장 먼저 둔다. 인덱스 있는 필드가 있으면 그 부분에 filterIndex. 시간 윈도는 필요한 만큼만 연다. 메시지를 JSON으로 적은 그룹은 parse를 빼고 들어간다. 자주 쓰는 쿼리는 콘솔에서 저장하고 dashboard widget에 두는 대신, 스캔 GB가 큰 쿼리는 직접 띄우는 곳에만 둔다. 다섯 줄짜리 규칙이지만 청구서가 다르게 나오는 핵심은 결국 이 다섯 줄이 결정한다.
참고 자료
- CloudWatch Logs Insights language query syntax: 18개 명령과 파이프 문법
- Supported logs and discovered fields: 5개 system 필드와 Lambda 자동 필드
- StartQuery API Reference: 한 호출 LogGroup 50개 한도
- CloudWatch Logs quotas: 결과 10,000줄, 동시 쿼리 한도
- Field indexes and
filterIndex: 2024-11 출시 - Increased query concurrency and API limits: 2026-03 동시 쿼리 30→100
- CloudWatch Pricing: 스캔한 GB 단가












