---
# System prepended metadata

title: ELK Stack

---

# ELK Stack

ELK (Elasticsearch/Logstash/Kibana) 是一套完整的數據分析工具

![](https://ithelp.ithome.com.tw/upload/images/20200914/2010998410xCKdSK8L.png)
## [Filebeat](https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-overview.html)

```
- type: log

  # Change to true to enable this input configuration.
  enabled: true
  
  # Paths that should be crawled and fetched. Glob based paths.
  paths:
    - L:\Logs\RTX\*\*\All*.log*
    
  # Optional additional fields. These fields can be freely picked
  # to add additional information to the crawled log files for filtering
  fields:
    source_type: "log4net"
    index_field: "general"
  
  # Exclude files. A list of regular expressions to match. Filebeat drops the files that
  # are matching any regular expression from the list. By default, no files are dropped.
  exclude_files: ['zip$','7z$','\b(\w*[Jj]obService\w*)\b']
  
  #Specifies the regular expression pattern to match.
  multiline.pattern: '^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}.[0-9]{3}'
  #Defines whether the pattern is negated. The default is false.
  multiline.negate: true
  #Specifies how Filebeat combines matching lines into an event.
  multiline.match: after
  
  #If this option is enabled, Filebeat ignores any files that were modified before the specified timespan.
  ignore_older: 24h
```

## [LogStash](https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-overview.html)

```
if [index_prefix] =~ "general" {
		grok{
			break_on_match => false
			match => [ "message", "%{TIMESTAMP_ISO8601:log_timestamp} \[%{NUMBER:thread:int}\] %{DATA:log_type} %{DATA:logger} %{GREEDYDATA:log_body}" ]
			match => [ "message", "(?i).*DBManager2.*- (?<database>\[.*\])(?<schema>\[.*\])\.(?<storedProcedure>\[.*\]) " ]
			match => [ "log_body", "^\[%{DATA:session_id}\] - (?<summary>[^\n]*)\n{0,1}(?<detail>.*)" ]
			match => [ "log_body", "^- (?<summary>[^\n]*)\n{0,1}(?<detail>.*)" ]

			match => [ "summary", "(?i)\[PacketMonitor\]\[%{DATA:packetType}\]" ]
			match => [ "summary", "(?i)(url|requestUrl):\s*%{DATA:url}(\?|\]|,)" ]
			match => [ "summary", "(?i)query:\s*%{DATA:query}(\]|,)" ]
			match => [ "summary", "(?i)topic(:|=)\s*%{DATA:messagingTopic}(\]|,)" ]

			match => [ "summary", "(?i)requestSize:\s*%{DATA:requestSize}\s*bytes" ]
			match => [ "summary", "(?i)responseSize:\s*%{DATA:responseSize}\s*bytes" ]
			match => [ "summary", "(?i)requestSize:\s*%{NUMBER:requestBytes:int}(,|\])" ]
			match => [ "summary", "(?i)responseSize:\s*%{NUMBER:responseBytes:int}(,|\])" ]
			match => [ "summary", "(?i)channel:\s*%{DATA:channel}(,|\])" ]

			match => [ "summary", "(?i)(spantime|duration):\s*%{TIME:duration}(\]|,)*" ]
			match => [ "summary", "(?i)spendtime:\s*%{NUMBER:spendtime:float}(,|\])" ]
			match => [ "summary", "\[%{TIME:duration}\]" ]
			match => [ "summary", "(?i).*\[(?<slowType>.*slow)\]" ]

			match => [ "message", "(?i)(ip|Forwarded-For).*(:|=)\s*\"*%{IP:ip}\"*(,|\]| )*" ]
		}
	}
```

## [Index Template](https://www.elastic.co/guide/en/elasticsearch/reference/current/index-templates.html)
![](https://i.imgur.com/NUnhZTD.png)
```
rtx-index-general-template
{
  "index": {
    "lifecycle": {
      "name": "dng-lifecycle-policy",
      "rollover_alias": "rtx-index-general"
    },
    "refresh_interval": "10s",
    "number_of_shards": "1",
    "number_of_replicas": "1"
  }
}
```
```
PUT rtx-index-general-000001
{
    "aliases": {
    "rtx-index-general":{
        "is_write_index":true
    }
  }  
}
```
## [Index Pattern](https://www.elastic.co/guide/en/kibana/7.17/index-patterns.html)
Index pattern can point to one or more indices.
rtx-index-general-* => rtx-index-general-000001、rtx-index-general-000002 ...

## [Kibana Watcher](https://www.elastic.co/guide/en/kibana/current/watcher-ui.html)
```
{ //多久觸發Watcher
  "trigger": {
    "schedule": {
      "interval": "1m"
    }
  },
  "input": {
    "search": {
      "request": {
        "search_type": "query_then_fetch",
        "indices": [
          "rtx-index-fatal-*"
        ],
        "rest_total_hits_as_int": true,
        "body": {
          "query": {
            "bool": {
              "filter": [
                {　／／搜尋的時間範圍
                  "range": {
                    "@timestamp": {
                      "gte": "now-1m"
                    }
                  }
                }
              ],
              "boost": 1
            }
          },
          "aggs": {
            "partners": {
              "terms": {
                "field": "partner.keyword"
              }
            }
          }
        }
      }
    }
  },
  "condition": {
    "script": {
      "source": "def result = ctx.payload.aggregations.partners.buckets; if (result.size() == 0) return false; return result.get(0).doc_count > 0;",
      "lang": "painless"
    }
  },
  "actions": {
    "skype_notify": {
      "transform": {
        "script": {
          "source": "return ctx.payload.aggregations.partners.buckets.stream().filter(op -> op.doc_count > 0).collect(Collectors.toList());",
          "lang": "painless"
        }
      },
      "webhook": {
        "scheme": "http",
        "host": "172.24.3.240",
        "port": 888,
        "method": "get",
        "path": "/",
        "params": {
          "msg": """Job Retry Failed
=================================
Partners who has Fatal Event Failed within 1 minutes are as following. 
		
{{#ctx.payload._value}}{{key}} : {{doc_count}}
{{/ctx.payload._value}}  
		
		
Link : http://kibana.mrk6.com/s/rtx/goto/2f901869530e21bf881c23a18fe29816
=================================""",
          "to": "ALERT_RTX"
        },
        "headers": {}
      }
    }
  }
}
```

[John Wu有寫手把手建置ELK的Blog 推推](https://blog.johnwu.cc/article/how-to-install-elasticsearch-logstash-and-kibana-elk-stack-on-centos-red-hat.html)

# ELK advance notification
![](https://i.imgur.com/0Qw1czr.jpg)

**Python API的組成由Flask 框架 + Python Skype套件( skpy )構成**

[Flask](https://zh.wikipedia.org/wiki/Flask)


```
from flask import Flask, jsonify, request

@app.route('/sendPicture', methods=['GET'])
@log_request
def sendPicture():
    data = request.get_json()     
    url = data['url']
    fileName = datetime.now().strftime("%Y-%m-%d_%H%M%S")
    skype_agent = skypeHelper.skype_agent(const.config_info,const.config_info['ChatName'])
    Selenium_Helper.execute(url, fileName) 
    filePath = f"{const.config_info['outputPath']}\\{fileName}.png"
    with open(rf"{filePath}", "rb") as f:
        skype_agent.sendFile(f, filePath, image=True)  
        return jsonify(isError=False, message="Success", statusCode=200)

if __name__ == "__main__":
    app.run(host=const.config_info['Host'], port=const.config_info['Port'], debug=True)
```

[skpy](https://pypi.org/project/SkPy/)

```
from skpy import Skype, SkypeChats

sk = Skype(config_info["account"], config_info["password"])

# 建立 SK 物件
skc = SkypeChats(sk)

# 指定發送訊息的目的聊天室(可以是群組 or 個人)
# cht_name = "Bot Test"

# 搜尋聊天室 ID
target_id = search_sk_id(sk, cht_name)

# 聊天功能匯入「聊天室ID」
ch = sk.chats[target_id]

return ch
```


