blob: de9524abd6017d823e1669fb497cff2efc18464e [file] [log] [blame]
#!/usr/bin/env python
# Copyright 2017 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""A script to parse apache error logs
The script gets the contents of the log file through stdin, and emits a counter
metric for the beginning of each error message it recognizes.
"""
from __future__ import print_function
import argparse
import re
import sys
import common
from chromite.lib import metrics
from chromite.lib import ts_mon_config
from autotest_lib.site_utils.stats import log_daemon_common
LOOP_INTERVAL = 60
ERROR_LOG_METRIC = '/chromeos/autotest/apache/error_log'
ERROR_LOG_LINE_METRIC = '/chromeos/autotest/apache/error_log_line'
ERROR_LOG_MATCHER = re.compile(
r'^\[[^]]+\] ' # The timestamp. We don't need this.
r'\[:(?P<log_level>\S+)\] '
r'\[pid \d+[^]]+\] ' # The PID, possibly followed by a task id.
# There may be other sections, such as [remote <ip>]
r'(?P<sections>\[[^]]+\] )*'
r'\S' # first character after pid must be non-space; otherwise it is
# indented, meaning it is just a continuation of a previous message.
r'(?P<mod_wsgi>od_wsgi)?' # Note: the 'm' of mod_wsgi was already matched.
)
def EmitErrorLogMetric(m):
"""Emits a Counter metric for error log messages.
@param m: A regex match object
"""
log_level = m.group('log_level') or ''
# It might be interesting to see whether the error/warning was emitted
# from python at the mod_wsgi process or not.
mod_wsgi_present = bool(m.group('mod_wsgi'))
metrics.Counter(ERROR_LOG_METRIC).increment(fields={
'log_level': log_level,
'mod_wsgi': mod_wsgi_present})
def EmitErrorLogLineMetric(_m):
"""Emits a Counter metric for each error log line.
@param _m: A regex match object.
"""
metrics.Counter(ERROR_LOG_LINE_METRIC).increment()
MATCHERS = [
(ERROR_LOG_MATCHER, EmitErrorLogMetric),
(re.compile(r'.*'), EmitErrorLogLineMetric),
]
def ParseArgs():
"""Parses the command line arguments."""
p = argparse.ArgumentParser(
description='Parses apache logs and emits metrics to Monarch')
p.add_argument('--output-logfile')
p.add_argument('--debug-metrics-file',
help='Output metrics to the given file instead of sending '
'them to production.')
return p.parse_args()
def Main():
"""Sets up logging and runs matchers against stdin"""
args = ParseArgs()
log_daemon_common.SetupLogging(args)
# Set up metrics sending and go.
ts_mon_args = {}
if args.debug_metrics_file:
ts_mon_args['debug_file'] = args.debug_metrics_file
with ts_mon_config.SetupTsMonGlobalState('apache_error_log_metrics',
**ts_mon_args):
log_daemon_common.RunMatchers(sys.stdin, MATCHERS)
metrics.Flush()
if __name__ == '__main__':
Main()