Projet

Général

Profil

Paste
Télécharger au format
Statistiques
| Branche: | Révision:

root / plugins / znc / znc_logs.py @ a7139bca

Historique | Voir | Annoter | Télécharger (7,43 ko)

1 a7139bca Lars Kruse
#!/usr/bin/env python3
2 8fe960de Thor77
# -*- coding: utf-8 -*-
3
'''
4
=head1 NAME
5 c6f88968 Lars Kruse

6 8fe960de Thor77
znc_logs
7

8
=head1 DESCRIPTION
9 c6f88968 Lars Kruse

10 8fe960de Thor77
Shows lines/minute in today's znc-logs
11

12
=head2 CONFIGURATION
13 c6f88968 Lars Kruse

14
 [znc_logs]
15
 user znc                              # or any other user/group that can read the znclog-folder
16
 group znc
17
 env.logdir /var/lib/znc/moddata/log/  # path to the GLOBAL log-folder with a "/" at the end
18
 env.expire 0                          # Keep channel names forever  - OR -
19
 env.expire 1                          # Forget channel names from last run
20 8fe960de Thor77

21
=head1 COPYRIGHT
22 c6f88968 Lars Kruse

23
GNU General Public License v3.0 only
24

25
SPDX-License-Identifier: GPL-3.0-only
26 8fe960de Thor77

27
=head1 AUTHOR
28 c6f88968 Lars Kruse

29 8fe960de Thor77
Thor77 <thor77[at]thor77.org>
30 c6f88968 Lars Kruse

31
=cut
32 8fe960de Thor77
'''
33 b17436fe Paul Saunders
import json
34
import os, sys, time
35
import re
36
import stat
37
import traceback
38 8fe960de Thor77
39 b17436fe Paul Saunders
logdir = os.environ.get('logdir')
40
expire = os.environ.get('expire', 0)
41 8fe960de Thor77
42
if not logdir:
43
    raise Exception('You have to set the logdir with env.logdir <path to log> in the plugin-conf!')
44
45 b17436fe Paul Saunders
date = time.strftime('%Y%m%d')
46
longdate = time.strftime('%Y-%m-%d')
47
last_values_file = os.environ['MUNIN_PLUGSTATE'] + '/znc_logs_last'
48 8fe960de Thor77
49
def get_last():
50
    try:
51
        d = {}
52
        with open(last_values_file, 'r') as f:
53 b17436fe Paul Saunders
            d = json.load(f)
54 8fe960de Thor77
        return d
55
    except FileNotFoundError:
56
        return {}
57
58 b17436fe Paul Saunders
def tail_open(filename, position=0):
59
    # Based on tail_open from perls' Munin::Plugin
60
    filereset = 0
61
    size = os.stat(filename)[stat.ST_SIZE]
62
    if size is None:
63
        return (undef, undef)
64
    f = open(filename, 'r', encoding='utf-8', errors='replace')
65
    if position > size:
66
        filereset = 1
67
    else:
68
        f.seek(position, 0)
69
        newpos = f.tell()
70
        if newpos != position:
71
            raise Exception
72
    return (f, filereset)
73 8fe960de Thor77
74 b17436fe Paul Saunders
def tail_close(fh):
75
    position = fh.tell()
76
    fh.close()
77
    return position
78 8fe960de Thor77
79
80 b17436fe Paul Saunders
last = get_last()
81
if "users" in last:
82
    user_list = last["users"]
83
else:
84
    user_list = {}
85
if "channels" in last:
86
    channel_list = last["channels"]
87
else:
88
    channel_list = {}
89
if "log_pos" in last:
90
    log_pos = last["log_pos"]
91
else:
92
    log_pos = {}
93
94
channel_stats = {}
95
user_stats = {}
96
97
def read_data(savestate=True):
98
    # Version 1.6 will change to directory-based filing, so walk recursively
99
    for (dirpath, dirnames, filenames) in os.walk(logdir):
100
        for filename in filenames:
101
            filename_ = filename.replace('.log', '')
102
103
            user, network, channel, file_date = (None, None, None, None)
104 8fe960de Thor77
105 b17436fe Paul Saunders
            try:
106
                if len(dirpath) > len(logdir):
107
                    # We're below the log path, so this is a 1.6-style log
108
                    reldir = dirpath.replace(logdir + "/", '', 1)
109
                    try:
110
                        network, channel = reldir.split(os.sep)
111
                    except ValueError as e:
112
                        user, network, channel = reldir.split(os.sep)
113
                    file_date = filename_
114
                else:
115
                    try:
116
                        network, channel, file_date = filename_.split('_')
117
                    except ValueError as e:
118
                        user, network, channel, file_date = filename_.split('_')
119
            except ValueError as e:
120
                continue
