| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
There was a problem hiding this comment.
Pretty awesome stuff!
Sorry, something went wrong.
| @@ -0,0 +1,6 @@ | |||
| Google App Engine Flexible Log Handler | |||
| ======================================= | |||
| @@ -0,0 +1,6 @@ | |||
| Google Container Engine Log Handler | |||
| ======================================= | |||
| Logging client. | ||
| It's possible to tie the Python :mod:`logging` module directly into Google Stackdriver Logging. | ||
| There are different handler options to accomplish this. To automatically pick the default, | ||
| use :meth:``google.cloud.logging.client.get_default_handler`. |
| >>> cloud_logger.error('bad news') | ||
|
|
||
| It is also possible to attach the handler to the root Python logger, so that for example a plain | ||
| `logging.warn` call would be sent to Cloud Logging, as well as any other loggers created. However, |
| method :meth:`setup_logging <google.cloud.logging.handlers.setup_logging>` is provided to configure | ||
| this automatically: | ||
| CloudLoggingHandler | ||
| ======================= |
| """Python :mod:`logging` handlers for Google Cloud Logging.""" | ||
|
|
||
| from google.cloud.logging.handlers.handlers import CloudLoggingHandler | ||
| from google.cloud.logging.handlers.GAEHandler import GAEHandler |
| :param excluded_loggers: The loggers to not attach the handler to. This | ||
| will always include the loggers in the path of | ||
| the logging client itself. | ||
| :param excluded_loggers: (Optional)The loggers to not attach the handler |
| to. This will always include the loggers in the | ||
| path of the logging client itself. | ||
|
|
||
| :type log_level: :mod:`logging` log level |
| class _Client(object): | ||
|
|
||
| def __init__(self, project): | ||
| def __init__(self, project, _=None, __=None): |
| return self | ||
|
|
||
| def authorize(self, http): | ||
| pass |
| Logs to the well-known file that the fluentd sidecar container on App Engine | ||
| Flexible is configured to read from and send to Stackdriver Logging. | ||
|
|
||
| This file is largely copied from: |
| import math | ||
| import os | ||
|
|
||
| LOG_PATH_TEMPLATE = '/var/log/app_engine/app.{pid}.json' |
| LOG_FILE_COUNT = 3 | ||
|
|
||
|
|
||
| class GAEHandler(logging.handlers.RotatingFileHandler): |
|
|
||
|
|
||
| class GAEHandler(logging.handlers.RotatingFileHandler): | ||
| """A handler that writes to the GAE fluentd Stackdriver logging file. |
| class GAEHandler(logging.handlers.RotatingFileHandler): | ||
| """A handler that writes to the GAE fluentd Stackdriver logging file. | ||
|
|
||
| Writes to the file that the fluentd agent on App Engine Flexbile is |
| target=self._run, | ||
| name='google.cloud.logging.handlers.transport.Worker') | ||
| thread_name = 'google.cloud.logging.handlers.transport.Worker' | ||
| self._thread = threading.Thread(target=self._run, |
| LOGNAME = 'loggername' | ||
| MESSAGE = 'hello world' | ||
| record = _Record(LOGNAME, logging.INFO, MESSAGE) | ||
| expected_payload = {'message': MESSAGE, |
| self.assertEqual(payload, json.dumps(expected_payload)) | ||
|
|
||
|
|
||
| class _Record(object): |
| import logging | ||
| import json | ||
| handler = self._makeOne() | ||
| LOGNAME = 'loggername' |
| class _Client(object): | ||
|
|
||
| def __init__(self, project): | ||
| def __init__(self, project, _=None, __=None): |
| logging-sink | ||
| logging-handlers | ||
| logging-handlers-GAEHandler | ||
| logging-handlers-GKEHandler |
| Google App Engine Flexible Log Handler | ||
| ======================================= | ||
|
|
||
| .. automodule:: google.cloud.logging.handlers.GAEHandler |
| Google Container Engine Log Handler | ||
| ======================================= | ||
|
|
||
| .. automodule:: google.cloud.logging.handlers.GKEHandler |
| create a :class:`CloudLoggingHandler <google.cloud.logging.CloudLoggingHandler>` instance from your | ||
| Logging client. | ||
| It's possible to tie the Python :mod:`logging` module directly into Google Stackdriver Logging. | ||
| There are different handler options to accomplish this. To automatically pick the default, |
| Logging client. | ||
| It's possible to tie the Python :mod:`logging` module directly into Google Stackdriver Logging. | ||
| There are different handler options to accomplish this. To automatically pick the default, | ||
| use :meth:``google.cloud.logging.client.get_default_handler`. |
|
|
||
| client = self._makeOne(self.PROJECT, credentials=_Credentials()) | ||
|
|
||
| with _Monkey(os, getenv=lambda var: var == 'GAE_APPENGINE_HOSTNAME'): |
|
|
||
| self.assertIsInstance(handler, GKEHandler) | ||
|
|
||
| with _Monkey(os, getenv=lambda _: None): |
|
|
||
| self.assertIsInstance(handler, GAEHandler) | ||
|
|
||
| with _Monkey(os, getenv=lambda var: var == 'KUBERNETES_SERVICE'): |
| def test_setup_logging(self): | ||
| from google.cloud._testing import _Monkey | ||
| import google.cloud.logging.client as MUT | ||
| client = self._makeOne( |
| self.called = False | ||
|
|
||
| def __call__(self, _, log_level, | ||
| excluded_loggers=None): # pylintdisable=unused-variable |
|
Think I addressed all comments, let me make sure Travis is passing and then I'll do another once-over before I request another review. |
Sorry, something went wrong.
|
|
||
| Integration with Python logging module | ||
| --------------------------------------------- | ||
| --------------------------------------- |
| credentials=_Credentials(), | ||
| use_gax=False) | ||
| os.environ[_CONTAINER_ENGINE_ENV] = True | ||
| os.environ[_CONTAINER_ENGINE_ENV] = "True" |
| (i.e. your app.yaml contains ``runtime: python``), and | ||
| :class:`~google.cloud.handlers.container_engine.ContainerEngineHandler`, | ||
| which is | ||
| recommended when running on `Google Container Engine` with the Stackdriver |
| from google.cloud.logging.handlers import AppEngineHandler | ||
| from google.cloud.logging.handlers import ContainerEngineHandler | ||
| from google.cloud.logging.handlers import setup_logging | ||
| from google.cloud.logging.handlers.handlers import EXCLUDED_LOGGER_DEFAULTS |
| """ | ||
|
|
||
| # This file is largely copied from: | ||
| # https://github.com/GoogleCloudPlatform/python-compat-runtime/blob/master/appengine-vmruntime/vmruntime/cloud_logging.py |
| >>> setup_logging(handler, excluded_loggers=('werkzeug',))) | ||
| >>> handler = CloudLoggingHandler(client, name="mycustomlog") | ||
|
|
||
| fluentd logging handlers |
| the environment to automatically detect whether the code is running in | ||
| these platforms and use the appropriate handler. | ||
|
|
||
| In both cases, the fluentd agent is configured to automatically parse log files |
| method :meth:`setup_logging <google.cloud.logging.handlers.setup_logging>` is provided to configure | ||
| this automatically: | ||
| If you prefer not to use :meth:`google.cloud.logging.client | ||
| .get_default_handler`, you can directly create a |
| 'message': message, | ||
| 'timestamp': { | ||
| 'seconds': 5, | ||
| 'nanos': int(.03 * 1e9) |
| 'timestamp': {'seconds': 5, | ||
| 'nanos': int(.03 * 1e9)}, | ||
| 'thread': record.thread, | ||
| 'severity': record.levelname} |
| 'nanos': int(subsecond * 1e9) | ||
| }, | ||
| 'thread': record.thread, | ||
| 'severity': record.levelname |
| 'message': message, | ||
| 'timestamp': { | ||
| 'seconds': int(second), | ||
| 'nanos': int(subsecond * 1e9) |
| payload = { | ||
| 'message': message, | ||
| 'timestamp': {'seconds': int(second), | ||
| 'nanos': int(subsecond * 1e9)}, |
| class AppEngineHandler(logging.handlers.RotatingFileHandler): | ||
| """A handler that writes to the App Engine fluentd Stackdriver log file. | ||
|
|
||
| Writes to the file that the fluentd agent on App Engine Flexbile is |
| itself, and not as a formatting step, so as not to interfere with | ||
| user-defined logging formats. | ||
|
|
||
| Logging calls can also alternatively 'trace_id' in as a field in the |
| """Logging handler for Google Container Engine (GKE). | ||
|
|
||
| Formats log messages in a JSON format, so that Kubernetes clusters with the | ||
| fluentd google cloud plugin installed can format their log messages so that |
|
|
||
|
|
||
| class ContainerEngineHandler(logging.StreamHandler): | ||
| """Handler to formats log messages in good fluentd GKE format. |
| """Python :mod:`logging` handlers for Google Cloud Logging.""" | ||
|
|
||
| import logging | ||
|
|
| class TestContainerEngineHandler(unittest.TestCase): | ||
| PROJECT = 'PROJECT' | ||
|
|
||
| def _getTargetClass(self): |
| 'message': message, | ||
| 'timestamp': { | ||
| 'seconds': 5, | ||
| 'nanos': int(.03 * 1e9) |
| 'message': MESSAGE, | ||
| 'python_logger': PYTHON_LOGGER_NAME | ||
| 'message': message, | ||
| 'python_logger': python_logger_name |
| 'message': MESSAGE, | ||
| 'python_logger': PYTHON_LOGGER_NAME | ||
| 'message': message, | ||
| 'python_logger': python_logger_name |
| return self | ||
|
|
||
| def authorize(self, http): | ||
| pass |
| self._scopes = scope | ||
| return self | ||
|
|
||
| def authorize(self, _): |
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
|
|
||
| import mock |
| use_gax=False) | ||
| handler = client.get_default_handler() | ||
| self.assertIsInstance(handler, CloudLoggingHandler) | ||
| self.assertTrue(credentials.authorized, http_mock) |
| def test_get_default_handler_general(self, deepcopy): | ||
| from google.cloud.logging.handlers import CloudLoggingHandler | ||
|
|
||
| http_mock = mock.Mock() |
| from google.cloud._testing import _Monkey | ||
| import google.cloud.logging.client as MUT | ||
|
|
||
| http_mock = mock.Mock() |
|
|
||
| def test_setup_logging(self): | ||
| @mock.patch('copy.deepcopy') | ||
| def test_setup_logging(self, deepcopy): |
|
@waprin Pretty please squash (either in the GitHub UI or in your local copy). @jonparrott Do you want to do a final pass? |
Sorry, something went wrong.
On GAE and GKE with the plugin installed, there is a fluentd plugin that collects logs from files. However without the right formatting, metadata like log_level is lost. Furthermore, the fluentd agents are configured to set the correct resources types, which could be done in the main handler as well, but it’s easier to rely on the fluentd configurations. This adds two new handlers and some helper functions to detect when they should be used.
Add GAE and GKE fluentd Handlers
Add GAE and GKE fluentd Handlers
| Back | FazBrowse Home | New Git URL |
On GAE and GKE with the plugin installed, there is a fluentd plugin that collects logs from files. However without the right formatting, metadata like log_level is lost. Furthermore, the fluentd agents are configured to set the correct resources types, which could be done in the main handler as well, but it’s easier to rely on the fluentd configurations.
This adds two new handlers and some helper functions to detect when they should be used.