Key-value processor
출처: 문서
본문
Key-value 프로세서
key_value 프로세서를 사용하면 지정한 필드를 키-값 쌍으로 파싱할 수 있어요. 다음 옵션들을 사용해 key_value 프로세서가 필드 정보를 파싱하는 방식을 커스터마이즈할 수 있어요. 다음 옵션 각각의 타입은 string이에요.
예제
다음 예제들은 이 프로세서에서 사용할 수 있는 여러 가지 설정을 보여줘요.
이 예제들은 보안을 사용하지 않으며 시연 목적으로만 제공돼요. 프로덕션 환경에서 이 예제들을 사용하기 전에 SSL을 설정하는 것을 강력히 권장해요. {: .warning}
키-값 파싱, 정규화, 중복 제거
다음 예제는 message 필드를 key=value 쌍으로 파싱하고, 키를 정규화 및 정리한 뒤 meta_ 접두사를 붙이며, 값을 중복 제거하고, 값이 없는 키는 버려서 parsed_kv에 담아요:
kv-basic-pipeline:
source:
http:
path: /logs
ssl: false
processor:
- key_value:
# Read key=value pairs from the "message" field (default anyway)
source: message
# Write parsed pairs into a nested object "parsed_kv"
destination: parsed_kv
# Split pairs on '&' and split key vs value on '='
field_split_characters: "&"
value_split_characters: "="
# Normalize keys and trim garbage whitespace around keys/values
transform_key: lowercase
delete_key_regex: "\\s+" # remove spaces from keys
delete_value_regex: "^\\s+|\\s+$" # trim leading/trailing spaces
# Add a prefix to every key (after normalization + delete_key_regex)
prefix: meta_
# Keep a single unique value for duplicate keys
skip_duplicate_values: true
# Drop keys whose value is empty/absent (e.g., `empty=` or `novalue`)
drop_keys_with_no_value: true
sink:
- opensearch:
hosts: ["https://opensearch:9200"]
insecure: true
username: admin
password: admin_password
index_type: custom
index: kv-basic-%{yyyy.MM.dd}
{% include copy.html %}
다음 명령어로 이 파이프라인을 테스트할 수 있어요:
curl -sS -X POST "http://localhost:2021/logs" \
-H "Content-Type: application/json" \
-d '[
{"message":"key1=value1&key1=value1&Key Two = value two & empty=&novalue"},
{"message":"ENV = prod & TEAM = core & owner = alice "}
]'
{% include copy.html %}
OpenSearch에 저장된 문서에는 다음 정보가 담겨 있어요:
{
...
"hits": {
"total": {
"value": 2,
"relation": "eq"
},
"max_score": 1,
"hits": [
{
"_index": "kv-basic-2025.10.14",
"_id": "M6d84pkB3P3jd6EROH_f",
"_score": 1,
"_source": {
"message": "key1=value1&key1=value1&Key Two = value two & empty=&novalue",
"parsed_kv": {
"meta_key1": "value1",
"meta_empty": "",
"meta_keytwo": "value two"
}
}
},
{
"_index": "kv-basic-2025.10.14",
"_id": "NKd84pkB3P3jd6EROH_f",
"_score": 1,
"_source": {
"message": "ENV = prod & TEAM = core & owner = alice ",
"parsed_kv": {
"meta_owner": "alice",
"meta_team": "core",
"meta_env": "prod"
}
}
}
]
}
}
그룹화된 값을 루트로 저장
다음 예제는 &&로 쌍을 구분하고 ==로 키와 값을 구분해서 payload 필드를 파싱해요. 괄호로 묶인 그룹은 하나의 값으로 유지하고, 파싱 결과는 기존 필드를 덮어쓰지 않고 이벤트 루트에 기록하며, 매칭되지 않은 토큰은 null로 기록해요:
kv-grouping-pipeline:
source:
http:
path: /logs
ssl: false
processor:
- key_value:
source: "payload"
destination: null
field_split_characters: "&&" # pair delimiter (OK with grouping)
value_split_characters: null # disable the default "="
key_value_delimiter_regex: "==" # exact '==' for key/value
value_grouping: true
remove_brackets: false
overwrite_if_destination_exists: false
non_match_value: null
sink:
- opensearch:
hosts: ["https://opensearch:9200"]
insecure: true
username: admin
password: "admin_pass"
index_type: custom
index: "kv-regex-%{yyyy.MM.dd}"
{% include copy.html %}
다음 명령어로 이 파이프라인을 테스트할 수 있어요:
curl -sS -X POST "http://localhost:2021/logs" \
-H "Content-Type: application/json" \
-d '[
{
"payload":"a==1&&b==[x=y,z=w]&&c==(inner=thing)&&http==http://example.com path",
"a":"keep-me"
},
{
"payload":"good==yes&&broken-token&&url==https://opensearch.org home",
"note":"second doc"
}
]'
{% include copy.html %}
OpenSearch에 저장된 문서에는 다음 정보가 담겨 있어요:
{
...
"hits": {
"total": {
"value": 2,
"relation": "eq"
},
"max_score": 1,
"hits": [
{
"_index": "kv-regex-2025.10.14",
"_id": "FuCX4pkB344hN2Iu62oT",
"_score": 1,
"_source": {
"payload": "a==1&&b==[x=y,z=w]&&c==(inner=thing)&&http==http://example.com path",
"a": "keep-me",
"b": "[x=y,z=w]",
"c": "(inner=thing)",
"http": "http://example.com path"
}
},
{
"_index": "kv-regex-2025.10.14",
"_id": "F-CX4pkB344hN2Iu62oT",
"_score": 1,
"_source": {
"payload": "good==yes&&broken-token&&url==https://opensearch.org home",
"note": "second doc",
"broken-token": null,
"good": "yes",
"url": "https://opensearch.org home"
}
}
]
}
}
조건부 재귀 키-값 파싱
다음 예제는 /type == "nested"인 경우에만 body에서 괄호로 묶인 중첩 key=value 구조를 parsed.*로 파싱해요. 그룹 계층 구조를 유지하고, 엄격한 중첩 규칙을 적용하며, 기본 필드를 적용하고, 중첩되지 않은 이벤트는 그대로 유지해요:
kv-conditional-recursive-pipeline:
source:
http:
path: /logs
ssl: false
processor:
- key_value:
source: "body"
destination: "parsed"
key_value_when: '/type == "nested"'
recursive: true
# Split rules (per docs; not regex)
field_split_characters: "&"
value_split_characters: "="
# Grouping & quoting (per docs)
value_grouping: true
string_literal_character: "\""
remove_brackets: false
# Keep only some top-level keys; then set defaults
include_keys: ["item1","item2","owner"]
default_values:
owner: "unknown"
region: "eu-west-1"
strict_grouping: true
tags_on_failure: ["keyvalueprocessor_failure"]
sink:
- opensearch:
hosts: ["https://opensearch:9200"]
insecure: true
username: admin
password: "admin_pass"
index_type: custom
index: "kv-recursive-%{yyyy.MM.dd}"
{% include copy.html %}
다음 명령어로 이 파이프라인을 테스트할 수 있어요:
curl -sS -X POST "http://localhost:2021/logs" \
-H "Content-Type: application/json" \
-d '[
{
"type":"nested","body":"item1=[a=1&b=(c=3&d=<e=5>)]&item2=2&owner=alice"
},
{
"type":"flat","body":"item1=[should=not&be=parsed]&item2=42"
},
{
"type":"nested","body":"item1=[desc=\"a=b + c=d\"&x=1]&item2=2"
}
]'
{% include copy.html %}
OpenSearch에 저장된 문서에는 다음 정보가 담겨 있어요:
{
...
"hits": {
"total": {
"value": 3,
"relation": "eq"
},
"max_score": 1,
"hits": [
{
"_index": "kv-recursive-2025.10.14",
"_id": "Q7fC4pkBc0UY8I7pZ6vZ",
"_score": 1,
"_source": {
"type": "nested",
"body": "item1=[a=1&b=(c=3&d=<e=5>)]&item2=2&owner=alice",
"parsed": {
"owner": "alice",
"item2": "2",
"item1": {
"a": "1",
"b": {
"c": "3",
"d": {
"e": "5"
}
}
},
"region": "eu-west-1"
}
}
},
{
"_index": "kv-recursive-2025.10.14",
"_id": "RLfC4pkBc0UY8I7pZ6vZ",
"_score": 1,
"_source": {
"type": "flat",
"body": "item1=[should=not&be=parsed]&item2=42"
}
},
{
"_index": "kv-recursive-2025.10.14",
"_id": "RbfC4pkBc0UY8I7pZ6vZ",
"_score": 1,
"_source": {
"type": "nested",
"body": """item1=[desc="a=b + c=d"&x=1]&item2=2""",
"parsed": {
"owner": "unknown",
"item2": "2",
"item1": {
"desc": "\"a=b + c=d\"",
"x": "1"
},
"region": "eu-west-1"
}
}
}
]
}
}
설정
| 옵션 | 설명 | 예시 |
|---|---|---|
source |
파싱할 메시지 필드예요. 선택 사항이며 기본값은 message예요. |
source가 "message1"이면 {"message1": {"key1=value1"}, "message2": {"key2=value2"}}가 {"message1": {"key1=value1"}, "message2": {"key2=value2"}, "parsed_message": {"key1": "value1"}}로 파싱돼요. |
destination |
파싱된 소스가 저장될 대상 필드예요. 파싱된 소스는 해당 키의 기존 데이터를 덮어써요. 선택 사항이에요. destination을 null로 설정하면 파싱된 필드가 이벤트 루트에 기록돼요. 기본값은 parsed_message예요. |
destination이 "parsed_data"이면 {"message": {"key1=value1"}}이 {"message": {"key1=value1"}, "parsed_data": {"key1": "value1"}}로 파싱돼요. |
field_delimiter_regex |
키-값 쌍을 구분하는 구분자를 지정하는 정규식이에요. [와 ] 같은 특수 정규식 문자는 \\로 이스케이프해야 해요. field_split_characters와 동시에 정의할 수 없어요. 선택 사항이며, 이 옵션이 정의되지 않으면 field_split_characters가 사용돼요. |
field_delimiter_regex가 "&\\{2\\}"이면 {"key1=value1&&key2=value2"}가 {"key1": "value1", "key2": "value2"}로 파싱돼요. |
field_split_characters |
키-값 쌍을 구분하는 구분자를 지정하는 문자열이에요. [와 ] 같은 특수 정규식 문자는 \\로 이스케이프해야 해요. field_delimiter_regex와 동시에 정의할 수 없어요. 선택 사항이며 기본값은 &예요. |
field_split_characters가 "&&"이면 {"key1=value1&&key2=value2"}가 {"key1": "value1", "key2": "value2"}로 파싱돼요. |
key_value_delimiter_regex |
키-값 쌍 내에서 키와 값을 구분하는 구분자를 지정하는 정규식이에요. [와 ] 같은 특수 정규식 문자는 \\로 이스케이프해야 해요. 이 옵션은 value_split_characters와 동시에 정의할 수 없어요. 선택 사항이며, 이 옵션이 정의되지 않으면 value_split_characters가 사용돼요. |
key_value_delimiter_regex가 "=\\{2\\}"이면 {"key1==value1"}이 {"key1": "value1"}로 파싱돼요. |
value_split_characters |
키-값 쌍 내에서 키와 값을 구분하는 구분자를 지정하는 문자열이에요. [와 ] 같은 특수 정규식 문자는 \\로 이스케이프해야 해요. key_value_delimiter_regex와 동시에 정의할 수 없어요. 선택 사항이며 기본값은 =예요. |
value_split_characters가 "=="이면 {"key1==value1"}이 {"key1": "value1"}로 파싱돼요. |
non_match_value |
키-값 쌍을 제대로 분리하지 못하면 해당 키-값 쌍이 key 필드에 담기고, 지정한 값이 value 필드에 담겨요. 선택 사항이며 기본값은 null이에요. |
key1value1&key2=value2는 {"key1value1": null, "key2": "value2"}로 파싱돼요. |
prefix |
모든 키 앞에 붙일 접두사예요. 선택 사항이며 기본값은 빈 문자열이에요. | prefix가 "custom"이면 {"key1=value1"}이 {"customkey1": "value1"}로 파싱돼요. |
delete_key_regex |
키에서 삭제할 문자를 지정하는 정규식이에요. [와 ] 같은 특수 정규식 문자는 \\로 이스케이프해야 해요. 빈 문자열일 수 없어요. 선택 사항이며 기본값은 없어요. |
delete_key_regex가 "\s"이면 {"key1 =value1"}이 {"key1": "value1"}로 파싱돼요. |
delete_value_regex |
값에서 삭제할 문자를 지정하는 정규식이에요. [와 ] 같은 특수 정규식 문자는 \\로 이스케이프해야 해요. 빈 문자열일 수 없어요. 선택 사항이며 기본값은 없어요. |
delete_value_regex가 "\s"이면 {"key1=value1 "}이 {"key1": "value1"}로 파싱돼요. |
include_keys |
파싱 시 추가할 키를 지정하는 배열이에요. 기본적으로 모든 키가 추가돼요. | include_keys가 ["key2"]이면,key1=value1&key2=value2가 {"key2": "value2"}로 파싱돼요. |
exclude_keys |
이벤트에 추가하지 않을 파싱된 키를 지정하는 배열이에요. 기본적으로 제외되는 키는 없어요. | exclude_keys가 ["key2"]이면 key1=value1&key2=value2가 {"key1": "value1"}로 파싱돼요. |
default_values |
파싱하는 소스 필드에 해당 키가 없을 때 이벤트에 추가할 기본 키와 값을 지정하는 맵이에요. 기본 키가 이미 메시지에 존재하면 값은 변경되지 않아요. include_keys 필터는 default_values보다 먼저 메시지에 적용돼요. |
default_values가 {"defaultkey": "defaultvalue"}이면 key1=value1이 {"key1": "value1", "defaultkey": "defaultvalue"}로 파싱돼요. default_values가 {"key1": "abc"}이면 key1=value1이 {"key1": "value1"}로 파싱돼요. include_keys가 ["key1"]이고 default_values가 {"key2": "value2"}이면 key1=value1&key2=abc가 {"key1": "value1", "key2": "value2"}로 파싱돼요. |
transform_key |
키를 소문자로, 대문자로, 또는 첫 글자만 대문자로 변환할 때 사용해요. | transform_key가 lowercase이면 {"Key1=value1"}이 {"key1": "value1"}로 파싱돼요. transform_key가 uppercase이면 {"key1=value1"}이 {"KEY1": "value1"}로 파싱돼요. transform_key가 capitalize이면 {"key1=value1"}이 {"Key1": "value1"}로 파싱돼요. |
whitespace |
설정된 값 분리 시퀀스 주변의 불필요한 공백을 관대하게 허용할지 엄격하게 처리할지 지정해요. 기본값은 lenient예요. |
whitespace가 "lenient"이면 {"key1 = value1"}이 {"key1 ": " value1"}로 파싱돼요. whitespace가 "strict"이면 {"key1 = value1"}이 {"key1": "value1"}로 파싱돼요. |
skip_duplicate_values |
중복된 키-값 쌍을 제거하는 불리언 옵션이에요. true로 설정하면 유일한 키-값 쌍 하나만 유지돼요. 기본값은 false예요. |
skip_duplicate_values가 false이면 {"key1=value1&key1=value1"}이 {"key1": ["value1", "value1"]}로 파싱돼요. skip_duplicate_values가 true이면 {"key1=value1&key1=value1"}이 {"key1": "value1"}로 파싱돼요. |
remove_brackets |
대괄호, 꺾쇠 괄호, 소괄호를 값에서 제거해야 하는 값 "래퍼"로 취급할지 지정해요. 기본값은 false예요. |
remove_brackets가 true이면 {"key1=(value1)"}이 {"key1": value1}로 파싱돼요. remove_brackets가 false이면 {"key1=(value1)"}이 {"key1": "(value1)"}로 파싱돼요. |
recursive |
값에서 추가 키-값 쌍을 재귀적으로 가져올지 지정해요. 추가 키-값 쌍은 루트 키의 하위 키로 저장돼요. 기본값은 false예요. 재귀 파싱의 각 레벨은 레벨마다 서로 다른 괄호로 정의해야 하며, 순서는 [], (), <>예요. 지정한 다른 설정들은 가장 바깥쪽 키에만 적용돼요. recursive가 true일 때: remove_brackets는 true로 설정할 수 없어요;skip_duplicate_values는 항상 true예요; whitespace는 항상 "strict"예요. |
recursive가 true이면 {"item1=[item1-subitem1=item1-subitem1-value&item1-subitem2=(item1-subitem2-subitem2A=item1-subitem2-subitem2A-value&item1-subitem2-subitem2B=item1-subitem2-subitem2B-value)]&item2=item2-value"}가 {"item1": {"item1-subitem1": "item1-subitem1-value", "item1-subitem2" {"item1-subitem2-subitem2A": "item1-subitem2-subitem2A-value", "item1-subitem2-subitem2B": "item1-subitem2-subitem2B-value"}}}로 파싱돼요. |
overwrite_if_destination_exists |
파싱된 필드를 이벤트에 기록할 때 키 충돌이 있으면 기존 필드를 덮어쓸지 지정해요. 기본값은 true예요. |
overwrite_if_destination_exists가 true이고 destination이 null이면 {"key1": "old_value", "message": "key1=new_value"}가 {"key1": "new_value", "message": "key1=new_value"}로 파싱돼요. |
tags_on_failure |
프로세서 내에서 kv 작업이 런타임 예외를 일으키면 프로세서가 크래시되지 않고 작업이 안전하게 중단되며, 이벤트에 지정한 태그가 붙어요. |
tags_on_failure가 ["keyvalueprocessor_failure"]로 설정되어 있으면 런타임 예외 발생 시 이벤트 메타데이터에 {"tags": ["keyvalueprocessor_failure"]}가 추가돼요. |
value_grouping |
미리 정의된 값 그룹화 구분자인 {...}, [...]', <...>, (...), "...", '...', http://... (space), https:// (space)를 사용해 값을 그룹화할지 지정해요. 이 플래그가 활성화되면 구분자 사이의 내용은 하나의 엔티티로 간주되어 키-값 쌍으로 파싱되지 않아요. 기본값은 false예요. value_grouping이 true이면 {"key1=[a=b,c=d]&key2=value2"}가 {"key1": "[a=b,c=d]", "key2": "value2"}`로 파싱돼요. |
|
drop_keys_with_no_value |
값이 null인 키를 버릴지 지정해요. 기본값은 false예요. drop_keys_with_no_value가 true로 설정되면 {"key1=value1&key2"}가 {"key1": "value1"}로 파싱돼요. |
|
strict_grouping |
value_grouping 또는 string_literal_character 옵션을 사용할 때 엄격한 그룹화를 활성화할지 지정해요. 기본값은 false예요. |
활성화하면 끝 문자가 짝이 맞지 않는 그룹에서 오류가 발생해요. 오류가 로그로 기록된 후 해당 이벤트는 무시돼요. |
string_literal_character |
작은따옴표(') 또는 큰따옴표(")로 설정할 수 있어요. 기본값은 null이에요. |
이 옵션을 사용하면 지정한 따옴표 문자로 감싸인 텍스트는 무시되고 키-값 파싱에서 제외돼요. 예를 들어 text1 "key1=value1" text2 key2=value2는 {"key2": "value2"}로 파싱돼요. |
key_value_when |
프로세서를 이벤트에 적용할지 판단하기 위해 평가되는 조건식을 지정할 수 있어요. 예를 들어 /some-key == "test" 같은 식이에요. |