121
            network_channel = '{}@{}'.format(channel, network)
122
            if network.lower() not in channel_list:
123
                channel_list[network.lower()] = {}
124
            if channel.startswith('#'):
125
                channel_list[network.lower()][channel.lower()] = network_channel
126
            user_list[user.lower()] = user
127
            # check if log is from today
128
            if (file_date == date or file_date == longdate):
129
                # current lines in the file
130
                (fh, r) = tail_open(os.path.join(dirpath,filename), log_pos.get(os.path.join(dirpath, filename), 0))
131
                current_value = 0
132
                while True:
133
                    where = fh.tell()
134
                    line = fh.readline()
135
                    if line.endswith('\n'):
136
                        current_value += 1
137
                    else:
138
                        # Incomplete last line
139
                        fh.seek(where, 0)
140
                        log_pos[os.path.join(dirpath, filename)] = tail_close(fh)
141
                        break
142
143
                if network_channel.lower() in channel_stats and channel.startswith('#'):
144
                    channel_stats[network_channel.lower()] += current_value
145
                else:
146
                    channel_stats[network_channel.lower()] = current_value
147
148
                if user is not None and user.lower() in user_stats:
149
                    user_stats[user.lower()] += current_value
150
                else:
151
                    user_stats[user.lower()] = current_value
152
    if savestate:
153
        savedata = {}
154
        if int(expire) == 0:
155
            savedata["users"] = user_list
156
            savedata["channels"] = channel_list
157
        savedata["log_pos"] = log_pos
158
        with open(last_values_file, 'w') as f:
159
            json.dump(savedata,f)
160
161
162
def emit_config():
163 8fe960de Thor77
    print('graph_title Lines in the ZNC-log')
164 ed77c82d dipohl
    print('graph_category chat')
165 b17436fe Paul Saunders
    print('graph_vlabel lines / ${graph_period}')
166 8fe960de Thor77
    print('graph_scale no')
167 b17436fe Paul Saunders
    print('graph_args --base 1000 --lower-limit 0')
168
    print('graph_period minute')
169
    graph_order = []
170
171 c81c20ab Lars Kruse
    if os.getenv('MUNIN_CAP_DIRTYCONFIG') == "1":
172 b17436fe Paul Saunders
        read_data(1)
173
    else:
174
        read_data(0)
175
176
    for network in channel_list.keys():
177
        for channel in channel_list[network].keys():
178
179
            # print things to munin
180
            network_channel = "{}_{}".format(network,channel).replace('.', '').replace('#', '').replace('@','_')
181
            print('{network_channel}.label {label}'.format(network_channel=network_channel, label=channel_list[network][channel]))
182
            print('{network_channel}.type  ABSOLUTE'.format(network_channel=network_channel))
183
            print('{network_channel}.min   0'.format(network_channel=network_channel))
184
            print('{network_channel}.draw  AREASTACK'.format(network_channel=network_channel))
185
186
            graph_order.append(network_channel)
187
    for user in user_list.keys():
188
            fuser = re.sub(r'^[^A-Za-z_]', '_', user)
189
            fuser = re.sub(r'[^A-Za-z0-9_]', '_', fuser)
190
            print('{fuser}.label User {user}'.format(fuser=fuser, user=user))
191
            print('{fuser}.type  ABSOLUTE'.format(fuser=fuser))
192
            print('{fuser}.min   0'.format(fuser=fuser))
193
            print('{fuser}.draw  LINE1'.format(fuser=fuser))
194
195
    print('graph_order {}'.format(" ".join(sorted(graph_order, key=str.lower))))
196
197
def emit_values():
198
    read_data(1)
199
    for network in channel_list.keys():
200
        for channel in channel_list[network].keys():
201
202
            # print things to munin
203
            key = channel_list[network][channel]
204
            network_channel = "{}_{}".format(network,channel).replace('.', '').replace('#', '').replace('@','_')
205
            if key.lower() in channel_stats:
206
                print('{network_channel}.value {value}'.format(network_channel=network_channel, value=channel_stats[key.lower()]))
207
            else:
208
                print('{network_channel}.value U'.format(network_channel=network_channel))
209
    for user in user_list.keys():
210
        fuser = re.sub(r'^[^A-Za-z_]', '_', user)
211
        fuser = re.sub(r'[^A-Za-z0-9_]', '_', fuser)
212
        if user.lower() in user_stats:
213
            print('{fuser}.value {value}'.format(fuser=fuser, value=user_stats[user.lower()]))
214
        else:
215
            print('{fuser}.value U'.format(fuser=fuser))
216
217
218
if len(sys.argv) > 1 and sys.argv[1] == 'config':
219
    emit_config()
220
    sys.exit(0)
221
222
emit_values()