Делаем эквивалент log_struct в python logger

В примере Google это дает следующее:

logger.log_struct({
    'message': 'My second entry',
    'weather': 'partly cloudy',
})

Как бы я сделал эквивалент в регистраторе Python. Например:

import logging
log.info(
    msg='My second entry', 
    extra = {'weather': "partly cloudy"}
)

Когда я просматриваю это в стекдрайвере, дополнительные поля не анализируются должным образом:

2018-11-12 15:41:12.366 PST
My second entry

Expand all | Collapse all 

{
 insertId:  "1de1tqqft3x3ri"  
 jsonPayload: {
  message:  "My second entry"   
  python_logger:  "Xhdoo8x"   
 }
 logName:  "projects/Xhdoo8x/logs/python"  
 receiveTimestamp:  "2018-11-12T23:41:12.366883466Z"  
 resource: {…}  
 severity:  "INFO"  
 timestamp:  "2018-11-12T23:41:12.366883466Z"  
}

Как бы я это сделал?

Самое близкое, что я могу сделать сейчас:

log.handlers[-1].client.logger('').log_struct("...")

Но это все еще требует второго звонка...

3 ответа

Текущее решение:

Обновление 1. Пользователь Сет Никелл улучшил предложенное мной решение, поэтому я обновляю этот ответ, поскольку его метод лучше. Следующее основано на его ответе на GitHub:

https://github.com/snickell/google_structlog

pip install google-structlog

Используется так:

import google_structlog

google_structlog.setup(log_name="here-is-mylilapp")

# Now you can use structlog to get searchable json details in stackdriver...
import structlog
logger = structlog.get_logger()
logger.error("Uhoh, something bad did", moreinfo="it was bad", years_back_luck=5)

# Of course, you can still use plain ol' logging stdlib to get { "message": ... } objects
import logging
logger = logging.getLogger("yoyo")
logger.error("Regular logging calls will work happily too")

# Now you can search stackdriver with the query:
# logName: 'here-is-mylilapp'

Оригинальный ответ:

Основываясь на ответе из этого потока GitHub, я использую следующий бодж для регистрации пользовательских объектов в качестве полезной нагрузки. Это больше похоже на оригинал_Worker.enqueue и поддерживает передачу настраиваемых полей.

from google.cloud.logging import _helpers
from google.cloud.logging.handlers.transports.background_thread import _Worker

def my_enqueue(self, record, message, resource=None, labels=None, trace=None, span_id=None):
    queue_entry = {
        "info": {"message": message, "python_logger": record.name},
        "severity": _helpers._normalize_severity(record.levelno),
        "resource": resource,
        "labels": labels,
        "trace": trace,
        "span_id": span_id,
        "timestamp": datetime.datetime.utcfromtimestamp(record.created),
    }

    if 'custom_fields' in record:
        entry['info']['custom_fields'] = record.custom_fields

    self._queue.put_nowait(queue_entry)

_Worker.enqueue = my_enqueue

затем

import logging
from google.cloud import logging as google_logging

logger = logging.getLogger('my_log_client')
logger.addHandler(CloudLoggingHandler(google_logging.Client(), 'my_log_client'))

logger.info('hello', extra={'custom_fields':{'foo': 1, 'bar':{'tzar':3}}})

В результате чего:

образ

Это значительно упрощает фильтрацию в соответствии с этими custom_fields.

Допустим, это плохое программирование, хотя до тех пор, пока эта функциональность не будет официально поддержана, кажется, что больше ничего нельзя сделать.

В настоящее время это невозможно, см. Этот открытый запрос на функциюgoogle-cloud-python Больше подробностей.

Официальная документация: Настройка облачного ведения журнала для Python

Вы можете записывать журналы в Logging из приложений Python.

  1. с помощью обработчика ведения журнала Python, включенного в клиентскую библиотеку ведения журнала, или
  2. с помощью облачной клиентской библиотеки Cloud Logging API для Python напрямую.

Я не получил модуль ведения журнала Python для экспорта jsonPayload, но библиотека облачного ведения журнала для Python работает:

google-cloud-logging >= v.3.0.0 может это сделать

