FFFF Add Error Reporting Client · googleapis/google-cloud-python@b3eba02 · GitHub
[go: up one dir, main page]

Skip to content

Commit b3eba02

Browse files
author
Bill Prin
committed
Add Error Reporting Client
1 parent 771bc7a commit b3eba02

File tree

7 files changed

+389
-0
lines changed
  • < 8000 div class="PRIVATE_VisuallyHidden prc-TreeView-TreeViewVisuallyHidden-1N8xK" aria-hidden="true" id="_R_6mml9bH1_">
    docs
  • gcloud/error_reporting
  • scripts
  • 7 files changed

    +389
    -0
    lines changed

    docs/error-reporting-client.rst

    Lines changed: 7 additions & 0 deletions
    Original file line numberDiff line numberDiff line change
    @@ -0,0 +1,7 @@
    1+
    Error Reporting Client
    2+
    =======================
    3+
    4+
    .. automodule:: gcloud.error_reporting.client
    5+
    :members:
    6+
    :show-inheritance:
    7+

    docs/error-reporting-usage.rst

    Lines changed: 68 additions & 0 deletions
    Original file line numberDiff line numberDiff line change
    @@ -0,0 +1,68 @@
    1+
    Using the API
    2+
    =============
    3+
    4+
    5+
    Authentication and Configuration
    6+
    --------------------------------
    7+
    8+
    - For an overview of authentication in ``gcloud-python``,
    9+
    see :doc:`gcloud-auth`.
    10+
    11+
    - In addition to any authentication configuration, you should also set the
    12+
    :envvar:`GCLOUD_PROJECT` environment variable for the project you'd like
    13+
    to interact with. If you are Google App Engine or Google Compute Engine
    14+
    this will be detected automatically.
    15+
    16+
    - After configuring your environment, create a
    17+
    :class:`Client <gcloud.logging.client.Client>`
    18+
    19+
    .. doctest::
    20+
    21+
    >>> from gcloud import error_reporting
    22+
    >>> client = error_reporting.Client()
    23+
    24+
    or pass in ``credentials`` and ``project`` explicitly
    25+
    26+
    .. doctest::
    27+
    28+
    >>> from gcloud import error_reporting
    29+
    >>> client = error_reporting.Client(project='my-project', credentials=creds)
    30+
    31+
    Error Reporting associates errors with a service, which is an identifier for an executable,
    32+
    App Engine module, or job. The default service is "python", but a default can be specified
    33+
    for the client on construction time. You can also optionally specify a version for that service,
    34+
    which defaults to "default."
    35+
    36+
    37+
    .. doctest::
    38+
    39+
    >>> from gcloud import error_reporting
    40+
    >>> client = error_reporting.Client(project='my-project',
    41+
    ... service="login_service",
    42+
    ... version="0.1.0")
    43+
    44+
    Reporting an exception
    45+
    -----------------------
    46+
    47+
    Report a stacktrace to Stackdriver Error Reporting after an exception
    48+
    49+
    .. doctest::
    50+
    51+
    >>> from gcloud import error_reporting
    52+
    >>> client = error_reporting.Client()
    53+
    >>> try:
    54+
    >>> raise NameError
    55+
    >>> except Exception:
    56+
    >>> client.report(message="Something went wrong")
    57+
    58+
    59+
    By default, the client will report the error using the service specified in the client's
    60+
    constructor, or the default service of "python". The service can also be manually specified
    61+
    in the parameters:
    62+
    63+
    .. doctest::
    64+
    65+
    >>> try:
    66+
    >>> raise NameError
    67+
    >>> except Exception:
    68+
    >>> client.report(message="Something went wrong", service="login_service")

    docs/index.rst

    Lines changed: 8 additions & 0 deletions
    Original file line numberDiff line numberDiff line change
    @@ -111,6 +111,14 @@
    111111
    logging-metric
    112112
    logging-sink
    113113

    114+
    .. toctree::
    115+
    :maxdepth: 0
    116+
    :hidden:
    117+
    :caption: Stackdriver Error Reporting
    118+
    119+
    error-reporting-usage
    120+
    Client <error-reporting-client>
    121+
    114122
    .. toctree::
    115123
    :maxdepth: 0
    116124
    :hidden:

    gcloud/error_reporting/__init__.py

    Lines changed: 18 additions & 0 deletions
    Original file line numberDiff line numberDiff line change
    @@ -0,0 +1,18 @@
    1+
    #!/usr/bin/env python
    2+
    # Copyright 2016 Google Inc. All Rights Reserved.
    3+
    #
    4+
    # Licensed under the Apache License, Version 2.0 (the "License");
    5+
    # you may not use this file except in compliance with the License.
    6+
    # You may obtain a copy of the License at
    7+
    #
    8+
    # http://www.apache.org/licenses/LICENSE-2.0
    9+
    #
    10+
    # Unless required by applicable law or agreed to in writing, software
    11+
    # distributed under the License is distributed on an "AS IS" BASIS,
    12+
    # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13+
    # See the License for the specific language governing permissions and
    14+
    # limitations under the License.
    15+
    16+
    """Client library for Stackdriver Error Reporting"""
    17+
    18+
    from gcloud.error_reporting.client import Client

    gcloud/error_reporting/client.py

    Lines changed: 142 additions & 0 deletions
    Original file line numberDiff line numberDiff line change
    @@ -0,0 +1,142 @@
    1+
    #!/usr/bin/env python
    2+
    # Copyright 2016 Google Inc. All Rights Reserved.
    3+
    #
    4+
    # Licensed under the Apache License, Version 2.0 (the "License");
    5+
    # you may not use this file except in compliance with the License.
    6+
    # You may obtain a copy of the License at
    7+
    #
    8+
    # http://www.apache.org/licenses/LICENSE-2.0
    9+
    #
    10+
    # Unless required by applicable law or agreed to in writing, software
    11+
    # distributed under the License is distributed on an "AS IS" BASIS,
    12+
    # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13+
    # See the License for the specific language governing permissions and
    14+
    # limitations under the License.
    15+
    16+
    """Client for interacting with the Stackdriver Logging API"""
    17+
    18+
    import traceback
    19+
    20+
    import gcloud.logging.client
    21+
    22+
    23+
    class Client(object):
    24+
    """Error Reporting client. Currently Error Reporting is done by creating
    25+
    a Logging client.
    26+
    27+
    :type project: string
    28+
    :param project: the project which the client acts on behalf of. If not
    29+
    passed falls back to the default inferred from the
    30+
    environment.
    31+
    32+
    :type credentials: :class:`oauth2client.client.OAuth2Credentials` or
    33+
    :class:`NoneType`
    34+
    :param credentials: The OAuth2 Credentials to use for the connection
    35+
    owned by this client. If not passed (and if no ``http``
    36+
    object is passed), falls back to the default inferred
    37+
    from the environment.
    38+
    39+
    :type http: :class:`httplib2.Http` or class that defines ``request()``.
    40+
    :param http: An optional HTTP object to make requests. If not passed, an
    41+
    ``http`` object is created that is bound to the
    42+
    ``credentials`` for the current object.
    43+
    44+
    :type service: str
    45+
    :param service: An identifier of the service, such as the name of the
    46< 3DA5 span class="diff-text-marker">+
    executable, job, or Google App Engine service name. This
    47+
    field is expected to have a low number of values that are
    48+
    relatively stable over time, as opposed to version,
    49+
    which can be changed whenever new code is deployed.
    50+
    51+
    52+
    :type version: str
    53+
    :param version: Represents the source code version that the developer
    54+
    provided, which could represent a version label or a Git
    55+
    SHA-1 hash, for example. If the developer did not provide
    56+
    a version, the value is set to default.
    57+
    58+
    :raises: :class:`ValueError` if the project is neither passed in nor
    59+
    set in the environment.
    60+
    """
    61+
    62+
    def __init__(self, project=None,
    63+
    credentials=None,
    64+
    http=None,
    65+
    service=None,
    66+
    version=None):
    67+
    self.logging_client = gcloud.logging.client.Client(
    68+
    project, credentials, http)
    69+
    self.service = service
    70+
    self.version = version
    71+
    72+
    DEFAULT_SERVICE = 'python'
    73+
    74+
    def _get_default_service(self):
    75+
    """Returns the service to use on method calls that don't specify an
    76+
    override.
    77+
    78+
    :rtype: string
    79+
    :returns: The default service for error reporting calls
    80+
    """
    81+
    if self.service:
    82+
    return self.service
    83+
    else:
    84+
    return self.DEFAULT_SERVICE
    85+
    86+
    def _get_default_version(self):
    87+
    """Returns the service to use on method calls that don't specify an
    88+
    override.
    89+
    90+
    :rtype: string
    91+
    :returns: The default version for error reporting calls.
    92+
    """
    93+
    if self.version:
    94+
    return self.version
    95+
    96+
    def report(self, message="", service=None, version=None):
    97+
    """ Reports the details of the latest exceptions to Stackdriver Error
    98+
    Reporting.
    99+
    100+
    https://cloud.google.com/error-reporting/docs/formatting-error-messages
    101+
    102+
    :type message: str
    103+
    :param message: An optional message to include with the exception
    104+
    detail
    105+
    106+
    :type service: str
    107+
    :param service: An identifier of the service, such as the name of
    108+
    the executable, job, or Google App Engine service
    109+
    name. This field is expected to have a low number
    110+
    of values that are relatively stable over time,
    111+
    as opposed to version, which can be changed
    112+
    whenever new code is deployed.
    113+
    114+
    :type version: str
    115+
    :param version: Represents the source code version that the
    116+
    developer provided, which could represent a
    117+
    version label or a Git SHA-1 hash, for example. If
    118+
    the developer did not provide a version, the value
    119+
    is set to default.
    120+
    121+
    122+
    Example::
    123+
    124+
    >>> try:
    125+
    >>> raise NameError
    126+
    >>> except Exception:
    127+
    >>> client.report("Something went wrong!")
    128+
    """
    129+
    if not service:
    130+
    service = self._get_default_service()
    131+
    if not version:
    132+
    version = self._get_default_version()
    133+
    payload = {
    134+
    'serviceContext': {
    135+
    'service': service,
    136+
    },
    137+
    'message': '{0} : {1}'.format(message, traceback.format_exc())
    138+
    }
    139+
    if < 3DA5 span class=pl-s1>version:
    140+
    payload['serviceContext']['version'] = version
    141+
    logger = self.logging_client.logger('errors')
    142+
    logger.log_struct(payload)
    Lines changed: 145 additions & 0 deletions
    Original file line numberDiff line numberDiff line change
    @@ -0,0 +1,145 @@
    1+
    #!/usr/bin/env python
    2+
    # Copyright 2016 Google Inc. All Rights Reserved.
    3+
    #
    4+
    # Licensed under the Apache License, Version 2.0 (the "License");
    5+
    # you may not use this file except in compliance with the License.
    6+
    # You may obtain a copy of the License at
    7+
    #
    8+
    # http://www.apache.org/licenses/LICENSE-2.0
    9+
    #
    10+
    # Unless required by applicable law or agreed to in writing, software
    11+
    # distributed under the License is distributed on an "AS IS" BASIS,
    12+
    # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13+
    # See the License for the specific language governing permissions and
    14+
    # limitations under the License.
    15+
    16+
    17+
    import unittest2
    18+
    19+
    20+
    class TestClient(unittest2.TestCase):
    21+
    22+
    def _getTargetClass(self):
    23+
    from gcloud.error_reporting.client import Client
    24+
    return Client
    25+
    26+
    def _makeOne(self, *args, **kw):
    27+
    return self._getTargetClass()(*args, **kw)
    28+
    29+
    PROJECT = 'PROJECT'
    30+
    SERVICE = 'SERVICE'
    31+
    VERSION = 'myversion'
    32+
    33+
    def test_ctor_default(self):
    34+
    CREDENTIALS = _Credentials()
    35+
    target = self._makeOne(project=self.PROJECT,
    36+
    credentials=CREDENTIALS)
    37+
    self.assertEquals(target._get_default_service(),
    38+
    target.DEFAULT_SERVICE)
    39+
    40+
    def test_ctor_params(self):
    41+
    CREDENTIALS = _Credentials()
    42+
    target = self._makeOne(project=self.PROJECT,
    43+
    credentials=CREDENTIALS,
    44+
    service=self.SERVICE,
    45+
    version=self.VERSION)
    46+
    self.assertEquals(target.service, self.SERVICE)
    47+
    self.assertEquals(target._get_default_service(), self.SERVICE)
    48+
    49+
    def test_report(self):
    50+
    CREDENTIALS = _Credentials()
    51+
    target = self._makeOne(project=self.PROJECT,
    52+
    credentials=CREDENTIALS)
    53+
    MESSAGE = 'hello world'
    54+
    55+
    logger = _Logger()
    56+
    target.logging_client.logger = lambda _: logger
    57+
    58+
    try:
    59+
    raise NameError
    60+
    except NameError:
    61+
    target.report(MESSAGE)
    62+
    63+
    payload = logger.log_struct_called_with
    64+
    self.assertEquals(payload['serviceContext'], {
    65+
    'service': target.DEFAULT_SERVICE,
    66+
    })
    67+
    self.assertIn(MESSAGE, payload['message'])
    68+
    self.assertIn('test_report', payload['message'])
    69+
    self.assertIn('test_client.py', payload['message'])
    70+
    71+
    def test_report_specify_service(self):
    72+
    CREDENTIALS = _Credentials()
    73+
    target = self._makeOne(project=self.PROJECT,
    74+
    credentials=CREDENTIALS)
    75+
    MESSAGE = 'hello world'
    76+
    SERVICE = "notdefault"
    77+
    VERSION = "notdefaultversion"
    78+
    79+
    logger = _Logger()
    80+
    target.logging_client.logger = lambda _: logger
    81+
    82+
    try:
    83+
    raise NameError
    84+
    except NameError:
    85+
    target.report(MESSAGE, service=SERVICE, version=VERSION)
    86+
    87+
    payload = logger.log_struct_called_with
    88+
    self.assertEquals(payload['serviceContext'], {
    89+
    'service': SERVICE,
    90+
    'version': VERSION
    91+
    })
    92+
    self.assertIn(MESSAGE, payload['message'])
    93+
    self.assertIn('test_report_specify_service', payload['message'])
    94+
    self.assertIn('test_client.py', payload['message'])
    95+
    96+
    def test_report_with_version_in_constructor(self):
    97+
    CREDENTIALS = _Credentials()
    98+
    VERSION = "notdefaultversion"
    99+
    target = self._makeOne(project=self.PROJECT,
    100+
    credentials=CREDENTIALS,
    101+
    version=VERSION)
    102+
    MESSAGE = 'hello world'
    103+
    SERVICE = "notdefault"
    104+
    105+
    logger = _Logger()
    106+
    target.logging_client.logger = lambda _: logger
    107+
    108+
    try:
    109+
    raise NameError
    110+
    except NameError:
    111+
    target.report(MESSAGE, service=SERVICE)
    112+
    113+
    payload = logger.log_struct_called_with
    114 9E0D +
    self.assertEquals(payload['serviceContext'], {
    115+
    'service': SERVICE,
    116+
    'version': VERSION
    117+
    })
    118+
    self.assertIn(MESSAGE, payload['message'])
    119+
    self.assertIn(
    120+
    'test_report_with_version_in_constructor', payload['message'])
    121+
    self.assertIn('test_client.py', payload['message'])
    122+
    123+
    124+
    class _Credentials(object):
    125+
    126+
    _scopes = None
    127+
    128+
    @staticmethod
    129+
    def create_scoped_required():
    130+
    return True
    131+
    132+
    def create_scoped(self, scope):
    133+
    self._scopes = scope
    134+
    return self
    135+
    136+
    137+
    class _Logger(object):
    138+
    139+
    def log_struct(self, payload, # pylint: disable=unused-argument
    140+
    client=None, # pylint: disable=unused-argument
    141+
    labels=None, # pylint: disable=unused-argument
    142+
    insert_id=None, # pylint: disable=unused-argument
    143+
    severity=None, # pylint: disable=unused-argument
    144+
    http_request=None): # pylint: disable=unused-argument
    145+
    self.log_struct_called_with = payload

    0 commit comments

    Comments
     (0)
    0