blob: a75bd6260b085652eecff689326de5189a9bbb96 [file] [log] [blame]
#!/usr/bin/env python3
#
# Copyright 2016 - The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import importlib
import logging
import os
ACTS_CONTROLLER_CONFIG_NAME = "DiagLogger"
ACTS_CONTROLLER_REFERENCE_NAME = "diag_logger"
class DiagLoggerError(Exception):
"""This is the base exception class for errors generated by
DiagLogger modules.
"""
pass
def create(configs):
"""Initializes the Diagnotic Logger instances based on the
provided JSON configuration(s). The expected keys are:
Package: A package name containing the diagnostic logger
module. It should be in python path of the environment.
Type: A first-level type for the Logger, which should correspond
the name of the module containing the Logger implementation.
SubType: The exact implementation of the sniffer, which should
correspond to the name of the class to be used.
HostLogPath: This is the default directory used to dump any logs
that are captured or any other files that are stored as part
of the logging process. It's use is implementation specific,
but it should be provided by all loggers for completeness.
Configs: A dictionary specifying baseline configurations of the
particular Logger. These configurations may be overridden at
the start of a session.
"""
objs = []
for c in configs:
diag_package_name = c["Package"] # package containing module
diag_logger_type = c["Type"] # module name
diag_logger_name = c["SubType"] # class name
host_log_path = c["HostLogPath"]
base_configs = c["Configs"]
module_name = "{}.{}".format(diag_package_name, diag_logger_type)
module = importlib.import_module(module_name)
logger_class = getattr(module, diag_logger_name)
objs.append(logger_class(host_log_path,
None,
config_container=base_configs))
return objs
def destroy(objs):
"""Stops all ongoing logger sessions, deletes any temporary files, and
prepares logger objects for destruction.
"""
for diag_logger in objs:
try:
diag_logger.reset()
except DiagLoggerError:
# TODO: log if things go badly here
pass
class DiagLoggerBase():
"""Base Class for Proprietary Diagnostic Log Collection
The DiagLoggerBase is a simple interface for running on-device logging via
a standard workflow that can be integrated without the caller actually
needing to know the details of what logs are captured or how.
The workflow is as follows:
1) Create a DiagLoggerBase Object
2) Call start() to begin an active logging session.
3) Call stop() to end an active logging session.
4) Call pull() to ensure all collected logs are stored at
'host_log_path'
5) Call reset() to stop all logging and clear any unretrieved logs.
"""
def __init__(self, host_log_path, logger=None, config_container=None):
"""Create a Diagnostic Logging Proxy Object
Args:
host_log_path: File path where retrieved logs should be stored
config_container: A transparent container used to pass config info
"""
self.host_log_path = os.path.realpath(os.path.expanduser(
host_log_path))
self.config_container = config_container
if not os.path.isdir(self.host_log_path):
os.mkdir(self.host_log_path)
self.logger = logger
if not self.logger:
self.logger = logging.getLogger(self.__class__.__name__)
def start(self, config_container=None):
"""Start collecting Diagnostic Logs
Args:
config_container: A transparent container used to pass config info
Returns:
A logging session ID that can be later used to stop the session
For Diag interfaces supporting only one session this is unneeded
"""
raise NotImplementedError("Base class should not be invoked directly!")
def stop(self, session_id=None):
"""Stop collecting Diagnostic Logs
Args:
session_id: an optional session id provided for multi session
logging support
Returns:
"""
raise NotImplementedError("Base class should not be invoked directly!")
def pull(self, session_id=None, out_path=None):
"""Save all cached diagnostic logs collected to the host
Args:
session_id: an optional session id provided for multi session
logging support
out_path: an optional override to host_log_path for a specific set
of logs
Returns:
An integer representing a port number on the host available for adb
forward.
"""
raise NotImplementedError("Base class should not be invoked directly!")
def reset(self):
"""Stop any ongoing logging sessions and clear any cached logs that have
not been retrieved with pull(). This must delete all session records and
return the logging object to a state equal to when constructed.
"""
raise NotImplementedError("Base class should not be invoked directly!")
def get_log_path(self):
"""Return the log path for this object"""
return self.host_log_path