Больше нет необходимости в обходном пути ведения журнала Python, единственное, что вам нужно, это установить Python >= 3.6 и

      pip install google-cloud-logging

Затем вы можете использовать ведение журнала Python с

      import os

# have the environment variable ready:
# GOOGLE_APPLICATION_CREDENTIALS
# Then:
GOOGLE_APPLICATION_CREDENTIALS = os.environ.get("GOOGLE_APPLICATION_CREDENTIALS")
client = google.cloud.logging.Client.from_service_account_json(
    GOOGLE_APPLICATION_CREDENTIALS
)
log_name = "test"
gcloud_logger = client.logger(log_name)

# jsonPayloads
gcloud_logger.log_struct(entry)
# messages
gcloud_logger.log_text('hello')
# generic (can find out whether it is jsonPayload or message)!
gcloud_logger.log(entry or 'hello')

Вы запускаете эти команды в файле Python за пределами GCP и получаете доступ к GCP более или менее с одной строкой, вам нужны только учетные данные.

Вы можете использовать gcloud logger даже для печати и регистрации за один раз, взято из Написание структурированных журналов , непроверено.

Ведение журнала Python для регистрации jsonPayload в журналах GCP (TL/DR)

Я не мог заставить это работать!

Вы также можете использовать встроенный модуль ведения журнала Python с обходным путем, упомянутым в другом ответе, но мне не удалось его запустить. Это не сработает, если вы передадите словарь или его json.dumps()непосредственно в качестве параметра, с тех пор вы получаете строковый вывод всего словаря, который вы не можете прочитать как дерево json. Но это также не сработало для меня, когда я использовал logger.info()для регистрации jsonPayload/json.dumps в параметре примера с именем extras.

      import json
import os

#...

# https://googleapis.dev/python/logging/latest/stdlib-usage.html
GOOGLE_APPLICATION_CREDENTIALS = os.environ.get("GOOGLE_APPLICATION_CREDENTIALS")
client = google.cloud.logging.Client.from_service_account_json(
    GOOGLE_APPLICATION_CREDENTIALS
)
log_name = "test"
handler = CloudLoggingHandler(client, name=log_name)
setup_logging(handler)
logger = logging.getLogger()
logger.setLevel(logging.INFO)  # Set default level.

#...

    # Complete a structured log entry.
    entry = dict(
        severity="NOTICE",
        message="This is the default display field.",
        # Log viewer accesses 'component' as jsonPayload.component'.
        component="arbitrary-property"
    )

    # Python logging to log jsonPayload into GCP logs
    logger.info('hello', extras=json.dumps(entry))

Я также попробовал google-structlogрешение другого ответа, которое только вызвало ошибку:

google_structlog.setup(log_name=log_name) TypeError: setup() получил неожиданный ключевой аргумент 'log_name'

Я использовал Python v3.10.2 и

      google-auth==1.35.0
google-cloud-core==1.7.2
google-cloud-logging==1.15.0
googleapis-common-protos==1.54.0

Шаги исследования Ведение журнала gcloud (TL/DR)

После исправленной проблемы (см. Слияние и закрытие в конце) на github в googleapis/python-logging: Ведение журнала: поддержка отправки структурированных журналов в stackdriver через stdlib 'logging'. №13

вы найдете подвиг !: журналы поддержки json #316:

Этот PR добавляет полную поддержку журналов JSON, а также стандартных текстовых журналов. Теперь пользователи могут вызвать logging.error({'a':'b'}), и они получат JsonPayload в облачном журналировании, или вызвать logging.error('test') для получения TextPayload.

В рамках этого изменения я добавил общую функцию logger.log(), которая служит универсальной точкой входа вместо logger.log_text или logger.log_struct. Он сделает вывод, какая функция журнала имеется в виду на основе типа ввода.

Раньше библиотека прикрепляла имя регистратора python как часть полезной нагрузки JSON для каждого журнала. Теперь эта информация будет прикрепляться в виде метки, предоставляя пользователям полный контроль над полями полезной нагрузки журнала.

Исправления №186, №263, №13

В основной новой версии перечислены новые функции:

работа(основная): версия 3.0.0 #473

Другие вопросы по тегам