Делаем эквивалент 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.
- с помощью обработчика ведения журнала Python, включенного в клиентскую библиотеку ведения журнала, или
- с помощью облачной клиентской библиотеки 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
В основной новой версии перечислены новые функции: