1# Copyright 2016 Google LLC 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"""Logging handler for App Engine Flexible 
    16 
    17Sends logs to the Cloud Logging API with the appropriate resource 
    18and labels for App Engine logs. 
    19""" 
    20 
    21import logging 
    22import os 
    23import warnings 
    24 
    25from google.cloud.logging_v2.handlers._helpers import get_request_data 
    26from google.cloud.logging_v2.handlers._monitored_resources import ( 
    27    _create_app_engine_resource, 
    28) 
    29from google.cloud.logging_v2.handlers.transports import BackgroundThreadTransport 
    30 
    31_DEFAULT_GAE_LOGGER_NAME = "app" 
    32 
    33_GAE_PROJECT_ENV_FLEX = "GCLOUD_PROJECT" 
    34_GAE_PROJECT_ENV_STANDARD = "GOOGLE_CLOUD_PROJECT" 
    35_GAE_SERVICE_ENV = "GAE_SERVICE" 
    36_GAE_VERSION_ENV = "GAE_VERSION" 
    37 
    38_TRACE_ID_LABEL = "appengine.googleapis.com/trace_id" 
    39 
    40_DEPRECATION_MSG = "AppEngineHandler is deprecated. Use CloudLoggingHandler instead." 
    41 
    42 
    43class AppEngineHandler(logging.StreamHandler): 
    44    """A logging handler that sends App Engine-formatted logs to Stackdriver. 
    45 
    46    DEPRECATED:  use CloudLoggingHandler instead. 
    47    """ 
    48 
    49    def __init__( 
    50        self, 
    51        client, 
    52        *, 
    53        name=_DEFAULT_GAE_LOGGER_NAME, 
    54        transport=BackgroundThreadTransport, 
    55        stream=None, 
    56    ): 
    57        """ 
    58        Args: 
    59            client (~logging_v2.client.Client): The authenticated 
    60                Google Cloud Logging client for this handler to use. 
    61            name (Optional[str]): Name for the logger. 
    62            transport (Optional[~logging_v2.transports.Transport]): 
    63                The transport class. It should be a subclass 
    64                of :class:`.Transport`. If unspecified, 
    65                :class:`.BackgroundThreadTransport` will be used. 
    66            stream (Optional[IO]): Stream to be used by the handler. 
    67 
    68        """ 
    69        super(AppEngineHandler, self).__init__(stream) 
    70        self.name = name 
    71        self.client = client 
    72        self.transport = transport(client, name) 
    73        self.project_id = os.environ.get( 
    74            _GAE_PROJECT_ENV_FLEX, os.environ.get(_GAE_PROJECT_ENV_STANDARD, "") 
    75        ) 
    76        self.module_id = os.environ.get(_GAE_SERVICE_ENV, "") 
    77        self.version_id = os.environ.get(_GAE_VERSION_ENV, "") 
    78        self.resource = self.get_gae_resource() 
    79 
    80        warnings.warn(_DEPRECATION_MSG, DeprecationWarning) 
    81 
    82    def get_gae_resource(self): 
    83        """Return the GAE resource using the environment variables. 
    84 
    85        Returns: 
    86            google.cloud.logging_v2.resource.Resource: Monitored resource for GAE. 
    87        """ 
    88        return _create_app_engine_resource() 
    89 
    90    def get_gae_labels(self): 
    91        """Return the labels for GAE app. 
    92 
    93        If the trace ID can be detected, it will be included as a label. 
    94        Currently, no other labels are included. 
    95 
    96        Returns: 
    97            dict: Labels for GAE app. 
    98        """ 
    99        gae_labels = {} 
    100 
    101        _, trace_id, _, _ = get_request_data() 
    102        if trace_id is not None: 
    103            gae_labels[_TRACE_ID_LABEL] = trace_id 
    104 
    105        return gae_labels 
    106 
    107    def emit(self, record): 
    108        """Actually log the specified logging record. 
    109 
    110        Overrides the default emit behavior of ``StreamHandler``. 
    111 
    112        See https://docs.python.org/2/library/logging.html#handler-objects 
    113 
    114        Args: 
    115            record (logging.LogRecord): The record to be logged. 
    116        """ 
    117        message = super(AppEngineHandler, self).format(record) 
    118        inferred_http, inferred_trace, _, _ = get_request_data() 
    119        if inferred_trace is not None: 
    120            inferred_trace = f"projects/{self.project_id}/traces/{inferred_trace}" 
    121        # allow user overrides 
    122        trace = getattr(record, "trace", inferred_trace) 
    123        span_id = getattr(record, "span_id", None) 
    124        http_request = getattr(record, "http_request", inferred_http) 
    125        resource = getattr(record, "resource", self.resource) 
    126        user_labels = getattr(record, "labels", {}) 
    127        # merge labels 
    128        gae_labels = self.get_gae_labels() 
    129        gae_labels.update(user_labels) 
    130        # send off request 
    131        self.transport.send( 
    132            record, 
    133            message, 
    134            resource=resource, 
    135            labels=gae_labels, 
    136            trace=trace, 
    137            span_id=span_id, 
    138            http_request=http_request, 
    139        )