# 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
```