Skip to content

Commit

Permalink
Improved Logging
Browse files Browse the repository at this point in the history
  • Loading branch information
verdan committed Dec 23, 2019
1 parent 675a62b commit 18e6940
Show file tree
Hide file tree
Showing 8 changed files with 139 additions and 70 deletions.
6 changes: 5 additions & 1 deletion atlasclient/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
__version__ = '1.0.1'
__version__ = '1.0.2'

# Set default logging handler to avoid "No handler found" warnings.
import logging
logging.getLogger('pyatlasclient').addHandler(logging.NullHandler())
38 changes: 30 additions & 8 deletions atlasclient/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,17 @@
Defines all the base classes for response objects.
"""

from datetime import datetime, timedelta
import ast
import json
import logging
import time
import ast

import six
import time
from datetime import datetime, timedelta

from atlasclient import events, exceptions, utils

LOG = logging.getLogger(__name__)
LOG.addHandler(utils.NullHandler())
LOG = logging.getLogger('pyatlasclient')

OLDEST_SUPPORTED_VERSION = (1, 7, 0)

Expand Down Expand Up @@ -259,6 +258,7 @@ def inflate(self):
# In case of DSL Queries, we can specify the list in a query
# but this will try to evaluate this as a list and failed as syntax error.
self._filter[k] = v
LOG.debug("Trying to fetch collection from server - ".format(self.model_class.__class__.__name__))
self.load(self.client.get(self.url, params=self._filter))

self._is_inflated = True
Expand All @@ -275,6 +275,7 @@ def load(self, response):
In some rare cases, a collection can have an asynchronous request
triggered. For those cases, we handle it here.
"""
LOG.debug("Parsing the GET response for the collection - {}".format(self.model_class.__class__.__name__))
self._models = []
if isinstance(response, dict):
for key in response.keys():
Expand All @@ -290,6 +291,7 @@ def load(self, response):

def create(self, *args, **kwargs):
"""Add a resource to this collection."""
LOG.debug(f"Adding a new resource to the collection {self.__class__.__name__} with the data {kwargs}")
href = self.url
if len(args) == 1:
kwargs[self.model_class.primary_key] = args[0]
Expand All @@ -303,13 +305,17 @@ def create(self, *args, **kwargs):

def update(self, **kwargs):
"""Update all resources in this collection."""
LOG.debug(f"Updating all resources in the collection "
f"{self.model_class.__class__.__name__} "
f"with the following arguments {kwargs}")
self.inflate()
for model in self._models:
model.update(**kwargs)
return self

def delete(self, **kwargs):
"""Delete all resources in this collection."""
LOG.debug("Deleting all resources in this collection: ".format(self.model_class.__class__.__name__))
self.inflate()
for model in self._models:
model.delete(**kwargs)
Expand Down Expand Up @@ -357,6 +363,7 @@ def __call__(self, *args):
What you start with is all you ever get. If the parent resource is
reloaded, it should create new collections for these resources.
"""
LOG.debug("Generating the models for this collection: ".format(self.model_class.__class__.__name__))
items = []
if len(args) == 1:
if isinstance(args[0], list):
Expand All @@ -366,9 +373,11 @@ def __call__(self, *args):
if len(matches) == 1:
return matches[0]
elif len(matches) > 1:
raise ValueError("More than one {0} with {1} '{2}' found in "
"collection".format(self.model_class.__class__.__name__,
self.model_class.primary_key, args[0]))
error_message = "More than one {0} with {1} '{2}' found in collection" \
.format(self.model_class.__class__.__name__,
self.model_class.primary_key, args[0])
LOG.error(error_message)
raise ValueError(error_message)
return None

if len(items) > 0:
Expand Down Expand Up @@ -418,6 +427,7 @@ class Model(object):
min_version = OLDEST_SUPPORTED_VERSION

def __init__(self, parent, data=None):
LOG.debug("Generating new model class: {} with the data: {}".format(self.__class__.__name__, data))
if data is None:
data = {}

Expand Down Expand Up @@ -471,6 +481,7 @@ def __getattr__(self, attr):
if attr in self.fields:
# if it came from a parent inflation, we might only have partial data
if attr not in self._data:
LOG.debug(f"Lazy-loading the relationship attribute: '{attr}'.")
self.inflate()
return self._data.get(attr)

Expand Down Expand Up @@ -612,6 +623,7 @@ def inflate(self):
msg = ("There is not enough data to inflate this object. "
"Need either an href: {} or a {}: {}")
msg = msg.format(self._href, self.primary_key, self._data.get(self.primary_key))
LOG.error(msg)
raise exceptions.ClientError(msg)

self._is_inflating = True
Expand All @@ -632,14 +644,17 @@ def _generate_input_dict(self, **kwargs):
data = {self.data_key: {}}
if len(kwargs) == 0:
data = self._data
LOG.info(f"Input data generated: {data}")
return data
for field in kwargs:
if field in self.fields:
data[self.data_key][field] = kwargs[field]
else:
data[field] = kwargs[field]
LOG.info(f"Input data generated: {data}")
return data
else:
LOG.info(f"No data key specified - Using kwargs: {kwargs}")
return kwargs

@events.evented
Expand All @@ -656,6 +671,8 @@ def load(self, response):
details are returned in a 'Requests' section. We need to store that
request object so we can poll it until completion.
"""
LOG.info(f"Trying to parse the raw JSON response from the server for "
f"{self.__class__.__name__} Response: {response}")
if 'href' in response:
self._href = response.pop('href')
if self.data_key and self.data_key in response:
Expand All @@ -682,6 +699,8 @@ def create(self, **kwargs):
if self.primary_key in kwargs:
del kwargs[self.primary_key]
data = self._generate_input_dict(**kwargs)
LOG.info(f"Creating a new instance of the resource "
f"{self.__class__.__name__}, with data: {data}")
self.load(self.client.post(self.url, data=data))
return self

