# Path: google/cloud/logging_v2/handlers/handlers.py
# class CloudLoggingFilter(logging.Filter):
#     """Python standard ``logging`` Filter class to add Cloud Logging
#     information to each LogRecord.
# 
#     When attached to a LogHandler, each incoming log will be modified
#     to include new Cloud Logging relevant data. This data can be manually
#     overwritten using the `extras` argument when writing logs.
#     """
# 
#     def __init__(self, project=None, default_labels=None):
#         self.project = project
#         self.default_labels = default_labels if default_labels else {}
# 
#     @staticmethod
#     def _infer_source_location(record):
#         """Helper function to infer source location data from a LogRecord.
#         Will default to record.source_location if already set
#         """
#         if hasattr(record, "source_location"):
#             return record.source_location
#         else:
#             name_map = [
#                 ("line", "lineno"),
#                 ("file", "pathname"),
#                 ("function", "funcName"),
#             ]
#             output = {}
#             for (gcp_name, std_lib_name) in name_map:
#                 value = getattr(record, std_lib_name, None)
#                 if value is not None:
#                     output[gcp_name] = value
#             return output if output else None
# 
#     def filter(self, record):
#         """
#         Add new Cloud Logging data to each LogRecord as it comes in
#         """
#         user_labels = getattr(record, "labels", {})
#         # infer request data from the environment
#         (
#             inferred_http,
#             inferred_trace,
#             inferred_span,
#             inferred_sampled,
#         ) = get_request_data()
#         if inferred_trace is not None and self.project is not None:
#             # add full path for detected trace
#             inferred_trace = f"projects/{self.project}/traces/{inferred_trace}"
#         # set new record values
#         record._resource = getattr(record, "resource", None)
#         record._trace = getattr(record, "trace", inferred_trace) or None
#         record._span_id = getattr(record, "span_id", inferred_span) or None
#         record._trace_sampled = bool(getattr(record, "trace_sampled", inferred_sampled))
#         record._http_request = getattr(record, "http_request", inferred_http)
#         record._source_location = CloudLoggingFilter._infer_source_location(record)
#         # add logger name as a label if possible
#         logger_label = {"python_logger": record.name} if record.name else {}
#         record._labels = {**logger_label, **self.default_labels, **user_labels} or None
#         # create string representations for structured logging
#         record._trace_str = record._trace or ""
#         record._span_id_str = record._span_id or ""
#         record._trace_sampled_str = "true" if record._trace_sampled else "false"
#         record._http_request_str = json.dumps(
#             record._http_request or {}, ensure_ascii=False
#         )
#         record._source_location_str = json.dumps(
#             record._source_location or {}, ensure_ascii=False
#         )
#         record._labels_str = json.dumps(record._labels or {}, ensure_ascii=False)
#         return True
# 
# Path: google/cloud/logging_v2/handlers/handlers.py
# def _format_and_parse_message(record, formatter_handler):
#     """
#     Helper function to apply formatting to a LogRecord message,
#     and attempt to parse encoded JSON into a dictionary object.
# 
#     Resulting output will be of type (str | dict | None)
# 
#     Args:
#         record (logging.LogRecord): The record object representing the log
#         formatter_handler (logging.Handler): The handler used to format the log
#     """
#     passed_json_fields = getattr(record, "json_fields", {})
#     # if message is a dictionary, use dictionary directly
#     if isinstance(record.msg, collections.abc.Mapping):
#         payload = record.msg
#         # attach any extra json fields if present
#         if passed_json_fields and isinstance(
#             passed_json_fields, collections.abc.Mapping
#         ):
#             payload = {**payload, **passed_json_fields}
#         return payload
#     # format message string based on superclass
#     message = formatter_handler.format(record)
#     try:
#         # attempt to parse encoded json into dictionary
#         if message[0] == "{":
#             json_message = json.loads(message)
#             if isinstance(json_message, collections.abc.Mapping):
#                 message = json_message
#     except (json.decoder.JSONDecodeError, IndexError):
#         # log string is not valid json
#         pass
#     # if json_fields was set, create a dictionary using that
#     if passed_json_fields and isinstance(passed_json_fields, collections.abc.Mapping):
#         if message != "None":
#             passed_json_fields["message"] = message
#         return passed_json_fields
#     # if formatted message contains no content, return None
#     return message if message != "None" else None


import collections
import json
import logging.handlers
from google.cloud.logging_v2.handlers.handlers import CloudLoggingFilter
from google.cloud.logging_v2.handlers.handlers import _format_and_parse_message

GCP_FORMAT = (
    "{%(_payload_str)s"
    '"severity": "%(levelname)s", '
    '"logging.googleapis.com/labels": %(_labels_str)s, '
    '"logging.googleapis.com/trace": "%(_trace_str)s", '
    '"logging.googleapis.com/spanId": "%(_span_id_str)s", '
    '"logging.googleapis.com/trace_sampled": %(_trace_sampled_str)s, '
    '"logging.googleapis.com/sourceLocation": %(_source_location_str)s, '
    '"httpRequest": %(_http_request_str)s '
    "}"
)


class StructuredLogHandler(logging.StreamHandler):
    """Handler to format logs into the Cloud Logging structured log format,
    and write them to standard output
    """

    def __init__(self, *, labels=None, stream=None, project_id=None):
        """
        Args:
            labels (Optional[dict]): Additional labels to attach to logs.
            stream (Optional[IO]): Stream to be used by the handler.
            project (Optional[str]): Project Id associated with the logs.
        """
        super(StructuredLogHandler, self).__init__(stream=stream)
        self.project_id = project_id

        # add extra keys to log record

