|
| 1 | +# Copyright 2016 Google Inc. All rights reserved. |
| 2 | +# |
| 3 | +# Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | +# you may not use this file except in compliance with the License. |
| 5 | +# You may obtain a copy of the License at |
| 6 | +# |
| 7 | +# http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | +# |
| 9 | +# Unless required by applicable law or agreed to in writing, software |
| 10 | +# distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | +# See the License for the specific language governing permissions and |
| 13 | +# limitations under the License. |
| 14 | + |
| 15 | +"""Python :mod:`logging` handlers for Google Cloud Logging.""" |
| 16 | + |
| 17 | +import logging |
| 18 | + |
| 19 | +from gcloud.logging.handlers.transports import BackgroundThreadTransport |
| 20 | + |
| 21 | + |
| 22 | +EXCLUDE_LOGGER_DEFAULTS = ( |
| 23 | + 'gcloud', |
| 24 | + 'oauth2client' |
| 25 | +) |
| 26 | + |
| 27 | +DEFAULT_LOGGER_NAME = 'python' |
| 28 | + |
| 29 | + |
| 30 | +class CloudLoggingHandler(logging.StreamHandler): |
| 31 | + """Python standard ``logging`` handler. |
| 32 | +
|
| 33 | + This handler can be used to route Python standard logging messages |
| 34 | + directly to the Google Cloud Logging API. |
| 35 | +
|
| 36 | + Note that this handler currently only supports a synchronous API call, |
| 37 | + which means each logging statement that uses this handler will require |
| 38 | + an API call. |
| 39 | +
|
| 40 | + :type client: :class:`gcloud.logging.client` |
| 41 | + :param client: the authenticated gcloud logging client for this handler |
| 42 | + to use |
| 43 | +
|
| 44 | + :type name: str |
| 45 | + :param name: the name of the custom log in Stackdriver Logging. Defaults |
| 46 | + to 'python'. The name of the Python logger will be represented |
| 47 | + in the ``python_logger`` field. |
| 48 | +
|
| 49 | + :type transport: type |
| 50 | + :param transport: Class for creating new transport objects. It should |
| 51 | + extend from the base :class:`.Transport` type and |
| 52 | + implement :meth`.Transport.send`. Defaults to |
| 53 | + :class:`.BackgroundThreadTransport`. The other |
| 54 | + option is :class:`.SyncTransport`. |
| 55 | +
|
| 56 | + Example: |
| 57 | +
|
| 58 | + .. doctest:: |
| 59 | +
|
| 60 | + import gcloud.logging |
| 61 | + from gcloud.logging.handlers import CloudLoggingHandler |
| 62 | +
|
| 63 | + client = gcloud.logging.Client() |
| 64 | + handler = CloudLoggingHandler(client) |
| 65 | +
|
| 66 | + cloud_logger = logging.getLogger('cloudLogger') |
| 67 | + cloud_logger.setLevel(logging.INFO) |
| 68 | + cloud_logger.addHandler(handler) |
| 69 | +
|
| 70 | + cloud.logger.error('bad news') # API call |
| 71 | +
|
| 72 | + """ |
| 73 | + |
| 74 | + def __init__(self, client, |
| 75 | + name=DEFAULT_LOGGER_NAME, |
| 76 | + transport=BackgroundThreadTransport): |
| 77 | + super(CloudLoggingHandler, self).__init__() |
| 78 | + self.name = name |
| 79 | + self.client = client |
| 80 | + self.transport = transport(client, name) |
| 81 | + |
| 82 | + def emit(self, record): |
| 83 | + """Actually log the specified logging record. |
| 84 | +
|
| 85 | + Overrides the default emit behavior of ``StreamHandler``. |
| 86 | +
|
| 87 | + See: https://docs.python.org/2/library/logging.html#handler-objects |
| 88 | +
|
| 89 | + :type record: :class:`logging.LogRecord` |
| 90 | + :param record: The record to be logged. |
| 91 | + """ |
| 92 | + message = super(CloudLoggingHandler, self).format(record) |
| 93 | + self.transport.send(record, message) |
| 94 | + |
| 95 | + |
| 96 | +def setup_logging(handler, excluded_loggers=EXCLUDE_LOGGER_DEFAULTS): |
| 97 | + """Attach the ``CloudLogging`` handler to the Python root logger |
| 98 | +
|
| 99 | + Excludes loggers that this library itself uses to avoid |
| 100 | + infinite recursion. |
| 101 | +
|
| 102 | + :type handler: :class:`logging.handler` |
| 103 | + :param handler: the handler to attach to the global handler |
| 104 | +
|
| 105 | + :type excluded_loggers: tuple |
| 106 | + :param excluded_loggers: The loggers to not attach the handler to. This |
| 107 | + will always include the loggers in the path of |
| 108 | + the logging client itself. |
| 109 | +
|
| 110 | + Example: |
| 111 | +
|
| 112 | + .. doctest:: |
| 113 | +
|
| 114 | + import logging |
| 115 | + import gcloud.logging |
| 116 | + from gcloud.logging.handlers import CloudLoggingHandler |
| 117 | +
|
| 118 | + client = gcloud.logging.Client() |
| 119 | + handler = CloudLoggingHandler(client) |
| 120 | + gcloud.logging.setup_logging(handler) |
| 121 | + logging.getLogger().setLevel(logging.DEBUG) |
| 122 | +
|
| 123 | + logging.error('bad news') # API call |
| 124 | +
|
| 125 | + """ |
| 126 | + all_excluded_loggers = set(excluded_loggers + EXCLUDE_LOGGER_DEFAULTS) |
| 127 | + logger = logging.getLogger() |
| 128 | + logger.addHandler(handler) |
| 129 | + logger.addHandler(logging.StreamHandler()) |
| 130 | + for logger_name in all_excluded_loggers: |
| 131 | + logger = logging.getLogger(logger_name) |
| 132 | + logger.propagate = False |
| 133 | + logger.addHandler(logging.StreamHandler()) |
0 commit comments