Expand All @@ -702,6 +721,8 @@ def update(self, **kwargs):
"""
self.method = 'put'
data = self._generate_input_dict(**kwargs)
LOG.info(f"Updating an instance of the resource "
f"{self.__class__.__name__}, with data: {data}")
self.load(self.client.put(self.url, data=data))
return self

Expand All @@ -713,6 +734,7 @@ def delete(self, **kwargs):
self.load(self.client.delete(self.url, params=kwargs))
else:
self.load(self.client.delete(self.url))
LOG.info(f"Deleting the resource {self.__class__.__name__} using url: {self.url}")
self.parent.remove(self)
return

Expand Down
18 changes: 12 additions & 6 deletions atlasclient/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,18 @@
# under the License.

import copy
import functools
import io
import json
import logging
import tarfile

import functools
import io
import requests

from atlasclient import models, utils, base, exceptions
from atlasclient.exceptions import handle_response

LOG = logging.getLogger(__name__)
LOG.addHandler(utils.NullHandler())
LOG = logging.getLogger('pyatlasclient')

# this defines where the Atlas client delegates to for actual logic
ENTRY_POINTS = {'entity_guid': models.EntityGuid,
Expand Down Expand Up @@ -54,7 +53,7 @@
'relationship_guid': models.RelationshipGuid,
'search_saved': models.SearchSaved,
'admin_metrics': models.AdminMetrics
}
}


class Atlas(object):
Expand All @@ -63,6 +62,7 @@ class Atlas(object):
This is the entry point to the Atlas API. Create this client and then
use one of the entry points to start hitting Atlas object collections.
"""

def __init__(self, host, port=None, username=None, password=None,
identifier=None, protocol=None, validate_ssl=True,
timeout=10, max_retries=5, auth=None):
Expand Down Expand Up @@ -115,13 +115,14 @@ class HttpClient(object):
was supplied by the API. This should be uncommon except for error cases, but
cases do exist either due to Atlas bugs or other mitigating circumstances.
"""

def __init__(self, host, username, password, identifier, validate_ssl=True,
timeout=10, max_retries=5, auth=None):
basic_token = utils.generate_http_basic_token(username=username, password=password)
self.request_params = {
'headers': {'X-Requested-By': identifier,
'Authorization': 'Basic {}'.format(basic_token)},
#'auth': (username, password),
# 'auth': (username, password),
'verify': validate_ssl,
'timeout': timeout,
}
Expand Down Expand Up @@ -151,6 +152,10 @@ def request(self, method, url, content_type=None, **kwargs):
elif 'data' in params and isinstance(params['data'], list):
params['data'] = json.dumps(params['data'])

LOG.debug(f"Requesting Atlas with the '{method}' method.")
if params.get('data'):
LOG.debug(f"With the following data: {params['data']}")

response = requests_method(url, **params)

# any error responses will generate exceptions here
Expand Down Expand Up @@ -191,6 +196,7 @@ class AtlasJsonEncoder(json.JSONEncoder):
This allows for passing in models and ModelCollections into related objects'
create/update methods and having it handle the conversion automatically.
"""

def default(self, obj): # pylint: disable=method-hidden
if isinstance(obj, base.ModelCollection):
dicts = []
Expand Down
9 changes: 3 additions & 6 deletions atlasclient/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,11 @@
# License for the specific language governing permissions and limitations
# under the License.

from collections import namedtuple
import logging
import inspect
import logging
from collections import namedtuple

from atlasclient.utils import NullHandler

LOG = logging.getLogger(__name__)
LOG.addHandler(NullHandler())
LOG = logging.getLogger('pyatlasclient')

EVENT_HANDLERS = {}
state_list = ['ANY', 'STARTED', 'FAILED', 'FINISHED', 'PROGRESS']
Expand Down
23 changes: 17 additions & 6 deletions atlasclient/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
https://github.com/openstack/python-novaclient/blob/master/novaclient/exceptions.py
"""
import logging

LOG = logging.getLogger('pyatlasclient')


class ClientError(Exception):
Expand All @@ -30,7 +33,9 @@ def __init__(self, message=None):
super(ClientError, self).__init__()

def __str__(self):
return "Unexpected client-side error: %s" % self.message
exception_message = "Unexpected client-side error: %s" % self.message
LOG.error(exception_message)
return exception_message


class Timeout(Exception):
Expand All @@ -45,7 +50,9 @@ def __init__(self, timeout, message=None):
super(Timeout, self).__init__()

def __str__(self):
return "Timed out after %s seconds: %s" % (self.timeout, self.message)
exception_message = "Timed out after %s seconds: %s" % (self.timeout, self.message)
LOG.error(exception_message)
return exception_message


class Failed(Exception):
Expand All @@ -60,9 +67,11 @@ def __init__(self, model, message=None):
super(Failed, self).__init__()

def __str__(self):
return "Failure detected for %s/%s: %s" % (self.model.__class__.__name__,
self.model.identifier,
self.message)
exception_message = "Failure detected for %s/%s: %s" % (self.model.__class__.__name__,
self.model.identifier,
self.message)
LOG.error(exception_message)
return exception_message


class HttpError(Exception):
Expand All @@ -84,7 +93,9 @@ def __init__(self, code=None, message=None, details=None, url=None, method=None,

def __str__(self):
params = (self.method, self.url, self.message, self.code, self.details)
return "HTTP request failed for %s %s: %s %s: %s" % params
exception_message = "HTTP request failed for %s %s: %s %s: %s" % params
LOG.error(exception_message)
return exception_message


class BadRequest(HttpError):
Expand Down
Loading

0 comments on commit 18e6940

Please sign in to comment.