Elasticsearchを使用した時系列データ
Elasticsearchは、ログやメトリクスなどの時系列データを保存、管理、検索するための機能を提供します。一度Elasticsearchにデータを取り込むと、Kibanaや他のElastic Stackの機能を使用してデータを分析および視覚化できます。
データティアの設定
ElasticsearchのILM機能は、データティアを使用して、古いデータを年齢に応じてコストの低いハードウェアを持つノードに自動的に移動します。これにより、パフォーマンスが向上し、ストレージコストが削減されます。
ホットティアとコンテンツティアは必須です。ウォーム、コールド、フローズンティアはオプションです。
ホットティアとウォームティアでは、高性能のノードを使用して、最新のデータに対するインデックス作成と検索を高速化します。コールドティアとフローズンティアでは、コストを削減するために、遅いコストの低いノードを使用します。
コンテンツティアは通常、時系列データには使用されません。ただし、システムインデックスやデータストリームの一部でない他のインデックスを作成するには必要です。
データティアの設定手順は、デプロイメントの種類によって異なります:
- 1. Elasticsearch Service Consoleにログインします。
- 2. Elasticsearch Serviceのホームページまたはデプロイメントページから、デプロイメントを追加または選択します。
- 3. デプロイメントメニューからデプロイメントの編集を選択します。
- 4. データティアを有効にするには、容量を追加をクリックします。
オートスケーリングを有効にする
オートスケーリングは、ストレージニーズに応じてデプロイメントの容量を自動的に調整します。オートスケーリングを有効にするには、デプロイメントの編集ページでこのデプロイメントをオートスケールを選択します。オートスケーリングはElasticsearch Serviceでのみ利用可能です。
ノードをデータティアに割り当てるには、ノードのelasticsearch.yml
ファイルにそれぞれのnode roleを追加します。既存のノードの役割を変更するには、ローリング再起動が必要です。
Yaml
# コンテンツティア
node.roles: [ data_content ]
# ホットティア
node.roles: [ data_hot ]
# ウォームティア
node.roles: [ data_warm ]
# コールドティア
node.roles: [ data_cold ]
# フローズンティア
node.roles: [ data_frozen ]
フローズンティアでは専用ノードを使用することをお勧めします。必要に応じて、他のノードを複数のティアに割り当てることができます。
Yaml
node.roles: [ data_content, data_hot, data_warm ]
クラスターに必要な他の役割をノードに割り当てます。たとえば、小さなクラスターには複数の役割を持つノードがある場合があります。
Yaml
node.roles: [ master, ingest, ml, data_hot, transform ]
スナップショットリポジトリの登録
コールドティアとフローズンティアは、検索可能なスナップショットを使用してローカルストレージコストを削減できます。
検索可能なスナップショットを使用するには、サポートされているスナップショットリポジトリを登録する必要があります。このリポジトリを登録する手順は、デプロイメントの種類とストレージプロバイダーによって異なります:
クラスターを作成すると、Elasticsearch Serviceはデフォルトのfound-snapshots
リポジトリを自動的に登録します。このリポジトリは検索可能なスナップショットをサポートしています。
次のカスタムリポジトリタイプのいずれかを検索可能なスナップショットとともに使用できます:
- [Google Cloud Storage (GCS)](https://www.elastic.co/guide/en/cloud/current/ec-gcs-snapshotting.html)
- [Azure Blob Storage](https://www.elastic.co/guide/en/cloud/current/ec-azure-snapshotting.html)
- [Amazon Web Services (AWS)](https://www.elastic.co/guide/en/cloud/current/ec-aws-custom-repository.html)
次のリポジトリタイプのいずれかを検索可能なスナップショットとともに使用できます:
- [AWS S3](/read/elasticsearch-8-15/094325de711979fa.md)
- [Google Cloud Storage](/read/elasticsearch-8-15/dfd43ab9354680ac.md)
- [Azure Blob Storage](/read/elasticsearch-8-15/d888d8b822a8a1ce.md)
- [Hadoop Distributed File Store (HDFS)](https://www.elastic.co/guide/en/elasticsearch/plugins/8.15/repository-hdfs.html)
- [共有ファイルシステム](/read/elasticsearch-8-15/42b9d9b87a4241d0.md)(NFSなど)
- [読み取り専用HTTPおよびHTTPSリポジトリ](/read/elasticsearch-8-15/e140b4739f315eab.md)
これらのリポジトリタイプの代替実装(たとえば、[MinIO](094325de711979fa.md#repository-s3-client))を使用することもできますが、完全に互換性がある必要があります。[リポジトリアナリシス](/read/elasticsearch-8-15/8c7630461481c6c0.md)APIを使用して、検索可能なスナップショットとの互換性を分析します。
## インデックスライフサイクルポリシーの作成または編集
[データストリーム](/read/elasticsearch-8-15/4618071bf1c879cb.md)は、複数のバックインデックスにわたってデータを保存します。ILMは、これらのインデックスをデータティア間で自動的に移動するために[インデックスライフサイクルポリシー](/read/elasticsearch-8-15/4188a8e0ae3d047f.md)を使用します。
FleetまたはElastic Agentを使用している場合は、Elasticsearchの組み込みライフサイクルポリシーの1つを編集します。カスタムアプリケーションを使用している場合は、自分のポリシーを作成します。いずれの場合も、ポリシーが次のことを確認してください:
- 構成した各データティアのフェーズを含む。
- ロールオーバーからのフェーズ遷移のためのしきい値、または`````min_age`````を計算する。
- 必要に応じて、コールドおよびフローズンフェーズで検索可能なスナップショットを使用する。
- 必要に応じて削除フェーズを含む。
FleetとElastic Agentは、次の組み込みライフサイクルポリシーを使用します:
- `````logs
metrics
synthetics
パフォーマンス、耐障害性、保持要件に基づいて、これらのポリシーをカスタマイズできます。
Kibanaでポリシーを編集するには、メインメニューを開き、Stack Management >
Index Lifecycle Policiesに移動します。編集したいポリシーをクリックします。
また、ライフサイクルポリシーの更新APIを使用することもできます。
Python
resp = client.ilm.put_lifecycle(
name="logs",
policy={
"phases": {
"hot": {
"actions": {
"rollover": {
"max_primary_shard_size": "50gb"
}
}
},
"warm": {
"min_age": "30d",
"actions": {
"shrink": {
"number_of_shards": 1
},
"forcemerge": {
"max_num_segments": 1
}
}
},
"cold": {
"min_age": "60d",
"actions": {
"searchable_snapshot": {
"snapshot_repository": "found-snapshots"
}
}
},
"frozen": {
"min_age": "90d",
"actions": {
"searchable_snapshot": {
"snapshot_repository": "found-snapshots"
}
}
},
"delete": {
"min_age": "735d",
"actions": {
"delete": {}
}
}
}
},
)
print(resp)
Js
const response = await client.ilm.putLifecycle({
name: "logs",
policy: {
phases: {
hot: {
actions: {
rollover: {
max_primary_shard_size: "50gb",
},
},
},
warm: {
min_age: "30d",
actions: {
shrink: {
number_of_shards: 1,
},
forcemerge: {
max_num_segments: 1,
},
},
},
cold: {
min_age: "60d",
actions: {
searchable_snapshot: {
snapshot_repository: "found-snapshots",
},
},
},
frozen: {
min_age: "90d",
actions: {
searchable_snapshot: {
snapshot_repository: "found-snapshots",
},
},
},
delete: {
min_age: "735d",
actions: {
delete: {},
},
},
},
},
});
console.log(response);
コンソール
PUT _ilm/policy/logs
{
"policy": {
"phases": {
"hot": {
"actions": {
"rollover": {
"max_primary_shard_size": "50gb"
}
}
},
"warm": {
"min_age": "30d",
"actions": {
"shrink": {
"number_of_shards": 1
},
"forcemerge": {
"max_num_segments": 1
}
}
},
"cold": {
"min_age": "60d",
"actions": {
"searchable_snapshot": {
"snapshot_repository": "found-snapshots"
}
}
},
"frozen": {
"min_age": "90d",
"actions": {
"searchable_snapshot": {
"snapshot_repository": "found-snapshots"
}
}
},
"delete": {
"min_age": "735d",
"actions": {
"delete": {}
}
}
}
}
}
Kibanaでポリシーを作成するには、メインメニューを開き、Stack Management >
Index Lifecycle Policiesに移動します。ポリシーを作成をクリックします。
また、ライフサイクルポリシーの更新APIを使用することもできます。
Python
resp = client.ilm.put_lifecycle(
name="my-lifecycle-policy",
policy={
"phases": {
"hot": {
"actions": {
"rollover": {
"max_primary_shard_size": "50gb"
}
}
},
"warm": {
"min_age": "30d",
"actions": {
"shrink": {
"number_of_shards": 1
},
"forcemerge": {
"max_num_segments": 1
}
}
},
"cold": {
"min_age": "60d",
"actions": {
"searchable_snapshot": {
"snapshot_repository": "found-snapshots"
}
}
},
"frozen": {
"min_age": "90d",
"actions": {
"searchable_snapshot": {
"snapshot_repository": "found-snapshots"
}
}
},
"delete": {
"min_age": "735d",
"actions": {
"delete": {}
}
}
}
},
)
print(resp)
Js
const response = await client.ilm.putLifecycle({
name: "my-lifecycle-policy",
policy: {
phases: {
hot: {
actions: {
rollover: {
max_primary_shard_size: "50gb",
},
},
},
warm: {
min_age: "30d",
actions: {
shrink: {
number_of_shards: 1,
},
forcemerge: {
max_num_segments: 1,
},
},
},
cold: {
min_age: "60d",
actions: {
searchable_snapshot: {
snapshot_repository: "found-snapshots",
},
},
},
frozen: {
min_age: "90d",
actions: {
searchable_snapshot: {
snapshot_repository: "found-snapshots",
},
},
},
delete: {
min_age: "735d",
actions: {
delete: {},
},
},
},
},
});
console.log(response);
コンソール
PUT _ilm/policy/my-lifecycle-policy
{
"policy": {
"phases": {
"hot": {
"actions": {
"rollover": {
"max_primary_shard_size": "50gb"
}
}
},
"warm": {
"min_age": "30d",
"actions": {
"shrink": {
"number_of_shards": 1
},
"forcemerge": {
"max_num_segments": 1
}
}
},
"cold": {
"min_age": "60d",
"actions": {
"searchable_snapshot": {
"snapshot_repository": "found-snapshots"
}
}
},
"frozen": {
"min_age": "90d",
"actions": {
"searchable_snapshot": {
"snapshot_repository": "found-snapshots"
}
}
},
"delete": {
"min_age": "735d",
"actions": {
"delete": {}
}
}
}
}
}
コンポーネントテンプレートの作成
FleetまたはElastic Agentを使用している場合は、データの検索と視覚化にスキップします。FleetとElastic Agentは、データストリームを作成するために組み込みテンプレートを使用します。
カスタムアプリケーションを使用している場合は、自分のデータストリームを設定する必要があります。データストリームには、一致するインデックステンプレートが必要です。ほとんどの場合、1つ以上のコンポーネントテンプレートを使用してこのインデックステンプレートを構成します。通常、マッピングとインデックス設定のために別々のコンポーネントテンプレートを使用します。これにより、複数のインデックステンプレートでコンポーネントテンプレートを再利用できます。
コンポーネントテンプレートを作成する際には、次のことを含めます:
date
](/read/elasticsearch-8-15/9dfa1da42eb162ff.md)またはdate_nanos
マッピングを@timestamp
フィールドに対して指定します。マッピングを指定しない場合、Elasticsearchは@timestamp
をデフォルトオプションのdate
フィールドとしてマッピングします。index.lifecycle.name
インデックス設定でのライフサイクルポリシー。
フィールドをマッピングする際には、Elastic Common Schema (ECS)を使用してください。ECSフィールドは、デフォルトでいくつかのElastic Stack機能と統合されます。
フィールドのマッピング方法が不明な場合は、ランタイムフィールドを使用して、検索時に非構造化コンテンツからフィールドを抽出できます。たとえば、ログメッセージをwildcard
フィールドにインデックスし、後で検索中にこのフィールドからIPアドレスや他のデータを抽出できます。
Kibanaでコンポーネントテンプレートを作成するには、メインメニューを開き、Stack
Management > Index Managementに移動します。インデックステンプレートビューで、コンポーネントテンプレートを作成をクリックします。
また、コンポーネントテンプレート作成APIを使用することもできます。
Python
resp = client.cluster.put_component_template(
name="my-mappings",
template={
"mappings": {
"properties": {
"@timestamp": {
"type": "date",
"format": "date_optional_time||epoch_millis"
},
"message": {
"type": "wildcard"
}
}
}
},
meta={
"description": "Mappings for @timestamp and message fields",
"my-custom-meta-field": "More arbitrary metadata"
},
)
print(resp)
resp1 = client.cluster.put_component_template(
name="my-settings",
template={
"settings": {
"index.lifecycle.name": "my-lifecycle-policy"
}
},
meta={
"description": "Settings for ILM",
"my-custom-meta-field": "More arbitrary metadata"
},
)
print(resp1)
Ruby
response = client.cluster.put_component_template(
name: 'my-mappings',
body: {
template: {
mappings: {
properties: {
"@timestamp": {
type: 'date',
format: 'date_optional_time||epoch_millis'
},
message: {
type: 'wildcard'
}
}
}
},
_meta: {
description: 'Mappings for @timestamp and message fields',
"my-custom-meta-field": 'More arbitrary metadata'
}
}
)
puts response
response = client.cluster.put_component_template(
name: 'my-settings',
body: {
template: {
settings: {
'index.lifecycle.name' => 'my-lifecycle-policy'
}
},
_meta: {
description: 'Settings for ILM',
"my-custom-meta-field": 'More arbitrary metadata'
}
}
)
puts response
Js
const response = await client.cluster.putComponentTemplate({
name: "my-mappings",
template: {
mappings: {
properties: {
"@timestamp": {
type: "date",
format: "date_optional_time||epoch_millis",
},
message: {
type: "wildcard",
},
},
},
},
_meta: {
description: "Mappings for @timestamp and message fields",
"my-custom-meta-field": "More arbitrary metadata",
},
});
console.log(response);
const response1 = await client.cluster.putComponentTemplate({
name: "my-settings",
template: {
settings: {
"index.lifecycle.name": "my-lifecycle-policy",
},
},
_meta: {
description: "Settings for ILM",
"my-custom-meta-field": "More arbitrary metadata",
},
});
console.log(response1);
コンソール
# マッピング用のコンポーネントテンプレートを作成
PUT _component_template/my-mappings
{
"template": {
"mappings": {
"properties": {
"@timestamp": {
"type": "date",
"format": "date_optional_time||epoch_millis"
},
"message": {
"type": "wildcard"
}
}
}
},
"_meta": {
"description": "@timestampとmessageフィールドのマッピング",
"my-custom-meta-field": "より多くの任意のメタデータ"
}
}
# インデックス設定用のコンポーネントテンプレートを作成
PUT _component_template/my-settings
{
"template": {
"settings": {
"index.lifecycle.name": "my-lifecycle-policy"
}
},
"_meta": {
"description": "ILMの設定",
"my-custom-meta-field": "より多くの任意のメタデータ"
}
}
インデックステンプレートの作成
コンポーネントテンプレートを使用してインデックステンプレートを作成します。次のことを指定します:
- データストリームの名前に一致する1つ以上のインデックスパターン。私たちのデータストリーム命名スキームを使用することをお勧めします。
- テンプレートがデータストリーム対応であること。
- マッピングとインデックス設定を含む任意のコンポーネントテンプレート。
- 組み込みテンプレートとの衝突を避けるために
200
よりも高い優先度。インデックスパターンの衝突を避けるには、インデックスパターンの衝突を避けるを参照してください。
Kibanaでインデックステンプレートを作成するには、メインメニューを開き、Stack
Management > Index Managementに移動します。インデックステンプレートビューで、テンプレートを作成をクリックします。
また、インデックステンプレート作成APIを使用することもできます。data_stream
オブジェクトを含めてデータストリームを有効にします。
Python
resp = client.indices.put_index_template(
name="my-index-template",
index_patterns=[
"my-data-stream*"
],
data_stream={},
composed_of=[
"my-mappings",
"my-settings"
],
priority=500,
meta={
"description": "Template for my time series data",
"my-custom-meta-field": "More arbitrary metadata"
},
)
print(resp)
Ruby
response = client.indices.put_index_template(
name: 'my-index-template',
body: {
index_patterns: [
'my-data-stream*'
],
data_stream: {},
composed_of: [
'my-mappings',
'my-settings'
],
priority: 500,
_meta: {
description: 'Template for my time series data',
"my-custom-meta-field": 'More arbitrary metadata'
}
}
)
puts response
Js
const response = await client.indices.putIndexTemplate({
name: "my-index-template",
index_patterns: ["my-data-stream*"],
data_stream: {},
composed_of: ["my-mappings", "my-settings"],
priority: 500,
_meta: {
description: "Template for my time series data",
"my-custom-meta-field": "More arbitrary metadata",
},
});
console.log(response);
コンソール
PUT _index_template/my-index-template
{
"index_patterns": ["my-data-stream*"],
"data_stream": { },
"composed_of": [ "my-mappings", "my-settings" ],
"priority": 500,
"_meta": {
"description": "Template for my time series data",
"my-custom-meta-field": "More arbitrary metadata"
}
}
データストリームにデータを追加
インデックスリクエストは、データストリームにドキュメントを追加します。これらのリクエストは、op_type
のcreate
を使用する必要があります。ドキュメントには@timestamp
フィールドを含める必要があります。
データストリームを自動的に作成するには、ストリームの名前をターゲットにしたインデックスリクエストを送信します。この名前は、インデックステンプレートのインデックスパターンの1つと一致する必要があります。
Python
resp = client.bulk(
index="my-data-stream",
operations=[
{
"create": {}
},
{
"@timestamp": "2099-05-06T16:21:15.000Z",
"message": "192.0.2.42 - - [06/May/2099:16:21:15 +0000] \"GET /images/bg.jpg HTTP/1.0\" 200 24736"
},
{
"create": {}
},
{
"@timestamp": "2099-05-06T16:25:42.000Z",
"message": "192.0.2.255 - - [06/May/2099:16:25:42 +0000] \"GET /favicon.ico HTTP/1.0\" 200 3638"
}
],
)
print(resp)
resp1 = client.index(
index="my-data-stream",
document={
"@timestamp": "2099-05-06T16:21:15.000Z",
"message": "192.0.2.42 - - [06/May/2099:16:21:15 +0000] \"GET /images/bg.jpg HTTP/1.0\" 200 24736"
},
)
print(resp1)
Ruby
response = client.bulk(
index: 'my-data-stream',
body: [
{
create: {}
},
{
"@timestamp": '2099-05-06T16:21:15.000Z',
message: '192.0.2.42 - - [06/May/2099:16:21:15 +0000] "GET /images/bg.jpg HTTP/1.0" 200 24736'
},
{
create: {}
},
{
"@timestamp": '2099-05-06T16:25:42.000Z',
message: '192.0.2.255 - - [06/May/2099:16:25:42 +0000] "GET /favicon.ico HTTP/1.0" 200 3638'
}
]
)
puts response
response = client.index(
index: 'my-data-stream',
body: {
"@timestamp": '2099-05-06T16:21:15.000Z',
message: '192.0.2.42 - - [06/May/2099:16:21:15 +0000] "GET /images/bg.jpg HTTP/1.0" 200 24736'
}
)
puts response
Js
const response = await client.bulk({
index: "my-data-stream",
operations: [
{
create: {},
},
{
"@timestamp": "2099-05-06T16:21:15.000Z",
message:
'192.0.2.42 - - [06/May/2099:16:21:15 +0000] "GET /images/bg.jpg HTTP/1.0" 200 24736',
},
{
create: {},
},
{
"@timestamp": "2099-05-06T16:25:42.000Z",
message:
'192.0.2.255 - - [06/May/2099:16:25:42 +0000] "GET /favicon.ico HTTP/1.0" 200 3638',
},
],
});
console.log(response);
const response1 = await client.index({
index: "my-data-stream",
document: {
"@timestamp": "2099-05-06T16:21:15.000Z",
message:
'192.0.2.42 - - [06/May/2099:16:21:15 +0000] "GET /images/bg.jpg HTTP/1.0" 200 24736',
},
});
console.log(response1);
コンソール
PUT my-data-stream/_bulk
{ "create":{ } }
{ "@timestamp": "2099-05-06T16:21:15.000Z", "message": "192.0.2.42 - - [06/May/2099:16:21:15 +0000] \"GET /images/bg.jpg HTTP/1.0\" 200 24736" }
{ "create":{ } }
{ "@timestamp": "2099-05-06T16:25:42.000Z", "message": "192.0.2.255 - - [06/May/2099:16:25:42 +0000] \"GET /favicon.ico HTTP/1.0\" 200 3638" }
POST my-data-stream/_doc
{
"@timestamp": "2099-05-06T16:21:15.000Z",
"message": "192.0.2.42 - - [06/May/2099:16:21:15 +0000] \"GET /images/bg.jpg HTTP/1.0\" 200 24736"
}
データを検索して視覚化する
Kibanaでデータを探索し、検索するには、メインメニューを開いてDiscoverを選択します。KibanaのDiscoverドキュメントを参照してください。
Kibanaのダッシュボード機能を使用して、チャート、テーブル、地図などでデータを視覚化します。Kibanaのダッシュボードドキュメントを参照してください。
また、検索APIを使用してデータを検索および集約できます。ランタイムフィールドとgrokパターンを使用して、検索時にログメッセージや他の非構造化コンテンツからデータを動的に抽出します。
Python
resp = client.search(
index="my-data-stream",
runtime_mappings={
"source.ip": {
"type": "ip",
"script": "\n String sourceip=grok('%{IPORHOST:sourceip} .*').extract(doc[ \"message\" ].value)?.sourceip;\n if (sourceip != null) emit(sourceip);\n "
}
},
query={
"bool": {
"filter": [
{
"range": {
"@timestamp": {
"gte": "now-1d/d",
"lt": "now/d"
}
}
},
{
"range": {
"source.ip": {
"gte": "192.0.2.0",
"lte": "192.0.2.255"
}
}
}
]
}
},
fields=[
"*"
],
source=False,
sort=[
{
"@timestamp": "desc"
},
{
"source.ip": "desc"
}
],
)
print(resp)
Js
const response = await client.search({
index: "my-data-stream",
runtime_mappings: {
"source.ip": {
type: "ip",
script:
"\n String sourceip=grok('%{IPORHOST:sourceip} .*').extract(doc[ \"message\" ].value)?.sourceip;\n if (sourceip != null) emit(sourceip);\n ",
},
},
query: {
bool: {
filter: [
{
range: {
"@timestamp": {
gte: "now-1d/d",
lt: "now/d",
},
},
},
{
range: {
"source.ip": {
gte: "192.0.2.0",
lte: "192.0.2.255",
},
},
},
],
},
},
fields: ["*"],
_source: false,
sort: [
{
"@timestamp": "desc",
},
{
"source.ip": "desc",
},
],
});
console.log(response);
コンソール
GET my-data-stream/_search
{
"runtime_mappings": {
"source.ip": {
"type": "ip",
"script": """
String sourceip=grok('%{IPORHOST:sourceip} .*').extract(doc[ "message" ].value)?.sourceip;
if (sourceip != null) emit(sourceip);
"""
}
},
"query": {
"bool": {
"filter": [
{
"range": {
"@timestamp": {
"gte": "now-1d/d",
"lt": "now/d"
}
}
},
{
"range": {
"source.ip": {
"gte": "192.0.2.0",
"lte": "192.0.2.255"
}
}
}
]
}
},
"fields": [
"*"
],
"_source": false,
"sort": [
{
"@timestamp": "desc"
},
{
"source.ip": "desc"
}
]
}
Elasticsearchの検索はデフォルトで同期的です。フローズンデータ、長い時間範囲、大規模データセットに対する検索は時間がかかる場合があります。非同期検索APIを使用して、バックグラウンドで検索を実行します。その他の検索オプションについては、検索APIを参照してください。
Python
resp = client.async_search.submit(
index="my-data-stream",
runtime_mappings={
"source.ip": {
"type": "ip",
"script": "\n String sourceip=grok('%{IPORHOST:sourceip} .*').extract(doc[ \"message\" ].value)?.sourceip;\n if (sourceip != null) emit(sourceip);\n "
}
},
query={
"bool": {
"filter": [
{
"range": {
"@timestamp": {
"gte": "now-2y/d",
"lt": "now/d"
}
}
},
{
"range": {
"source.ip": {
"gte": "192.0.2.0",
"lte": "192.0.2.255"
}
}
}
]
}
},
fields=[
"*"
],
source=False,
sort=[
{
"@timestamp": "desc"
},
{
"source.ip": "desc"
}
],
)
print(resp)
Js
const response = await client.asyncSearch.submit({
index: "my-data-stream",
runtime_mappings: {
"source.ip": {
type: "ip",
script:
"\n String sourceip=grok('%{IPORHOST:sourceip} .*').extract(doc[ \"message\" ].value)?.sourceip;\n if (sourceip != null) emit(sourceip);\n ",
},
},
query: {
bool: {
filter: [
{
range: {
"@timestamp": {
gte: "now-2y/d",
lt: "now/d",
},
},
},
{
range: {
"source.ip": {
gte: "192.0.2.0",
lte: "192.0.2.255",
},
},
},
],
},
},
fields: ["*"],
_source: false,
sort: [
{
"@timestamp": "desc",
},
{
"source.ip": "desc",
},
],
});
console.log(response);
コンソール
POST my-data-stream/_async_search
{
"runtime_mappings": {
"source.ip": {
"type": "ip",
"script": """
String sourceip=grok('%{IPORHOST:sourceip} .*').extract(doc[ "message" ].value)?.sourceip;
if (sourceip != null) emit(sourceip);
"""
}
},
"query": {
"bool": {
"filter": [
{
"range": {
"@timestamp": {
"gte": "now-2y/d",
"lt": "now/d"
}
}
},
{
"range": {
"source.ip": {
"gte": "192.0.2.0",
"lte": "192.0.2.255"
}
}
}
]
}
},
"fields": [
"*"
],
"_source": false,
"sort": [
{
"@timestamp": "desc"
},
{
"source.ip": "desc"
}
]
}