STREAMSTATS 명령어

STREAMSTATS 명령어

streamstats 명령어는 이벤트가 순서대로 처리됨에 따라 누적(누적 합계) 또는 롤링 통계를 계산해요. stats나 eventstats는 전체 데이터셋을 한 번에 처리하지만, streamstats는 이벤트를 점진적으로 처리해서 시계열 및 시퀀스 기반 분석에 적합해요.

출처: 문서

본문

streamstats 명령어는 이벤트가 순서대로 처리됨에 따라 누적 또는 롤링 통계를 계산해요. 전체 데이터셋을 한 번에 처리하는 stats나 eventstats와 달리, streamstats는 이벤트를 점진적으로 처리하므로 시계열 및 시퀀스 기반 분석에 적합해요.

주요 기능으로는 window(슬라이딩 윈도우 계산)와 current(계산에 현재 이벤트 포함 여부) 매개변수 지원, 그리고 이벤트 시퀀스에서 추세 식별이나 변화 감지 같은 특수 사용 사례가 있어요.

stats, eventstats, streamstats 비교

stats, eventstats, streamstats 명령어는 모두 평균, 합, 최댓값 같은 집계를 생성할 수 있어요. 하지만 동작 방식과 결과가 달라요. 다음 표가 이러한 차이를 요약해요.

측면 stats eventstats streamstats
변환 동작 모든 이벤트를 집계된 결과 테이블로 변환해 원래 이벤트 구조를 잃어요 집계 결과를 원래 이벤트에 새 필드로 추가하되 이벤트 구조는 제거하지 않아요 각 이벤트가 파이프라인을 흐르면서 누적(런닝) 집계 결과를 추가해요
출력 형식 집계된 값만 포함해요. 원래 원시 이벤트는 보존되지 않아요 원래 이벤트가 남고, 요약 통계를 담은 추가 필드가 더해져요 원래 이벤트가 남고, 러닝 합계나 누적 통계를 담은 추가 필드가 더해져요
집계 범위 검색의 모든 이벤트(또는 by 절로 정의된 그룹)를 기준으로 해요 해당하는 모든 이벤트를 기준으로 계산한 뒤 그룹의 각 이벤트에 결과를 다시 더해요 각 이벤트가 처리될 때 점진적으로 계산돼요. window로 범위를 지정할 수 있어요
사용 사례 집계된 결과만 필요한 경우(예: 개수, 평균, 합계) 원래 이벤트 데이터와 함께 집계 통계가 필요한 경우 이벤트 스트림 전체에 걸쳐 러닝 합계나 누적 통계가 필요한 경우

구문 (Syntax)

streamstats 명령어의 구문은 다음과 같아요:

streamstats [bucket_nullable=bool] [current=<bool>] [window=<int>] [global=<bool>] [reset_before="("<eval-expression>")"] [reset_after="("<eval-expression>")"] <function>... [by-clause]

다음은 streamstats 명령어 구문의 예시예요:

source = table | streamstats avg(a)
source = table | streamstats current = false avg(a)
source = table | streamstats window = 5 sum(b)
source = table | streamstats current = false window = 2 max(a)
source = table | where a < 50 | streamstats count(c)
source = table | streamstats min(c), max(c) by b
source = table | streamstats count(c) as count_by by b | where count_by > 1000
source = table | streamstats dc(field) as distinct_count
source = table | streamstats distinct_count(category) by region
source = table | streamstats current=false window=2 global=false avg(a) by b
source = table | streamstats window=2 reset_before=a>31 avg(b)
source = table | streamstats current=false reset_after=a>31 avg(b) by c

매개변수 (Parameters)

streamstats 명령어는 다음 매개변수를 지원해요.

