156 lines
4.6 KiB
Python
156 lines
4.6 KiB
Python
# Copyright 2015 kornicameister@gmail.com
|
|
# Copyright 2016 FUJITSU LIMITED
|
|
#
|
|
# 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 datetime
|
|
|
|
from monasca_common.rest import utils as rest_utils
|
|
from oslo_config import cfg
|
|
from oslo_log import log
|
|
|
|
from monasca_log_api.api import exceptions
|
|
from monasca_log_api import conf
|
|
from monasca_log_api.reference.common import model
|
|
from monasca_log_api.reference.common import validation
|
|
|
|
LOG = log.getLogger(__name__)
|
|
CONF = conf.CONF
|
|
|
|
EPOCH_START = datetime.datetime(1970, 1, 1)
|
|
|
|
|
|
class LogCreator(object):
|
|
"""Transforms logs,
|
|
|
|
Takes care of transforming information received via
|
|
HTTP requests into log and log envelopes objects.
|
|
|
|
For more details see following:
|
|
|
|
* :py:func:`LogCreator.new_log`
|
|
* :py:func:`LogCreator.new_log_envelope`
|
|
|
|
"""
|
|
|
|
def __init__(self):
|
|
self._log = log.getLogger('service.LogCreator')
|
|
self._log.info('Initializing LogCreator')
|
|
|
|
@staticmethod
|
|
def _create_meta_info(tenant_id):
|
|
"""Creates meta block for log envelope.
|
|
|
|
Additionally method accesses oslo configuration,
|
|
looking for *service.region* configuration property.
|
|
|
|
For more details see :py:data:`service_opts`
|
|
|
|
:param tenant_id: ID of the tenant
|
|
:type tenant_id: str
|
|
:return: meta block
|
|
:rtype: dict
|
|
|
|
"""
|
|
return {
|
|
'tenantId': tenant_id,
|
|
'region': cfg.CONF.service.region
|
|
}
|
|
|
|
def new_log(self,
|
|
application_type,
|
|
dimensions,
|
|
payload,
|
|
content_type='application/json',
|
|
validate=True):
|
|
"""Creates new log object.
|
|
|
|
:param str application_type: origin of the log
|
|
:param dict dimensions: dictionary of dimensions (any data sent to api)
|
|
:param stream payload: stream to read log entry from
|
|
:param str content_type: actual content type used to send data to
|
|
server
|
|
:param bool validate: by default True, marks if log should be validated
|
|
:return: log object
|
|
:rtype: dict
|
|
|
|
:keyword: log_object
|
|
"""
|
|
|
|
payload = rest_utils.read_body(payload, content_type)
|
|
if not payload:
|
|
return None
|
|
|
|
# normalize_yet_again
|
|
application_type = parse_application_type(application_type)
|
|
dimensions = parse_dimensions(dimensions)
|
|
|
|
if validate:
|
|
self._log.debug('Validation enabled, proceeding with validation')
|
|
validation.validate_application_type(application_type)
|
|
validation.validate_dimensions(dimensions)
|
|
|
|
self._log.debug(
|
|
'application_type=%s,dimensions=%s' % (
|
|
application_type, dimensions)
|
|
)
|
|
|
|
log_object = {}
|
|
if content_type == 'application/json':
|
|
log_object.update(payload)
|
|
else:
|
|
log_object.update({'message': payload})
|
|
|
|
validation.validate_log_message(log_object)
|
|
|
|
dimensions['component'] = application_type
|
|
log_object.update({'dimensions': dimensions})
|
|
|
|
return log_object
|
|
|
|
def new_log_envelope(self, log_object, tenant_id):
|
|
return model.Envelope(
|
|
log=log_object,
|
|
meta=self._create_meta_info(tenant_id)
|
|
)
|
|
|
|
|
|
def parse_application_type(app_type):
|
|
if app_type:
|
|
app_type = app_type.strip()
|
|
return app_type if app_type else None
|
|
|
|
|
|
def parse_dimensions(dimensions):
|
|
if not dimensions:
|
|
raise exceptions.HTTPUnprocessableEntity('Dimension are required')
|
|
|
|
new_dimensions = {}
|
|
dimensions = map(str.strip, dimensions.split(','))
|
|
|
|
for dim in dimensions:
|
|
if not dim:
|
|
raise exceptions.HTTPUnprocessableEntity(
|
|
'Dimension cannot be empty')
|
|
elif ':' not in dim:
|
|
raise exceptions.HTTPUnprocessableEntity(
|
|
'%s is not a valid dimension' % dim)
|
|
|
|
dim = dim.split(':')
|
|
name = str(dim[0].strip()) if dim[0] else None
|
|
value = str(dim[1].strip()) if dim[1] else None
|
|
if name and value:
|
|
new_dimensions.update({name: value})
|
|
|
|
return new_dimensions
|