Projet

Général

Profil

Révision 10ca1256

ID10ca12569acda3ccc36467a5b9943569cd3d8401
Parent 271e21d3
Enfant 852aa41a

Ajouté par pcy il y a plus de 5 ans

[plugins/mysql/mysql_audit] Added plugin for monitoring the audit log

Shows the number of active over time

Voir les différences:

plugins/mysql/mysql_audit
1
#!/usr/bin/env python3
2
# -*- python -*-
3

  
4
"""
5
=head1 INTRODUCTION
6

  
7
Plugin to monitor the MySQL audit log connection count
8

  
9
=head1 APPLICABLE SYSTEMS
10

  
11
MySQL needs to be configured manually in order to enable the audit log. This
12
can be done as follows:
13

  
14
=over 2
15

  
16
      plugin-load = server_audit=server_audit.so
17
      server_audit_events = connect
18
      server_audit_output_type=file
19
      server_audit_file_path = /var/log/mysql/audit.log
20
      server_audit_file_rotate_size = 512M
21
      server_audit_logging = ON
22
      server_audit_file_rotations = 5
23

  
24
=back
25

  
26
=head1 INSTALLATION
27

  
28
Place in /etc/munin/plugins/ (or link it there using ln -s)
29

  
30
=head1 CONFIGURATION
31

  
32
Add this to your /etc/munin/plugin-conf.d/munin-node:
33

  
34
=over 2
35

  
36
      [mysql_audit]
37
      user mysql
38
      env.logfile /var/log/mysql/audit.log
39
      env.sorted 1 # sort output (if not: unsorted)
40
      env.toplist 10 # only the top 10 (if unset: all of them). implies sorted
41

  
42
=back
43

  
44
=head1 HISTORY
45

  
46
2017-11-03: v 1.0 Bert Van de Poel <bert@bhack.net>: created
47
2020-07-19: v 1.1 pcy <pcy@ulyssis.org>: added config options
48

  
49
=head1 USAGE
50

  
51
Parameters understood:
52

  
53
  config   (required)
54
  autoconf (optional - used by munin-config)
55

  
56
=head1 MAGIC MARKERS
57

  
58
#%# family=auto
59
#%# capabilities=autoconf
60
"""
61

  
62

  
63
from datetime import datetime, timedelta, timezone
64
import operator
65
import os
66
import struct
67
import sys
68

  
69

  
70
def weakbool(x):
71
    return x.lower().strip() in {'true', 'yes', 'y', '1'}
72

  
73

  
74
logfile = os.getenv('logfile', '/var/log/mysql/audit.log')
75
toplist = int(os.getenv('toplist', '0'))
76
sortlist = weakbool(os.getenv('sorted', 'N')) or toplist > 0
77

  
78
STATEFILE = os.getenv('MUNIN_STATEFILE')
79

  
80

  
81
def loadstate():
82
    if not os.path.isfile(STATEFILE):
83
        return None
84

  
85
    with open(STATEFILE, 'rb') as f:
86
        tstamp = struct.unpack('d', f.read())[0]
87
        return datetime.fromtimestamp(tstamp, tz=timezone.utc)
88

  
89

  
90
def savestate(state):
91
    with open(STATEFILE, 'wb') as f:
92
        f.write(struct.pack('d', state.timestamp()))
93

  
94

  
95
def reverse_lines(filename, BUFSIZE=4096):
96
    with open(filename, "r") as f:
97
        f.seek(0, 2)
98
        p = f.tell()
99
        remainder = ""
100
        while True:
101
            sz = min(BUFSIZE, p)
102
            p -= sz
103
            f.seek(p)
104
            buf = f.read(sz) + remainder
105
            if '\n' not in buf:
106
                remainder = buf
107
            else:
108
                i = buf.index('\n')
109
                for L in buf[i + 1:].split("\n")[::-1]:
110
                    yield L
111
                remainder = buf[:i]
112
            if p == 0:
113
                break
114
        yield remainder
115

  
116

  
117
def get_data(do_save=True):
118
    occurrences = {}
119
    begin = datetime.now(timezone.utc)
120
    begin_local = datetime.now()
121

  
122
    state = loadstate()
123
    if state is None:
124
        # need to do something here to prevent reading indefinitely
125
        state = begin - timedelta(minutes=5)
126

  
127
    for line in reverse_lines(logfile):
128
        if ',CONNECT,' not in line:
129
            continue
130

  
131
        splitted = line.split(',')
132
        key = splitted[2]
133
        date = datetime.strptime(splitted[0], '%Y%m%d %H:%M:%S')
134
        # hack to add timezone data to the datetime
135
        date = begin + (date - begin_local)
136

  
137
        if date < state:
138
            break
139

  
140
        occurrences[key] = occurrences.get(key, 0) + 1
141

  
142
    if do_save:
143
        savestate(begin)
144

  
145
    return occurrences
146

  
147

  
148
def autoconf():
149
    print("no (logfile not found)" if os.path.isfile(logfile) else "yes")
150

  
151

  
152
def configure():
153
    print('graph_title MySQL Audit connect count')
154
    print('graph_vlabel Connections')
155
    print('graph_category mysql')
156

  
157
    occurrences = get_data(False)
158
    occitems = occurrences.items()
159
    occitems = sorted(occitems, key=operator.itemgetter(1 if sortlist else 0),
160
                      reverse=sortlist)
161
    if toplist > 0:
162
        occitems = occitems[:toplist]
163

  
164
    for key, value in occitems:
165
        print('{}.label {}'.format(key.lower(), key))
166
        print('{}.type GAUGE'.format(key.lower()))
167
        print('{}.draw AREASTACK'.format(key.lower()))
168

  
169

  
170
def fetch():
171
    occurrences = get_data()
172
    occitems = occurrences.items()
173
    occitems = sorted(occitems, key=operator.itemgetter(1 if sortlist else 0),
174
                      reverse=sortlist)
175
    if toplist > 0:
176
        occitems = occitems[:toplist]
177

  
178
    for key, value in occitems:
179
        print('{}.value {}'.format(key, value))
180

  
181

  
182
if len(sys.argv) == 2 and sys.argv[1] == "autoconf":
183
    autoconf()
184
elif len(sys.argv) == 2 and sys.argv[1] == "config":
185
    configure()
186
else:
187
    fetch()
188

  
189
# flake8: noqa: E265

Formats disponibles : Unified diff