매개변수 필수/선택 설명
<function> 필수 집계 함수 또는 윈도우 함수예요.
bucket_nullable 선택 그룹별 집계에서 null 버킷을 유효한 그룹으로 간주할지 여부를 제어해요. false로 설정하면 집계 중 null 그룹 값이 별도 그룹으로 취급되지 않아요. 기본값은 plugins.ppl.syntax.legacy.preferred의 값이에요.
current 선택 요약 계산에 현재 이벤트를 포함할지 여부예요. true면 현재 이벤트를 포함하고, false면 이전 이벤트의 필드 값을 사용해요. 기본값은 true예요.
window 선택 통계 계산에 사용할 이벤트 수예요. 기본값은 0(모든 이전 및 현재 이벤트를 사용)이에요.
global 선택 window가 지정된 경우에만 사용돼요. 단일 윈도우(true)를 사용할지, by 절로 정의된 각 그룹에 대해 별도 윈도우(false)를 사용할지 결정해요. false이고 window가 0이 아니면, by 절에 지정된 각 필드 값 그룹마다 별도 윈도우를 사용해요. 기본값은 true예요.
reset_before 선택 eval-expression이 true로 평가되면 streamstats가 이벤트의 러닝 통계를 계산하기 전에 누적된 모든 통계를 초기화해요. window와 함께 사용하면 윈도우도 초기화돼요. 구문: reset_before="(<eval-expression>)". 기본값은 false예요.
reset_after 선택 eval-expression이 true로 평가되면 streamstats가 이벤트의 러닝 통계를 계산한 후 누적된 모든 통계를 초기화해요. 이 표현식은 streamstats가 반환한 필드를 참조할 수 있어요. window와 함께 사용하면 윈도우도 초기화돼요. 구문: reset_after="(<eval-expression>)". 기본값은 false예요.
<by-clause> 선택 스칼라 함수와 집계 함수를 포함한 그룹핑용 필드 및 표현식이에요. span 절로 특정 필드를 간격별 버킷으로 나눌 수 있어요. 구문: by [span-expression,] [field,]... 지정하지 않으면 모든 이벤트가 단일 그룹으로 처리되고, 전체 이벤트 스트림에 걸쳐 러닝 통계가 계산돼요.
<span-expression> 선택 필드를 간격별 버킷으로 나눠요 (최대 1개). 구문: span(field_expr, interval_expr). 기본적으로 간격은 필드의 기본 단위를 사용해요. 날짜/시간 필드의 경우 집계 결과는 null 값을 무시해요. 예를 들어 span(age, 10)은 10년 단위 나이 버킷을 만들고, span(timestamp, 1h)은 시간 단위 버킷을 만들어요. 유효한 시간 단위는 밀리초(ms), 초(s), 분(m), 시간(h), 일(d), 주(w), 월(M), 분기(q), 년(y)이에요.

집계 함수 (Aggregation functions)

streamstats 명령어는 다음 집계 함수를 지원해요:

  • COUNT – 값의 개수
  • SUM – 숫자 값의 합
  • AVG – 숫자 값의 평균
  • MAX – 최댓값
  • MIN – 최솟값
  • VAR_SAMP – 표본 분산
  • VAR_POP – 모집단 분산
  • STDDEV_SAMP – 표본 표준편차
  • STDDEV_POP – 모집단 표준편차
  • DISTINCT_COUNT / DC – 값의 중복 제외 개수
  • EARLIEST – 타임스탬프 기준 가장 이른 값
  • LATEST – 타임스탬프 기준 가장 최근 값

각 함수의 자세한 문서는 집계 함수 (Aggregation Functions)를 참고해요.

예제 1: 서비스별 오류의 러닝 개수 계산하기

다음 쿼리는 오류 로그의 러닝 개수를 서비스별로 계산해요. 장애 발생 중 서비스 전반에 걸쳐 오류가 어떻게 누적되는지 추적하는 데 유용해요:

source=otellogs
| where severityText IN ('ERROR', 'WARN')
| sort `resource.attributes.service.name`
| streamstats count() as running_count by `resource.attributes.service.name`
| fields `resource.attributes.service.name`, severityText, running_count

쿼리는 다음과 같은 결과를 반환해요:

resource.attributes.service.name | severityText | running_count
checkout | ERROR | 1
checkout | ERROR | 2
frontend-proxy | ERROR | 1
frontend-proxy | WARN | 2
frontend-proxy | WARN | 3
payment | ERROR | 1
payment | ERROR | 2
product-catalog | WARN | 1
product-catalog | WARN | 2
product-catalog | ERROR | 3
recommendation | ERROR | 1

예제 2: 슬라이딩 윈도우에서 러닝 최대 심각도 계산하기

다음 쿼리는 직전 2개 로그 항목에 대한 러닝 최대 심각도 수준을 계산하되 현재 이벤트는 제외해요. 심각도가 최근 패턴을 넘어서는 경우 알림을 보낼 때 유용해요:

source=otellogs
| sort @timestamp
| streamstats current=false window=2 max(severityNumber) as prev_max_severity
| fields @timestamp, severityText, severityNumber, prev_max_severity
| head 6

쿼리는 다음과 같은 결과를 반환해요:

@timestamp | severityText | severityNumber | prev_max_severity
2024-02-01 09:10:00 | INFO | 9 | null
2024-02-01 09:11:00 | INFO | 9 | 9
2024-02-01 09:12:00 | WARN | 13 | 9
2024-02-01 09:13:00 | ERROR | 17 | 13
2024-02-01 09:14:00 | DEBUG | 5 | 17
2024-02-01 09:15:00 | ERROR | 17 | 17

예제 3: 전역 윈도우와 그룹별 윈도우 비교하기

global 매개변수는 다음 값을 가져요:

  • true : 모든 행에 전역 윈도우가 적용되지만, 윈도우 안의 계산은 여전히 by 그룹을 존중해요.
  • false : 윈도우 자체가 그룹별로 만들어져, 각 그룹이 독립적인 윈도우를 받아요.

