[autotest] RPC logging server
frontend.afe.rpc_handler is not logging RPC logs correctly
due to multi apache processes.
We should have a daemon that receives log records from the
apache processes and writes them in a chronological order.
BUG=chromium:490897
TEST=Run dummy suite on local machine.
DEPLOY=apache
Change-Id: If53667720922d0db248225d17b9e5a31d350f063
Reviewed-on: https://chromium-review.googlesource.com/274827
Reviewed-by: Dan Shi <dshi@chromium.org>
Reviewed-by: Fang Deng <fdeng@chromium.org>
Tested-by: Mungyung Ryu <mkryu@google.com>
Commit-Queue: Mungyung Ryu <mkryu@google.com>
diff --git a/frontend/afe/rpc_handler.py b/frontend/afe/rpc_handler.py
index 52c0adf..a197239 100644
--- a/frontend/afe/rpc_handler.py
+++ b/frontend/afe/rpc_handler.py
@@ -13,9 +13,10 @@
LOGGING_REGEXPS = [r'.*add_.*',
r'delete_.*',
- r'.*_remove_.*',
+ r'.*remove_.*',
r'modify_.*',
- r'create.*']
+ r'create.*',
+ r'set_.*']
FULL_REGEXP = '(' + '|'.join(LOGGING_REGEXPS) + ')'
COMPILED_REGEXP = re.compile(FULL_REGEXP)
diff --git a/frontend/afe/rpcserver_logging.py b/frontend/afe/rpcserver_logging.py
index a8523fb..f181672 100644
--- a/frontend/afe/rpcserver_logging.py
+++ b/frontend/afe/rpcserver_logging.py
@@ -1,29 +1,23 @@
import logging, logging.handlers, time, os
import common
from autotest_lib.client.common_lib import global_config
+from autotest_lib.site_utils import rpc_logserver
config = global_config.global_config
LOGGING_ENABLED = config.get_config_value('SERVER', 'rpc_logging', type=bool)
-MEGABYTE = 1024 * 1024
-
rpc_logger = None
-def configure_logging():
- MAX_LOG_SIZE = config.get_config_value('SERVER', 'rpc_max_log_size_mb',
- type=int)
- NUMBER_OF_OLD_LOGS = config.get_config_value('SERVER', 'rpc_num_old_logs',
- type=int)
- log_path = config.get_config_value('SERVER', 'rpc_log_path')
- formatter = logging.Formatter(
- fmt='[%(asctime)s %(levelname)-5.5s] %(message)s',
- datefmt='%m/%d %H:%M:%S')
- handler = logging.handlers.RotatingFileHandler(log_path,
- maxBytes=MAX_LOG_SIZE*MEGABYTE,
- backupCount=NUMBER_OF_OLD_LOGS)
- handler.setFormatter(formatter)
+def configure_logging():
+ logserver_enabled = config.get_config_value(
+ 'SERVER', 'rpc_logserver', type=bool)
+ if logserver_enabled:
+ handler = logging.handlers.SocketHandler(
+ 'localhost', rpc_logserver.DEFAULT_PORT)
+ else:
+ handler = rpc_logserver.get_logging_handler()
global rpc_logger
rpc_logger = logging.getLogger('rpc_logger')
diff --git a/global_config.ini b/global_config.ini
index 637ef3b..f0702eb 100644
--- a/global_config.ini
+++ b/global_config.ini
@@ -104,6 +104,8 @@
# Number of old logs to keep around
rpc_num_old_logs: 5
rpc_max_log_size_mb: 20
+# Transfer RPC logs to a RPC logging server
+rpc_logserver: False
# Minimum amount of disk space required for AutoTest in GB
gb_diskspace_required: 1.0
# Minmum number of i-nodes for stateful, in 1000 i-node units.
diff --git a/site_utils/rpc_logserver.py b/site_utils/rpc_logserver.py
new file mode 100755
index 0000000..dd74457
--- /dev/null
+++ b/site_utils/rpc_logserver.py
@@ -0,0 +1,129 @@
+#!/usr/bin/python
+#
+# Copyright (c) 2015 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.
+
+
+import argparse
+import ctypes
+import logging
+import logging.handlers
+import multiprocessing
+import signal
+import sys
+import time
+
+import common
+from autotest_lib.client.common_lib.global_config import global_config as config
+from autotest_lib.site_utils import log_socket_server
+
+
+DEFAULT_PORT = 9080
+LOGGING_FORMAT = '%(asctime)s.%(msecs)03d %(levelname)-5.5s| %(message)s'
+MEGABYTE = 1024 * 1024
+
+
+class LogServerAlreadyRunningError(Exception):
+ pass
+
+
+class LogServer(object):
+ """A wrapper class to start and stop a TCP server for logging."""
+
+ process = None
+
+ @staticmethod
+ def start(port, log_handler):
+ """Start Log Record Socket Receiver in a new process.
+
+ @param port: Port to listen on.
+ @param log_handler: Logging handler.
+
+ @raise Exception: if TCP server is already running.
+ """
+ if LogServer.process:
+ raise LogServerAlreadyRunningError('LogServer is already running.')
+ server_started = multiprocessing.Value(ctypes.c_bool, False)
+ LogServer.process = multiprocessing.Process(
+ target=LogServer._run,
+ args=(server_started, port, log_handler))
+ LogServer.process.start()
+ while not server_started.value:
+ time.sleep(0.1)
+ print 'LogServer is started at port %d.' % port
+
+
+ @staticmethod
+ def _run(server_started, port, log_handler):
+ """Run LogRecordSocketReceiver to receive log.
+
+ @param server_started: True if socket log server is started.
+ @param port: Port used by socket log server.
+ @param log_handler: Logging handler.
+ """
+ # Clear all existing log handlers.
+ logging.getLogger().handlers = []
+ logging.getLogger().addHandler(log_handler)
+
+ tcp_server = log_socket_server.LogRecordSocketReceiver(
+ port=port)
+ print('Starting LogServer...')
+ server_started.value = True
+ tcp_server.serve_until_stopped()
+
+
+ @staticmethod
+ def stop():
+ """Stop LogServer."""
+ if LogServer.process:
+ LogServer.process.terminate()
+ LogServer.process = None
+
+
+def signal_handler(signal, frame):
+ """Handler for signal SIGINT.
+
+ @param signal: SIGINT
+ @param frame: the current stack frame
+ """
+ LogServer.stop()
+ sys.exit(0)
+
+
+def get_logging_handler():
+ """Return a logging handler.
+
+ Configure a RPC logging handler based on global_config and return
+ the handler.
+ """
+ max_log_size = config.get_config_value('SERVER', 'rpc_max_log_size_mb',
+ type=int)
+ number_of_old_logs = config.get_config_value('SERVER', 'rpc_num_old_logs',
+ type=int)
+ log_path = config.get_config_value('SERVER', 'rpc_log_path')
+
+ formatter = logging.Formatter(
+ fmt=LOGGING_FORMAT, datefmt='%m/%d %H:%M:%S')
+ handler = logging.handlers.RotatingFileHandler(
+ log_path,
+ maxBytes=max_log_size*MEGABYTE,
+ backupCount=number_of_old_logs)
+ handler.setFormatter(formatter)
+ return handler
+
+
+def main():
+ parser = argparse.ArgumentParser(
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter)
+ parser.add_argument('-p', type=int, dest='port',
+ help=('Listening port number'), default=DEFAULT_PORT)
+ options = parser.parse_args()
+
+ signal.signal(signal.SIGINT, signal_handler)
+
+ LogServer.start(options.port, get_logging_handler())
+
+
+if __name__ == '__main__':
+ sys.exit(main())