1d95e98d88a6cf4347853427dc4ef6fd0222a3881Patrick Bellasi# SPDX-License-Identifier: Apache-2.0
2d95e98d88a6cf4347853427dc4ef6fd0222a3881Patrick Bellasi#
3d95e98d88a6cf4347853427dc4ef6fd0222a3881Patrick Bellasi# Copyright (C) 2015, ARM Limited and contributors.
4d95e98d88a6cf4347853427dc4ef6fd0222a3881Patrick Bellasi#
5d95e98d88a6cf4347853427dc4ef6fd0222a3881Patrick Bellasi# Licensed under the Apache License, Version 2.0 (the "License"); you may
6d95e98d88a6cf4347853427dc4ef6fd0222a3881Patrick Bellasi# not use this file except in compliance with the License.
7d95e98d88a6cf4347853427dc4ef6fd0222a3881Patrick Bellasi# You may obtain a copy of the License at
8d95e98d88a6cf4347853427dc4ef6fd0222a3881Patrick Bellasi#
9d95e98d88a6cf4347853427dc4ef6fd0222a3881Patrick Bellasi# http://www.apache.org/licenses/LICENSE-2.0
10d95e98d88a6cf4347853427dc4ef6fd0222a3881Patrick Bellasi#
11d95e98d88a6cf4347853427dc4ef6fd0222a3881Patrick Bellasi# Unless required by applicable law or agreed to in writing, software
12d95e98d88a6cf4347853427dc4ef6fd0222a3881Patrick Bellasi# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13d95e98d88a6cf4347853427dc4ef6fd0222a3881Patrick Bellasi# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14d95e98d88a6cf4347853427dc4ef6fd0222a3881Patrick Bellasi# See the License for the specific language governing permissions and
15d95e98d88a6cf4347853427dc4ef6fd0222a3881Patrick Bellasi# limitations under the License.
16d95e98d88a6cf4347853427dc4ef6fd0222a3881Patrick Bellasi#
17eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi
18eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasiimport json
19eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasiimport os
20eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasiimport re
21c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasiimport logging
22c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasiimport logging.config
23c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi
24c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi
25c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasiclass LisaLogging(object):
26c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi
27c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi    @classmethod
28c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi    def setup(self, filepath='logging.conf', level=logging.INFO):
29c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi        """
30c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi        Initialize logging used for all the LISA modules.
31c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi
32c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi        :param filepath: the relative or absolute path of the logging configuration to use.
33c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi                         Relative path uses the LISA_HOME environment variable
34c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi                         has base folder.
35c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi        :type filepath: str
36c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi
37c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi        :param level: the default log level to enable, INFO by default
38c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi        :type level: logging.<level> or int in [0..50]
39c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi        """
40c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi
41c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi        # Load the specified logfile using an absolute path
42c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi        basepath = os.path.dirname(__file__).replace('/libs/utils', '')
43c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi        filepath = os.path.join(basepath, filepath)
44c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi        if not os.path.exists(filepath):
45c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi            raise ValueError('Logging configuration file not found in: {}'\
46c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi                             .format(filepath))
47c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi        logging.config.fileConfig(filepath)
48c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi        logging.getLogger().setLevel(level)
49c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi
50c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi        logging.info('Using LISA logging configuration:')
51c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi        logging.info('  %s', filepath)
52eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi
53eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasiclass JsonConf(object):
5404cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman    """
5504cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman    Class for parsing a JSON superset with comments.
5604cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman
5704cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman    Simply strips comments and then uses the standard JSON parser.
5804cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman
5904cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman    :param filename: Path to file to parse
6004cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman    :type filename: str
6104cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman    """
62eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi
63eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi    def __init__(self, filename):
64eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi        self.filename = filename
65eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi        self.json = None
66eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi
67eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi    def load(self):
6804cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman        """
6904cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman        Parse a JSON file
7004cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman
7104cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman        First remove comments and then use the json module package
7204cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman        Comments look like :
7304cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman
7404cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman        ::
7504cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman
7604cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman            // ...
7704cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman
7804cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman        or
7904cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman
8004cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman        ::
8104cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman
8204cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman            /*
8304cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman            ...
8404cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman            */
8504cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman
86eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi        """
87c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi
88c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi        # Setup logging
89c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi        self._log = logging.getLogger('JsonConf')
90c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi
91eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi        if not os.path.isfile(self.filename):
92eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi            raise RuntimeError(
93eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi                'Missing configuration file: {}'.format(self.filename)
94eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi            )
95c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi        self._log.debug('loading JSON...')
96eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi
97eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi        with open(self.filename) as fh:
98eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi            content = ''.join(fh.readlines())
99eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi
100eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi            ## Looking for comments
101eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi            match = JSON_COMMENTS_RE.search(content)
102eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi            while match:
103eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi                # single line comment
104eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi                content = content[:match.start()] + content[match.end():]
105eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi                match = JSON_COMMENTS_RE.search(content)
106eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi
1070759fbf90dfcf666d32e48f63a06871956bddd0cJavi Merino            # Allow trailing commas in dicts an lists in JSON
1080759fbf90dfcf666d32e48f63a06871956bddd0cJavi Merino            # Note that this simple implementation will mangle things like:
1090759fbf90dfcf666d32e48f63a06871956bddd0cJavi Merino            # {"config": ", }"}
1100759fbf90dfcf666d32e48f63a06871956bddd0cJavi Merino            content = re.sub(r',[ \t\r\n]+}', '}', content)
1110759fbf90dfcf666d32e48f63a06871956bddd0cJavi Merino            content = re.sub(r',[ \t\r\n]+\]', ']', content)
1120759fbf90dfcf666d32e48f63a06871956bddd0cJavi Merino
113eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi            # Return json file
114eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi            self.json = json.loads(content, parse_int=int)
115c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi            self._log.debug('Loaded JSON configuration:')
116c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi            self._log.debug('   %s', self.json)
117eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi
118eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi        return self.json
119eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi
120eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi    def show(self):
12104cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman        """
12204cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman        Pretty-print content of parsed JSON
12304cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman        """
124eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi        print json.dumps(self.json, indent=4)
125eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi
126eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi# Regular expression for comments
127eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick BellasiJSON_COMMENTS_RE = re.compile(
12827683041209d4cf7bd5276e82a1622d6b3b01c14Javi Merino    r'(^)?[^\S\n]*/(?:\*(.*?)\*/[^\S\n]*|/[^\n]*)($)?',
129eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi    re.DOTALL | re.MULTILINE
130eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi)
131eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi
132eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi
133