다음 예제는 다음 데이터를 담은 샘플 인덱스를 사용해요:

name | country | state | month | year | age
Jake | USA | California | 4 | 2023 | 70
Hello | USA | New York | 4 | 2023 | 30
John | Canada | Ontario | 4 | 2023 | 25
Jane | Canada | Quebec | 4 | 2023 | 20
Jim | Canada | B.C | 4 | 2023 | 27
Peter | Canada | B.C | 4 | 2023 | 57
Rick | Canada | B.C | 4 | 2023 | 70
David | USA | Washington | 4 | 2023 | 40

다음 예제는 서로 다른 global 매개변수를 사용해 국가별 계정의 age 러닝 평균을 계산해요.

global=true일 때, 윈도우는 입력 순서대로 모든 행을 가로질러 이동하지만 집계는 여전히 country별로 계산돼요. 슬라이딩 윈도우 크기는 2예요:

source=state_country
| streamstats window=2 global=true avg(age) as running_avg by country

결과적으로 모든 행에 걸쳐 running_avg를 계산할 때 David와 Rick이 같은 슬라이딩 윈도우에 포함돼요:

name | country | state | month | year | age | running_avg
Jake | USA | California | 4 | 2023 | 70 | 70.0
Hello | USA | New York | 4 | 2023 | 30 | 50.0
John | Canada | Ontario | 4 | 2023 | 25 | 25.0
Jane | Canada | Quebec | 4 | 2023 | 20 | 22.5
Jim | Canada | B.C | 4 | 2023 | 27 | 23.5
Peter | Canada | B.C | 4 | 2023 | 57 | 42.0
Rick | Canada | B.C | 4 | 2023 | 70 | 63.5
David | USA | Washington | 4 | 2023 | 40 | 40.0

반면 global=false일 때는 각 by 그룹이 독립적인 스트림과 윈도우를 형성해요:

source=state_country
| streamstats window=2 global=false avg(age) as running_avg by country

David와 Hello가 USA 그룹의 윈도우를 형성해요. 그 결과 David의 running_avg는 이전 사례의 40.0이 아니라 35.0이 돼요:

name | country | state | month | year | age | running_avg
Jake | USA | California | 4 | 2023 | 70 | 70.0
Hello | USA | New York | 4 | 2023 | 30 | 50.0
John | Canada | Ontario | 4 | 2023 | 25 | 25.0
Jane | Canada | Quebec | 4 | 2023 | 20 | 22.5
Jim | Canada | B.C | 4 | 2023 | 27 | 23.5
Peter | Canada | B.C | 4 | 2023 | 57 | 42.0
Rick | Canada | B.C | 4 | 2023 | 70 | 63.5
David | USA | Washington | 4 | 2023 | 40 | 35.0

예제 4: 조건부로 통계 초기화하기

다음 쿼리는 초기화(reset)를 적용하며 국가별 계정의 age 러닝 평균을 계산해요:

source=state_country
| streamstats current=false reset_before=age>34 reset_after=age<25 avg(age) as avg_age by country

쿼리는 다음과 같은 결과를 반환해요:

name | country | state | month | year | age | avg_age
Jake | USA | California | 4 | 2023 | 70 | null
Hello | USA | New York | 4 | 2023 | 30 | 70.0
John | Canada | Ontario | 4 | 2023 | 25 | null
Jane | Canada | Quebec | 4 | 2023 | 20 | 25.0
Jim | Canada | B.C | 4 | 2023 | 27 | null
Peter | Canada | B.C | 4 | 2023 | 57 | null
Rick | Canada | B.C | 4 | 2023 | 70 | null
David | USA | Washington | 4 | 2023 | 40 | null

예제 5: null 버킷 동작

bucket_nullable=false면 그룹별 집계에서 null 값이 제외돼요:

source=accounts
| streamstats bucket_nullable=false count() as cnt by employer
| fields account_number, firstname, employer, cnt

by 필드가 null인 행은 집계에서 제외되므로 Dale의 cnt는 null이 돼요:

account_number | firstname | employer | cnt
1 | Amber | Pyrami | 1
6 | Hattie | Netagy | 1
13 | Nanette | Quility | 1
18 | Dale | null | null

bucket_nullable=true면 null 값이 유효한 그룹으로 취급돼요:

source=accounts
| streamstats bucket_nullable=true count() as cnt by employer
| fields account_number, firstname, employer, cnt

그 결과 Dale의 cnt가 포함되어 정상적으로 계산돼요:

account_number | firstname | employer | cnt
1 | Amber | Pyrami | 1
6 | Hattie | Netagy | 1
13 | Nanette | Quility | 1
18 | Dale | null | 1

더 알아보기 (Learn more)