# Path: google/cloud/logging_v2/handlers/handlers.py
# class CloudLoggingFilter(logging.Filter):
#     """Python standard ``logging`` Filter class to add Cloud Logging
#     information to each LogRecord.
# 
#     When attached to a LogHandler, each incoming log will be modified
#     to include new Cloud Logging relevant data. This data can be manually
#     overwritten using the `extras` argument when writing logs.
#     """
# 
#     def __init__(self, project=None, default_labels=None):
#         self.project = project
#         self.default_labels = default_labels if default_labels else {}
# 
#     @staticmethod
#     def _infer_source_location(record):
#         """Helper function to infer source location data from a LogRecord.
#         Will default to record.source_location if already set
#         """
#         if hasattr(record, "source_location"):
#             return record.source_location
#         else:
#             name_map = [
#                 ("line", "lineno"),
#                 ("file", "pathname"),
#                 ("function", "funcName"),
#             ]
#             output = {}
#             for (gcp_name, std_lib_name) in name_map:
#                 value = getattr(record, std_lib_name, None)
#                 if value is not None:
#                     output[gcp_name] = value
#             return output if output else None
# 
#     def filter(self, record):
#         """
#         Add new Cloud Logging data to each LogRecord as it comes in
#         """
#         user_labels = getattr(record, "labels", {})
#         # infer request data from the environment
#         (
#             inferred_http,
#             inferred_trace,
#             inferred_span,
#             inferred_sampled,
#         ) = get_request_data()
#         if inferred_trace is not None and self.project is not None:
#             # add full path for detected trace
#             inferred_trace = f"projects/{self.project}/traces/{inferred_trace}"
#         # set new record values
#         record._resource = getattr(record, "resource", None)
#         record._trace = getattr(record, "trace", inferred_trace) or None
#         record._span_id = getattr(record, "span_id", inferred_span) or None
#         record._trace_sampled = bool(getattr(record, "trace_sampled", inferred_sampled))
#         record._http_request = getattr(record, "http_request", inferred_http)
#         record._source_location = CloudLoggingFilter._infer_source_location(record)
#         # add logger name as a label if possible
#         logger_label = {"python_logger": record.name} if record.name else {}
#         record._labels = {**logger_label, **self.default_labels, **user_labels} or None
#         # create string representations for structured logging
#         record._trace_str = record._trace or ""
#         record._span_id_str = record._span_id or ""
#         record._trace_sampled_str = "true" if record._trace_sampled else "false"
#         record._http_request_str = json.dumps(
#             record._http_request or {}, ensure_ascii=False
#         )
#         record._source_location_str = json.dumps(
#             record._source_location or {}, ensure_ascii=False
#         )
#         record._labels_str = json.dumps(record._labels or {}, ensure_ascii=False)
#         return True
# 
# Path: google/cloud/logging_v2/handlers/handlers.py
# def _format_and_parse_message(record, formatter_handler):
#     """
#     Helper function to apply formatting to a LogRecord message,
#     and attempt to parse encoded JSON into a dictionary object.
# 
#     Resulting output will be of type (str | dict | None)
# 
#     Args:
#         record (logging.LogRecord): The record object representing the log
#         formatter_handler (logging.Handler): The handler used to format the log
#     """
#     passed_json_fields = getattr(record, "json_fields", {})
#     # if message is a dictionary, use dictionary directly
#     if isinstance(record.msg, collections.abc.Mapping):
#         payload = record.msg
#         # attach any extra json fields if present
#         if passed_json_fields and isinstance(
#             passed_json_fields, collections.abc.Mapping
#         ):
#             payload = {**payload, **passed_json_fields}
#         return payload
#     # format message string based on superclass
#     message = formatter_handler.format(record)
#     try:
#         # attempt to parse encoded json into dictionary
#         if message[0] == "{":
#             json_message = json.loads(message)
#             if isinstance(json_message, collections.abc.Mapping):
#                 message = json_message
#     except (json.decoder.JSONDecodeError, IndexError):
#         # log string is not valid json
#         pass
#     # if json_fields was set, create a dictionary using that
#     if passed_json_fields and isinstance(passed_json_fields, collections.abc.Mapping):
#         if message != "None":
#             passed_json_fields["message"] = message
#         return passed_json_fields
#     # if formatted message contains no content, return None
#     return message if message != "None" else None


# Path: google/cloud/logging_v2/handlers/structured_log.py
import collections
import json
import logging.handlers
from google.cloud.logging_v2.handlers.handlers import CloudLoggingFilter
from google.cloud.logging_v2.handlers.handlers import _format_and_parse_message


GCP_FORMAT = (
    "{%(_payload_str)s"
    '"severity": "%(levelname)s", '
    '"logging.googleapis.com/labels": %(_labels_str)s, '
    '"logging.googleapis.com/trace": "%(_trace_str)s", '
    '"logging.googleapis.com/spanId": "%(_span_id_str)s", '
    '"logging.googleapis.com/trace_sampled": %(_trace_sampled_str)s, '
    '"logging.googleapis.com/sourceLocation": %(_source_location_str)s, '
    '"httpRequest": %(_http_request_str)s '
    "}"
)


class StructuredLogHandler(logging.StreamHandler):
    """Handler to format logs into the Cloud Logging structured log format,
    and write them to standard output
    """

    def __init__(self, *, labels=None, stream=None, project_id=None):
        """
        Args:
            labels (Optional[dict]): Additional labels to attach to logs.
            stream (Optional[IO]): Stream to be used by the handler.
            project (Optional[str]): Project Id associated with the logs.
        """
        super(StructuredLogHandler, self).__init__(stream=stream)
        self.project_id = project_id

        # add extra keys to log record

        log_filter = CloudLoggingFilter(project=project_id, default_labels=labels)