From 0307d222acb8a6c675b985463f56f1071a4e5364 Mon Sep 17 00:00:00 2001 From: Rob Crittenden Date: May 12 2021 15:55:50 +0000 Subject: Parse the debugging cache log to determine the read savings Read the FINAL lines from the Apache error log, optionally from a start time, and calculate the total cache hits and misses and calculate the average read savings. https://pagure.io/freeipa/issue/8798 Signed-off-by: Rob Crittenden Reviewed-By: Rafael Guterres Jeffman --- diff --git a/contrib/cachelog b/contrib/cachelog new file mode 100644 index 0000000..1f77920 --- /dev/null +++ b/contrib/cachelog @@ -0,0 +1,118 @@ +#!/usr/bin/python3 +# +# Copyright (C) 2021 FreeIPA Contributors see COPYING for license +# + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from __future__ import division + +from datetime import datetime +import logging +import re + +from ipapython import admintool +from ipalib.facts import is_ipa_configured + + +# r'(?P\S+)/1\(.*\): (?P\S+) etime=(?P\d+)' +TIME_RE = re.compile( + r'\[(?P.*)\] \[.*\].* \[pid \d+:tid \d+\] \[remote .*\] ' + r'ipa: DEBUG: FINAL: Hits (?P\d+) Misses (?P\d+) ' + r'Size (?P\d+)' +) + +DATE_FORMAT = '%a %b %d %H:%M:%S.%f %Y' + +logger = logging.getLogger(__name__) + + +class cachelog(admintool.AdminTool): + command_name = "cachelog" + + usage = "%prog [options]" + description = "Parse the Apache error log for cache performance data. " \ + "Enable debugging by creating /etc/ipa/server.conf with " \ + "the contents: [global]\\ndebug = True" + + def __init__(self, options, args): + super(cachelog, self).__init__(options, args) + self.since = None + + @classmethod + def add_options(cls, parser): + super(cachelog, cls).add_options(parser, debug_option=True) + parser.add_option( + "--command", + dest="command", + action="store", + default=None, + help="Command to analyze", + ) + parser.add_option( + "--start-time", + dest="start_time", + action="store", + default=None, + help="time to begin analyzing logfile from, e.g. " + "Fri May 7 16:33:08.0 2021", + ) + parser.add_option( + "--file", + dest="file", + action="store", + default="/var/log/httpd/error_log", + help="Log file to parse", + ) + + def validate_options(self): + super(cachelog, self).validate_options(needs_root=True) + + if self.options.start_time: + self.since = datetime.strptime( + self.options.start_time, + DATE_FORMAT + ) + + def run(self): + super(cachelog, self).run() + + if not is_ipa_configured(): + logger.error("IPA server is not configured on this system.") + raise admintool.ScriptError() + + with open(self.options.file, 'r') as f: + data = f.read() + + matches = list(re.finditer(TIME_RE, data)) + + hits = 0 + misses = 0 + count = 0 + + for match in matches: + if self.since: + logtime = datetime.strptime(match.group('date'), DATE_FORMAT) + if logtime < self.since: + continue + hits += int(match.group('hits')) + misses += int(match.group('misses')) + count += 1 + + print('Total reads %d, hits %d, misses %d, avg %1.4f' % + (hits + misses, hits, misses, hits / (hits + misses))) + + +if __name__ == '__main__': + cachelog.run_cli()