Added the multi website log parsing availability

This commit is contained in:
Alexey Nizhegolenko 2022-07-18 13:30:09 +03:00
parent 62ae4451e2
commit 1e09249b2f
4 changed files with 128 additions and 46 deletions

View file

@ -2,58 +2,71 @@
"__inputs": [ "__inputs": [
{ {
"name": "DS_INFLUXDB", "name": "DS_INFLUXDB",
"label": "Influxdb", "label": "InfluxDB",
"description": "", "description": "",
"type": "datasource", "type": "datasource",
"pluginId": "influxdb", "pluginId": "influxdb",
"pluginName": "InfluxDB" "pluginName": "InfluxDB"
} }
], ],
"__elements": [],
"__requires": [ "__requires": [
{ {
"type": "grafana", "type": "grafana",
"id": "grafana", "id": "grafana",
"name": "Grafana", "name": "Grafana",
"version": "5.2.3" "version": "9.0.2"
}, },
{ {
"type": "panel", "type": "panel",
"id": "grafana-worldmap-panel", "id": "grafana-worldmap-panel",
"name": "Worldmap Panel", "name": "Worldmap Panel",
"version": "0.1.2" "version": "0.3.3"
}, },
{ {
"type": "datasource", "type": "datasource",
"id": "influxdb", "id": "influxdb",
"name": "InfluxDB", "name": "InfluxDB",
"version": "5.0.0" "version": "1.0.0"
}, },
{ {
"type": "panel", "type": "panel",
"id": "table", "id": "table-old",
"name": "Table", "name": "Table (old)",
"version": "5.0.0" "version": ""
} }
], ],
"annotations": { "annotations": {
"list": [ "list": [
{ {
"builtIn": 1, "builtIn": 1,
"datasource": "-- Grafana --", "datasource": {
"type": "datasource",
"uid": "grafana"
},
"enable": true, "enable": true,
"hide": true, "hide": true,
"iconColor": "rgba(0, 211, 255, 1)", "iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts", "name": "Annotations & Alerts",
"target": {
"limit": 100,
"matchAny": false,
"tags": [],
"type": "dashboard"
},
"type": "dashboard" "type": "dashboard"
} }
] ]
}, },
"description": "Dashboard for showing GEOIP information from Nginx logs, for the GeoStat script. ",
"editable": true, "editable": true,
"gnetId": null, "fiscalYearStartMonth": 0,
"gnetId": 8342,
"graphTooltip": 0, "graphTooltip": 0,
"id": null, "id": null,
"iteration": 1539373533704, "iteration": 1658128917676,
"links": [], "links": [],
"liveNow": false,
"panels": [ "panels": [
{ {
"circleMaxSize": "8", "circleMaxSize": "8",
@ -63,7 +76,10 @@
"rgba(237, 129, 40, 0.89)", "rgba(237, 129, 40, 0.89)",
"rgba(50, 172, 45, 0.97)" "rgba(50, 172, 45, 0.97)"
], ],
"datasource": "${DS_INFLUXDB}", "datasource": {
"type": "influxdb",
"uid": "${DS_INFLUXDB}"
},
"decimals": 0, "decimals": 0,
"esMetric": "Count", "esMetric": "Count",
"gridPos": { "gridPos": {
@ -95,6 +111,10 @@
}, },
"targets": [ "targets": [
{ {
"datasource": {
"type": "influxdb",
"uid": "${DS_INFLUXDB}"
},
"groupBy": [ "groupBy": [
{ {
"params": [ "params": [
@ -133,6 +153,12 @@
"key": "host", "key": "host",
"operator": "=~", "operator": "=~",
"value": "/^$host$/" "value": "/^$host$/"
},
{
"condition": "AND",
"key": "website",
"operator": "=~",
"value": "/^$website$/"
} }
] ]
} }
@ -147,7 +173,10 @@
}, },
{ {
"columns": [], "columns": [],
"datasource": "${DS_INFLUXDB}", "datasource": {
"type": "influxdb",
"uid": "${DS_INFLUXDB}"
},
"fontSize": "100%", "fontSize": "100%",
"gridPos": { "gridPos": {
"h": 13, "h": 13,
@ -167,7 +196,7 @@
"styles": [ "styles": [
{ {
"alias": "Country", "alias": "Country",
"colorMode": null, "align": "auto",
"colors": [ "colors": [
"rgba(245, 54, 54, 0.9)", "rgba(245, 54, 54, 0.9)",
"rgba(237, 129, 40, 0.89)", "rgba(237, 129, 40, 0.89)",
@ -183,7 +212,7 @@
}, },
{ {
"alias": "", "alias": "",
"colorMode": null, "align": "auto",
"colors": [ "colors": [
"rgba(245, 54, 54, 0.9)", "rgba(245, 54, 54, 0.9)",
"rgba(237, 129, 40, 0.89)", "rgba(237, 129, 40, 0.89)",
@ -200,7 +229,7 @@
}, },
{ {
"alias": "Count", "alias": "Count",
"colorMode": null, "align": "auto",
"colors": [ "colors": [
"rgba(245, 54, 54, 0.9)", "rgba(245, 54, 54, 0.9)",
"rgba(237, 129, 40, 0.89)", "rgba(237, 129, 40, 0.89)",
@ -217,6 +246,10 @@
], ],
"targets": [ "targets": [
{ {
"datasource": {
"type": "influxdb",
"uid": "${DS_INFLUXDB}"
},
"groupBy": [ "groupBy": [
{ {
"params": [ "params": [
@ -255,59 +288,90 @@
"key": "host", "key": "host",
"operator": "=~", "operator": "=~",
"value": "/^$host$/" "value": "/^$host$/"
},
{
"condition": "AND",
"key": "website",
"operator": "=~",
"value": "/^$website$/"
} }
] ]
} }
], ],
"title": "Country SUM", "title": "Country SUM",
"transform": "table", "transform": "table",
"type": "table" "type": "table-old"
} }
], ],
"refresh": "30s", "refresh": "5s",
"schemaVersion": 16, "schemaVersion": 36,
"style": "dark", "style": "dark",
"tags": [], "tags": [],
"templating": { "templating": {
"list": [ "list": [
{ {
"current": { "current": {
"selected": false,
"text": "default", "text": "default",
"value": "default" "value": "default"
}, },
"hide": 0, "hide": 0,
"label": null, "includeAll": false,
"multi": false,
"name": "datasource", "name": "datasource",
"options": [], "options": [],
"query": "influxdb", "query": "influxdb",
"queryValue": "",
"refresh": 1, "refresh": 1,
"regex": "", "regex": "",
"skipUrlSync": false,
"type": "datasource" "type": "datasource"
}, },
{ {
"allValue": null,
"current": {}, "current": {},
"datasource": "${DS_INFLUXDB}", "datasource": {
"type": "influxdb",
"uid": "${DS_INFLUXDB}"
},
"definition": "",
"hide": 0, "hide": 0,
"includeAll": false, "includeAll": false,
"label": null,
"multi": false, "multi": false,
"name": "host", "name": "host",
"options": [], "options": [],
"query": "SHOW TAG VALUES FROM \"geodata\" WITH KEY = \"host\"", "query": "SHOW TAG VALUES FROM \"geodata\" WITH KEY = \"host\"",
"refresh": 1, "refresh": 1,
"regex": "", "regex": "",
"skipUrlSync": false,
"sort": 0, "sort": 0,
"tagValuesQuery": "", "tagValuesQuery": "",
"tags": [],
"tagsQuery": "", "tagsQuery": "",
"type": "query", "type": "query",
"useTags": false "useTags": false
},
{
"current": {},
"datasource": {
"type": "influxdb",
"uid": "${DS_INFLUXDB}"
},
"definition": "SHOW TAG VALUES FROM \"geodata\" WITH KEY = \"website\"",
"hide": 0,
"includeAll": false,
"multi": false,
"name": "website",
"options": [],
"query": "SHOW TAG VALUES FROM \"geodata\" WITH KEY = \"website\"",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 0,
"type": "query"
} }
] ]
}, },
"time": { "time": {
"from": "now-6h", "from": "now-5m",
"to": "now" "to": "now"
}, },
"timepicker": { "timepicker": {
@ -336,7 +400,8 @@
] ]
}, },
"timezone": "", "timezone": "",
"title": "GeoMap", "title": "Nginx GEOIP Statistic",
"uid": "IYo4xyJmz", "uid": "IYo4xyJmz",
"version": 6 "version": 5,
} "weekStart": ""
}

View file

@ -3,6 +3,8 @@
# Parts added by Remko Lodder, 2019. # Parts added by Remko Lodder, 2019.
# Added: IPv6 matching, make query based on geoip2 instead of # Added: IPv6 matching, make query based on geoip2 instead of
# geoip, which is going away r.s.n. # geoip, which is going away r.s.n.
# Added possibility of processing more than one Nginx log file,
# by adding threading support. 2022 July by Alexey Nizhegolenko
import os import os
import re import re
@ -15,6 +17,7 @@ import geoip2.database
import configparser import configparser
from influxdb import InfluxDBClient from influxdb import InfluxDBClient
from IPy import IP as ipadd from IPy import IP as ipadd
import threading
class SyslogBOMFormatter(logging.Formatter): class SyslogBOMFormatter(logging.Formatter):
@ -30,7 +33,7 @@ root = logging.getLogger(__name__)
root.setLevel(os.environ.get("LOGLEVEL", "INFO")) root.setLevel(os.environ.get("LOGLEVEL", "INFO"))
root.addHandler(handler) root.addHandler(handler)
def logparse(LOGPATH, INFLUXHOST, INFLUXPORT, INFLUXDBDB, INFLUXUSER, INFLUXUSERPASS, MEASUREMENT, GEOIPDB, INODE): # NOQA def logparse(LOGPATH, WEBSITE, INFLUXHOST, INFLUXPORT, INFLUXDBDB, INFLUXUSER, INFLUXUSERPASS, MEASUREMENT, GEOIPDB, INODE): # NOQA
# Preparing variables and params # Preparing variables and params
IPS = {} IPS = {}
COUNT = {} COUNT = {}
@ -43,8 +46,7 @@ def logparse(LOGPATH, INFLUXHOST, INFLUXPORT, INFLUXDBDB, INFLUXUSER, INFLUXUSER
re_IPV6 = re.compile('(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))') # NOQA re_IPV6 = re.compile('(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))') # NOQA
GI = geoip2.database.Reader(GEOIPDB) GI = geoip2.database.Reader(GEOIPDB)
# Main loop that parses log file in tailf style with sending metrics out
# Main loop to parse access.log file in tailf style with sending metrcs
with open(LOGPATH, "r") as FILE: with open(LOGPATH, "r") as FILE:
STR_RESULTS = os.stat(LOGPATH) STR_RESULTS = os.stat(LOGPATH)
ST_SIZE = STR_RESULTS[6] ST_SIZE = STR_RESULTS[6]
@ -55,7 +57,7 @@ def logparse(LOGPATH, INFLUXHOST, INFLUXPORT, INFLUXDBDB, INFLUXUSER, INFLUXUSER
LINE = FILE.readline() LINE = FILE.readline()
INODENEW = os.stat(LOGPATH).st_ino INODENEW = os.stat(LOGPATH).st_ino
if INODE != INODENEW: if INODE != INODENEW:
break return
if not LINE: if not LINE:
time.sleep(1) time.sleep(1)
FILE.seek(WHERE) FILE.seek(WHERE)
@ -74,6 +76,7 @@ def logparse(LOGPATH, INFLUXHOST, INFLUXPORT, INFLUXDBDB, INFLUXUSER, INFLUXUSER
COUNT['count'] = 1 COUNT['count'] = 1
GEOHASH['geohash'] = HASH GEOHASH['geohash'] = HASH
GEOHASH['host'] = HOSTNAME GEOHASH['host'] = HOSTNAME
GEOHASH['website'] = WEBSITE
GEOHASH['country_code'] = INFO.country.iso_code GEOHASH['country_code'] = INFO.country.iso_code
GEOHASH['country_name'] = INFO.country.name GEOHASH['country_name'] = INFO.country.name
GEOHASH['city_name'] = INFO.city.name GEOHASH['city_name'] = INFO.city.name
@ -81,8 +84,7 @@ def logparse(LOGPATH, INFLUXHOST, INFLUXPORT, INFLUXDBDB, INFLUXUSER, INFLUXUSER
IPS['fields'] = COUNT IPS['fields'] = COUNT
IPS['measurement'] = MEASUREMENT IPS['measurement'] = MEASUREMENT
METRICS.append(IPS) METRICS.append(IPS)
# Sending json data itto InfluxDB
# Sending json data to InfluxDB
try: try:
CLIENT.write_points(METRICS) CLIENT.write_points(METRICS)
except Exception: except Exception:
@ -90,14 +92,14 @@ def logparse(LOGPATH, INFLUXHOST, INFLUXPORT, INFLUXDBDB, INFLUXUSER, INFLUXUSER
def main(): def main():
# Preparing for reading config file # Preparing for reading the config file
PWD = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) PWD = os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
CONFIG = configparser.ConfigParser() CONFIG = configparser.ConfigParser()
CONFIG.read('%s/settings.ini' % PWD) CONFIG.read(f'{PWD}/settings.ini')
# Getting params from config # Getting params from config
GEOIPDB = CONFIG.get('GEOIP', 'geoipdb') GEOIPDB = CONFIG.get('GEOIP', 'geoipdb')
LOGPATH = CONFIG.get('NGINX_LOG', 'logpath') LOGPATH = CONFIG.get('NGINX_LOGS', 'logpath').split()
INFLUXHOST = CONFIG.get('INFLUXDB', 'host') INFLUXHOST = CONFIG.get('INFLUXDB', 'host')
INFLUXPORT = CONFIG.get('INFLUXDB', 'port') INFLUXPORT = CONFIG.get('INFLUXDB', 'port')
INFLUXDBDB = CONFIG.get('INFLUXDB', 'database') INFLUXDBDB = CONFIG.get('INFLUXDB', 'database')
@ -107,14 +109,29 @@ def main():
# Parsing log file and sending metrics to Influxdb # Parsing log file and sending metrics to Influxdb
while True: while True:
# Get inode from log file logs = []
INODE = os.stat(LOGPATH).st_ino thread_names = []
# Run main loop and grep a log file for logitem in LOGPATH:
if os.path.exists(LOGPATH): logs.append(logitem.split(":"))
logparse(LOGPATH, INFLUXHOST, INFLUXPORT, INFLUXDBDB, INFLUXUSER, INFLUXUSERPASS, MEASUREMENT, GEOIPDB, INODE) # NOQA for website, log in logs:
else: # Get inode from log file
logging.info('Nginx log file %s not found', LOGPATH) if os.path.exists(log):
print('Nginx log file %s not found' % LOGPATH) INODE = os.stat(log).st_ino
else:
logging.info('Nginx log file %s not found', log)
print('Nginx log file %s not found' % log)
return
# Run the main loop and grep data in separate threads
t = website
if os.path.exists(log):
t = threading.Thread(target=logparse, args=[log, website, INFLUXHOST, INFLUXPORT, INFLUXDBDB, INFLUXUSER, INFLUXUSERPASS, MEASUREMENT, GEOIPDB, INODE], daemon=True, name=website) # NOQA
for thread in threading.enumerate():
thread_names.append(thread.name)
if website not in thread_names:
t.start()
else:
logging.info('Nginx log file %s not found', log)
print('Nginx log file %s not found' % log)
if __name__ == '__main__': if __name__ == '__main__':

Binary file not shown.

Before

Width:  |  Height:  |  Size: 188 KiB

After

Width:  |  Height:  |  Size: 137 KiB

View file

@ -1,6 +1,6 @@
[NGINX_LOG] [NGINX_LOGS]
#Path for the log file (Nginx) #Path for the log file (Nginx)
logpath = /var/log/nginx/access.log logpath = website1:/var/log/website1/access.log website2:/var/log/website2/access.log
[GEOIP] [GEOIP]
#Path for the GEOIP DB file #Path for the GEOIP DB file