1 #!/usr/bin/env python3.4 2 # 3 # Copyright 2016 - The Android Open Source Project 4 # 5 # Licensed under the Apache License, Version 2.0 (the "License"); 6 # you may not use this file except in compliance with the License. 7 # You may obtain a copy of the License at 8 # 9 # http://www.apache.org/licenses/LICENSE-2.0 10 # 11 # Unless required by applicable law or agreed to in writing, software 12 # distributed under the License is distributed on an "AS IS" BASIS, 13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 # See the License for the specific language governing permissions and 15 # limitations under the License. 16 17 import importlib 18 import logging 19 import os 20 21 ACTS_CONTROLLER_CONFIG_NAME = "DiagLogger" 22 ACTS_CONTROLLER_REFERENCE_NAME = "diag_logger" 23 24 25 class DiagLoggerError(Exception): 26 """This is the base exception class for errors generated by 27 DiagLogger modules. 28 """ 29 pass 30 31 32 def create(configs, logger): 33 """Initializes the Diagnotic Logger instances based on the 34 provided JSON configuration(s). The expected keys are: 35 36 Package: A package name containing the diagnostic logger 37 module. It should be in python path of the environment. 38 Type: A first-level type for the Logger, which should correspond 39 the name of the module containing the Logger implementation. 40 SubType: The exact implementation of the sniffer, which should 41 correspond to the name of the class to be used. 42 HostLogPath: This is the default directory used to dump any logs 43 that are captured or any other files that are stored as part 44 of the logging process. It's use is implementation specific, 45 but it should be provided by all loggers for completeness. 46 Configs: A dictionary specifying baseline configurations of the 47 particular Logger. These configurations may be overridden at 48 the start of a session. 49 """ 50 objs = [] 51 for c in configs: 52 diag_package_name = c["Package"] # package containing module 53 diag_logger_type = c["Type"] # module name 54 diag_logger_name = c["SubType"] # class name 55 host_log_path = c["HostLogPath"] 56 base_configs = c["Configs"] 57 module_name = "{}.{}".format(diag_package_name, diag_logger_type) 58 module = importlib.import_module(module_name) 59 logger = getattr(module, diag_logger_name) 60 61 objs.append(logger(host_log_path, 62 logger, 63 config_container=base_configs)) 64 return objs 65 66 67 def destroy(objs): 68 """Stops all ongoing logger sessions, deletes any temporary files, and 69 prepares logger objects for destruction. 70 """ 71 for diag_logger in objs: 72 try: 73 diag_logger.reset() 74 except DiagLoggerError: 75 # TODO: log if things go badly here 76 pass 77 78 79 class DiagLoggerBase(): 80 """Base Class for Proprietary Diagnostic Log Collection 81 82 The DiagLoggerBase is a simple interface for running on-device logging via 83 a standard workflow that can be integrated without the caller actually 84 needing to know the details of what logs are captured or how. 85 The workflow is as follows: 86 87 1) Create a DiagLoggerBase Object 88 2) Call start() to begin an active logging session. 89 3) Call stop() to end an active logging session. 90 4) Call pull() to ensure all collected logs are stored at 91 'host_log_path' 92 5) Call reset() to stop all logging and clear any unretrieved logs. 93 """ 94 95 def __init__(self, host_log_path, logger=None, config_container=None): 96 """Create a Diagnostic Logging Proxy Object 97 98 Args: 99 host_log_path: File path where retrieved logs should be stored 100 config_container: A transparent container used to pass config info 101 """ 102 self.host_log_path = os.path.realpath(os.path.expanduser( 103 host_log_path)) 104 self.config_container = config_container 105 if not os.path.isdir(self.host_log_path): 106 os.mkdir(self.host_log_path) 107 self.logger = logger 108 if not self.logger: 109 self.logger = logging.getLogger(self.__class__.__name__) 110 111 def start(self, config_container=None): 112 """Start collecting Diagnostic Logs 113 114 Args: 115 config_container: A transparent container used to pass config info 116 117 Returns: 118 A logging session ID that can be later used to stop the session 119 For Diag interfaces supporting only one session this is unneeded 120 """ 121 raise NotImplementedError("Base class should not be invoked directly!") 122 123 def stop(self, session_id=None): 124 """Stop collecting Diagnostic Logs 125 126 Args: 127 session_id: an optional session id provided for multi session 128 logging support 129 130 Returns: 131 """ 132 raise NotImplementedError("Base class should not be invoked directly!") 133 134 def pull(self, session_id=None, out_path=None): 135 """Save all cached diagnostic logs collected to the host 136 137 Args: 138 session_id: an optional session id provided for multi session 139 logging support 140 141 out_path: an optional override to host_log_path for a specific set 142 of logs 143 144 Returns: 145 An integer representing a port number on the host available for adb 146 forward. 147 """ 148 raise NotImplementedError("Base class should not be invoked directly!") 149 150 def reset(self): 151 """Stop any ongoing logging sessions and clear any cached logs that have 152 not been retrieved with pull(). This must delete all session records and 153 return the logging object to a state equal to when constructed. 154 """ 155 raise NotImplementedError("Base class should not be invoked directly!") 156 157 def get_log_path(self): 158 """Return the log path for this object""" 159 return self.host_log_path 160