• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
17import importlib
18import logging
19import os
20
21ACTS_CONTROLLER_CONFIG_NAME = "DiagLogger"
22ACTS_CONTROLLER_REFERENCE_NAME = "diag_logger"
23
24
25class DiagLoggerError(Exception):
26    """This is the base exception class for errors generated by
27    DiagLogger modules.
28    """
29    pass
30
31
32def 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
67def 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
79class 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