Добавить ручную трассировку
Пролеты, журналы и отслеживание¶
Ниже приведен простой пример использования Logfire:
import time
import logfire
logfire.configure()
with logfire.span('This is a span'):
time.sleep(1)
logfire.info('This is an info log')
time.sleep(2)
Если вы запустите это, он должен печатать что-то похожее:
Logfire project URL: https://logfire.pydantic.dev/my_username/my_project_name
21:02:55.078 This is a span
21:02:56.084 This is an info log
URL-адрес открытого проекта должен отображаться в режиме Live:
Синяя рамка с 1+ означает, что диапазон содержит 1 прямой дочерний элемент. Щелкните рамку, чтобы расширить диапазон, чтобы отобразить дочерние элементы:
Пожалуйста, обратите внимание:
-
Любой диапазон или журнал, созданный в блоке
with logfire.span(...):, будет дочерним элементом этого диапазона. Это позволит вам хорошо организовать журналы в структурированном дереве. Вы также можете увидеть это родительское отношение в журнале консоли на основе отступа. -
Пролет имеет время начала и окончания, поэтому имеет продолжительность. Для завершения этого отрезка требуется 3 секунды.
-
Для журналов время начала и окончания одинаковы, поэтому они не имеют продолжительности. Тем не менее, вы все равно можете увидеть, что журналы создаются за 1 секунду после начала и за 2 секунды до конца диапазона.
Если щелкнуть ссылку Discovery на верхней панели навигации, можно написать SQL для дальнейшего изучения, например:
Внимание:
-
Пролеты и журналы хранятся вместе в одной таблице
记录. -
Журнал
的parent_span_id-это диапазонspan_id. -
Оба имеют тот же
trace_id. Вы можете нажать на него, чтобы открыть новую вкладку в режиме реального времени, которая отфильтрована к_для отслеживания_.
_Отслеживание_Это дерево интервалов/журналов, которое имеет общий корень. Каждый раз, когда новый пролет/журнал создается без активного диапазона, создается новый трассировка. Если это диапазон, то любые потомки этого диапазона будут частью того же трека. Чтобы хорошо организовать журнал в треке, лучше создать проемы на верхнем уровне, чтобы представлять расширенные действия, такие как обработка запросов веб-сервера.
Атрибуты¶
Пролеты и журналы могут быть добавлены к структурированным данным, например:
logfire.info ("Привет", name="Мир")
Если щелкнуть журнал «Hello » в режиме реального времени, на панели «Подробности» справа появятся следующие данные:
Эти данные хранятся в формате JSON в столбце 属性 таблицы 记录. Например, вы можете использовать attributes->>'name' = 'world' для отображения только этого журнала в SQL-фильтрах в верхней части просмотра в режиме реального времени. Это используется в качестве предложения WHERE для SQL-запросов в таблице 记录.
Как span, так и logs могут иметь свойства, содержащие любые значения, которые будут разумно сериализованы в JSON по мере необходимости. Вы можете передать любые параметры ключевых слов, чтобы задать свойства, если они не начинаются с подчеркивания (_). Пространство имен зарезервировано для других ключевых параметров, имеющих особое значение logfire.
Иногда полезно присоединить свойства к диапазону после создания диапазона, но до его завершения. Это можно сделать, вызовав метод span.set_attribute:
with logfire.span('Calculating...') as span:
result = 1 + 2
span.set_attribute('result', result)
Сообщения и имена проходов¶
При запуске этого кода:
import logfire
logfire.configure()
for name in ['Alice', 'Bob', 'Carol']:
logfire.info('Hello {name}', name=name)
Здесь вы можете увидеть:
-
Первый аргумент
“Hello {name}”становится значением в столбцеspan_name. Вы можете использовать его для поиска всех записей из одного и того же кода, даже если сообщения отличаются, например, с помощью SQL-фильтраspan_name = 'Hello {name}'. -
Имя span также используется в качестве шаблона для стиля
str.format, который использует свойства для создания столбца消息. Это сообщение отображается в журнале консоли и режиме реального времени.
Вы также можете настроить span.message после начала пролета, но до его завершения, например:
with logfire.span('Calculating...') as span:
result = 1 + 2
span.message = f'Calculated: {result}'
Вы можете использовать.消息 для фильтрации соответствующих записей, например “Hello%”之类的消息 Но да.span_name Фильтрация столбцов является более эффективной, поскольку они индексированы. Аналогично, используйте span_name = 'Hello {name}' 和 attributes->>'name' = 'Alice' чем использование message = 'Hello Alice' Лучше.
Для того, чтобы иметь возможность эффективно фильтровать связанные записи, название диапазона_Базовая основа_Они должны быть ниже, а это означает, что они не должны сильно меняться. Например, это было бы плохо:
name = get_username()
logfire.info('Hello ' + name, name=name)
Потому что теперь столбец span_name будет иметь разные значения для каждого имени пользователя. Но это хорошо:
Потому что теперь столбцы span_name имеют только два значения ('Goodbye {name}' и 'Hello {name}'), а фильтрация span_name = 'Hello {name}' проще и эффективнее, чем фильтрация span_name = '{word} {name}' 和 attributes->>'word' = 'Hello'.
Если вы хотите, чтобы имя span отличалось от шаблона сообщения, используйте параметр _span_name, например:
word = 'Goodbye' if leaving else 'Hello'
logfire.info(word + ' {name}', name=name)
При этом span_name будет иметь значение “Hello”, а 消息-“Hello world”。 Обратите внимание, что параметр _span_name начинается с подчеркивания, чтобы отличить его от свойства.
logfire.info('Hello {name}', name='world', _span_name='Hello')
f-strings¶
Вместо этого:
logfire.info('Hello {name}', name=name)
Гораздо проще использовать F-String, чтобы избежать повторения NAME в три раза:
logfire.info(f'Hello {name}')
В отличие от предыдущей версии, это в Python 3.11+._Эффект_Отлично, потому что Logfire будет использовать специальную магию.span_name Установить в “Hello {name}” и будет name Свойство установлено как name Значение переменной, поэтому оно эквивалентно предыдущему фрагменту кода. Вот что вам нужно знать:
-
Эта функция включена по умолчанию в Python 3.11+. Вы можете отключить ее с помощью [
logfire.configure(inspect_arguments=False)][logfire.configure(inspect_arguments)]. Вы также можете включить ее в Python 3.9 и 3.10, но скорее всего она не будет работать должным образом. -
В нормальных условиях проверка параметров должна всегда быть действительной. Обратите внимание, что исходный код должен быть доступен, поэтому, например, развертывание только файла
.pycприведет к его сбою. -
Если проверка параметров не удаётся, вы получите предупреждение, и аргумент f-string будет использоваться в качестве шаблона форматирования. Это означает, что вы получите имена диапазонов с высоким базовым уровнем, такие как
“Hello Alice”, и не имеете свойств名称, но информация не будет полностью потеряна. -
Если параметр проверки включен, он будет проверяться независимо от того, используется ли строка f или нет. Таким образом, если вы пишете
logfire.info('Hello {name}', name=name)и не выполните проверку параметра, вы все равно получите предупреждение. -
Значения в f-strings снова вычисляются и отформатируются Logfire. Это означает, что если
get_username()(или любое преобразование строки, которое он возвращает) является дорогостоящим или имеет побочные эффекты, вы должны избегать использования кода, подобногоlogfire.info(f'Hello {get_username()}'). -
Первым аргументом должна быть фактическая строка f.
logfire.info(f'Hello {name}')будет работать, ноmessage = f'Hello {name}'; logfire.info(message)не будет,logfire.info('Hello ' + name)тоже не будет. -
Параметры проверки кэшируются, чтобы повторно проверить одну и ту же строку f с минимальными издержками производительности. Однако при анализе больших исходных файлов при первой проверке параметров возникают значительные дополнительные издержки. В любом случае, чтобы избежать этих издержек, необходимо полностью отключить параметр проверки, а не просто избежать строки f.
Необычное¶
logfire.span Контекстный менеджер автоматически регистрирует любые исключения, которые привели к его выходу, такие как:
import logfire
logfire.configure()
with logfire.span('This is a span'):
raise ValueError('This is an error')
При щелчке на пролете в реальном режиме времени на правой панели появится вкладка "Исключение заднего хода":
Исключения, которые были захвачены, но не были повторно запущены, не записываются, например:
with logfire.span('This is a span'):
try:
raise ValueError('This is an acceptable error not worth recording')
except ValueError:
pass
Если вы хотите записать обработанные исключения, используйте метод span.record_exception :
with logfire.span('This is a span') as span:
try:
raise ValueError('Catch this error, but record it')
except ValueError as e:
span.record_exception(e)
Или, если вы хотите просто записать исключения без создания диапазона для нормальной ситуации, вы можете использовать logfire.exception :
try:
raise ValueError('This is an error')
except ValueError:
logfire.exception('Something went wrong')
logfire.exception(...) является эквивалентом logfire.error(..., _exc_info=True)。 Если вы хотите записывать в журнал отходы, не связанные с ошибками, вы можете использовать _exc_info с другими методами ведения журнала. Если объект исключения не является объектом, который обрабатывается, вы можете установить _exc_info в качестве объекта исключения. Не забудьте подчеркнуть главный акцент!
Удобный диапазон функций с помощью @logfire.instrument¶
Как правило, вы хотите, чтобы вся функция была упакована в один диапазон. Вместо этого:
def my_function(x, y):
with logfire.span('my_function', x=x, y=y):
...
Вы можете использовать @logfire.instrument декор:
По умолчанию параметр функции добавляется к свойству span. Чтобы отключить эту функцию (например, если параметр является большим объектом, который не стоит собирать), используйте instrument(extract_args=False)。
Имя пролета по умолчанию аналогично Calling module_name.my_function. Вы можете передать альтернативное имя пролета в instrument в качестве первого аргумента, и это может даже служить шаблоном форматирования аргументов, например:
@logfire.instrument('Applying my_function to {x=} and {y=}')
def my_function(x, y):
...
my_function(3, 4)
# Logs: Applying my_function to x=3 and y=4
Внимание
- '@logfire.instrument' декор должен быть применен сначала, то есть под любым другим украшением.
- Исходный код функции должен быть доступным.
Уровень журнала¶
Для создания журналов различных уровней можно использовать следующие способы:
logfire.tracelogfire.debuglogfire.infologfire.notice(日志火灾通知)logfire.warnlogfire.errorlogfire.fatal
По умолчанию журналы 跟踪 и 调试 скрыты. Вы можете изменить этот параметр, нажав раскрывающееся меню «Уровень по умолчанию» в режиме реального времени:
Вы также можете установить минимальный уровень для ведения журналов консоли с помощью logfire.configure , например:
import logfire
logfire.configure(console=logfire.ConsoleOptions(min_log_level='debug'))
Для записи сообщений с уровнем переменных можно использовать logfire.log, например,logfire.log('info', 'This is an info log') эквивалентен logfire.info('This is an info log')。
По умолчанию диапазон является уровнем 信息. Вы можете изменить этот параметр с помощью параметра _level, например 使用 logfire.span('This is a debug span', _level='debug'):. Вы также можете изменить уровень после начала пролета, но до завершения пролета, используя span.set_level . LogfireSpan.set_level], например:
with logfire.span('Doing a thing') as span:
success = do_thing()
if not success:
span.set_level('error')
В режиме реального времени:Пролеты окрашены в соответствии с их высшим уровнем и потомкамиНапример, следующий код:
import logfire
logfire.configure()
with logfire.span('Outer span'):
with logfire.span('Inner span'):
logfire.info('This is an info message')
logfire.error('This is an error message')
Отображается так:
Здесь уровень самого диапазона по-прежнему установлен как 默认, но они окрашены красным, а не синим, потому что содержат журналы ошибок.
Если пролет заканчивается необработанным исключением, то в дополнение к записям, описанным выше, уровень журнала для пролета также устанавливается на 错误. Это не происходит при использовании span.record_exception . Метод LogfireSpan.record_exception].
В базе данных уровень журнала хранится в столбце 级别 в виде цифр. Эти значения основаны на OpenTelemetry, например,info в 9. Вы можете преобразовать имена уровней в цифры с помощью функции level_num SQL, например 级别 > level_num('info') будет искать все «исключения». Вы также можете преобразовать цифры в имена с помощью функции level_name SQL, например SELECT level_name(level)、..., чтобы увидеть уровень чтения для человека в представлении «Исследование». Обратите внимание, что столбец level проиндексирован, поэтому фильтрация level = level_num('error') является эффективной, но не для level_name(level) = 'error'.
本文总阅读量次







