Перейти к содержанию

Добавить ручную трассировку

Пролеты, журналы и отслеживание

Ниже приведен простой пример использования 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:

Simple example in Live view

Синяя рамка с 1+ означает, что диапазон содержит 1 прямой дочерний элемент. Щелкните рамку, чтобы расширить диапазон, чтобы отобразить дочерние элементы:

Simple example in Live view with span opened

Пожалуйста, обратите внимание:

  1. Любой диапазон или журнал, созданный в блоке with logfire.span(...):, будет дочерним элементом этого диапазона. Это позволит вам хорошо организовать журналы в структурированном дереве. Вы также можете увидеть это родительское отношение в журнале консоли на основе отступа.

  2. Пролет имеет время начала и окончания, поэтому имеет продолжительность. Для завершения этого отрезка требуется 3 секунды.

  3. Для журналов время начала и окончания одинаковы, поэтому они не имеют продолжительности. Тем не менее, вы все равно можете увидеть, что журналы создаются за 1 секунду после начала и за 2 секунды до конца диапазона.

Если щелкнуть ссылку Discovery на верхней панели навигации, можно написать SQL для дальнейшего изучения, например:

Query in Explore view: select extract ('seconds' from end_timestamp-start_timestamp) as duration, kind, message, trace_id, span_id, parent_span_id from records order by start_timestamp.

Внимание:

  1. Пролеты и журналы хранятся вместе в одной таблице 记录.

  2. Журнал 的parent_span_id-это диапазон span_id.

  3. Оба имеют тот же trace_id. Вы можете нажать на него, чтобы открыть новую вкладку в режиме реального времени, которая отфильтрована к_для отслеживания_.

_Отслеживание_Это дерево интервалов/журналов, которое имеет общий корень. Каждый раз, когда новый пролет/журнал создается без активного диапазона, создается новый трассировка. Если это диапазон, то любые потомки этого диапазона будут частью того же трека. Чтобы хорошо организовать журнал в треке, лучше создать проемы на верхнем уровне, чтобы представлять расширенные действия, такие как обработка запросов веб-сервера.

Атрибуты

Пролеты и журналы могут быть добавлены к структурированным данным, например:

logfire.info ("Привет", name="Мир")

Если щелкнуть журнал «Hello » в режиме реального времени, на панели «Подробности» справа появятся следующие данные:

Имя attribute in Live View

Эти данные хранятся в формате 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)

Query in Explore view: select span_name, attributes->…'name' as name, message from records order by start_timestamp

Здесь вы можете увидеть:

  1. Первый аргумент “Hello {name}” становится значением в столбце span_name. Вы можете использовать его для поиска всех записей из одного и того же кода, даже если сообщения отличаются, например, с помощью SQL-фильтра span_name = 'Hello {name}'.

  2. Имя 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')

При щелчке на пролете в реальном режиме времени на правой панели появится вкладка "Исключение заднего хода":

Traceback in UI

Исключения, которые были захвачены, но не были повторно запущены, не записываются, например:

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.trace
  • logfire.debug
  • logfire.info
  • logfire.notice(日志火灾通知)
  • logfire.warn
  • logfire.error
  • logfire.fatal

По умолчанию журналы 跟踪 и 调试 скрыты. Вы можете изменить этот параметр, нажав раскрывающееся меню «Уровень по умолчанию» в режиме реального времени:

Default level dropdown

Вы также можете установить минимальный уровень для ведения журналов консоли с помощью 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')

Отображается так:

Spans colored by Level

Здесь уровень самого диапазона по-прежнему установлен как 默认, но они окрашены красным, а не синим, потому что содержат журналы ошибок.

Если пролет заканчивается необработанным исключением, то в дополнение к записям, описанным выше, уровень журнала для пролета также устанавливается на 错误. Это не происходит при использовании 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'.


本文总阅读量次