Spec-Zone.ru › Julia 1.10

Ведение журнала

Модуль Logging предоставляет способ записи истории и прогресса вычисления в виде журнала событий. События создаются путем вставки оператора ведения журнала в исходный код, например:

@warn "Abandon printf debugging, all ye who enter here!"
┌ Warning: Abandon printf debugging, all ye who enter here!
└ @ Main REPL[1]:1

Система предоставляет несколько преимуществ по сравнению с включением вызовов println() в исходный код. Во-первых, она позволяет управлять видимость и представлением сообщений без редактирования исходного кода. Например, в отличие от @warn выше

@debug "The sum of some values $(sum(rand(100)))"

по умолчанию не будет выводить ничего. Кроме того, включение отладочных утверждений в исходный код очень дешево, поскольку система избегает оценки сообщения, если оно будет впоследствии проигнорировано. В этом случае sum(rand(100)) и связанная с ним обработка строк никогда не будут выполнены, если отладочное ведение журнала не включено.

Во-вторых, инструменты ведения журнала позволяют прикреплять произвольные данные к каждому событию в виде набора пар «ключ-значение». Это позволяет захватывать локальные переменные и другие состояния программы для последующего анализа. Например, чтобы прикрепить локальную переменную массива A и сумму вектора v в качестве ключа s, можно использовать

A = ones(Int, 4, 4)
v = ones(100)
@info "Some variables"  A  s=sum(v)

# output
┌ Info: Some variables
│   A =
│    4×4 Matrix{Int64}:
│     1  1  1  1
│     1  1  1  1
│     1  1  1  1
│     1  1  1  1
└   s = 100.0

Все макросы ведения журнала @debug, @info, @warn и @error имеют общие особенности, подробно описанные в документации для более общего макроса @logmsg.

Структура события журнала

Каждое событие генерирует несколько фрагментов данных, некоторые предоставлены пользователем, а некоторые автоматически извлечены. Давайте сначала рассмотрим данные, определенные пользователем:

  • Уровень журнала — это широкая категория для сообщения, используемого для ранней фильтрации. Существует несколько стандартных уровней типа LogLevel; также возможны пользовательские уровни. Каждый имеет свое назначение:

    • Logging.Debug (уровень журнала -1000) — информация, предназначенная для разработчика программы. Эти события по умолчанию отключены.
    • Logging.Info (уровень журнала 0) — общая информация для пользователя. Представьте его как альтернативу прямому использованию println.
    • Logging.Warn (уровень журнала 1000) означает, что что-то не так, и, вероятно, требуется действие, но пока программа работает.
    • Logging.Error (уровень журнала 2000) означает, что что-то не так, и это вряд ли будет восстановлено, по крайней мере, этой частью кода. Часто этот уровень журнала не нужен, так как выброс исключения может передать всю необходимую информацию.
  • Сообщение — это объект, описывающий событие. По соглашению AbstractString сообщения, передаваемые в качестве сообщений, предполагаются в формате маркировки. Другие типы будут отображаться с использованием print(io, obj) или string(obj) для текстового вывода и, возможно, show(io,mime,obj) для других мультимедийных дисплеев, используемых в установленной программе ведения журнала.

  • Дополнительные пары «ключ-значение» позволяют прикреплять произвольные данные к каждому событию. Некоторые ключи имеют общепринятое значение, которое может повлиять на то, как событие интерпретируется (см. @logmsg).

Система также генерирует некоторую стандартную информацию для каждого события:

  • Строка, в которой был развернут макрос ведения журнала.
  • Строка и столбец, где появляется макрос ведения журнала в исходном коде.
  • Сообщение id — это уникальный, фиксированный идентификатор для утверждения исходного кода, где появляется макрос ведения журнала. Этот идентификатор предназначен для обеспечения достаточной стабильности даже при изменении исходного кода файла, пока само утверждение ведения журнала остается неизменным.
  • Группа для события, которая по умолчанию устанавливается в базовое имя файла без расширения. Это можно использовать для группирования сообщений в более мелкие категории, чем уровень журнала (например, все предупреждения об устаревании имеют группу :depwarn), или в логические группы между модулями или внутри них.

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

Обработка событий журнала

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

  • Создание событий журнала — это задача автора модуля, который должен решить, где будут срабатывать события и какую информацию включить.
  • Обработка событий журнала — то есть отображение, фильтрация, агрегация и запись — это задача автора приложения, который должен объединить несколько модулей в работающее приложение.

Журнализаторы

Обработка событий выполняется журнализатором, который является первой конфигурируемой пользователем частью кода, которая видит событие. Все журнализаторы должны быть подтипами AbstractLogger.

При возникновении события соответствующий журнализатор находится путем поиска журнализатора, локального для задачи, с глобальным журнализатором в качестве резервного варианта. Здесь идея в том, что код приложения знает, как должны обрабатываться события журнала, и существует где-то в верхней части стека вызовов. Таким образом, мы должны искать вверх по стеку вызовов, чтобы обнаружить журнализатор — другими словами, журнализатор должен быть динамически охватывающим. (Это различие с фреймворками ведения журнала, где журнализатор лексически охватывается; явно предоставляется автором модуля или как простая глобальная переменная. В такой системе неудобно управлять ведением журнала при компоновке функциональности из нескольких модулей.)

Глобальный журнализатор можно установить с помощью global_logger, а локальные для задачи журнализаторы можно управлять с помощью with_logger. Новые задачи наследуют журнализатор родительской задачи.

Библиотека предоставляет три типа журнализаторов. ConsoleLogger является журнализатором по умолчанию, который вы видите при запуске REPL. Он отображает события в удобочитаемом текстовом формате и пытается предоставить простой, но удобный для пользователя контроль над форматированием и фильтрацией. NullLogger — удобный способ отбросить все сообщения, где это необходимо; это эквивалент потока devnull для ведения журнала. SimpleLogger — это очень простой журнализатор форматирования текста, в основном полезный для отладки системы ведения журнала.

Пользовательские журнализаторы должны иметь перегрузки для функций, описанных в разделе справочника.

Ранняя фильтрация и обработка сообщений

При возникновении события выполняется несколько шагов ранней фильтрации, чтобы избежать генерации сообщений, которые будут отброшены:

  1. Проверяется уровень сообщения журнала по отношению к глобальному минимальному уровню (устанавливается с помощью disable_logging). Это грубое, но чрезвычайно дешевое глобальное значение.
  2. Изучается текущее состояние журнализатора, и уровень сообщения проверяется по отношению к минимальному уровню кэшированного журнализатора, как найденному путем вызова Logging.min_enabled_level. Это поведение можно переопределить с помощью переменных окружения (подробнее об этом позже).
  3. Вызывается функция Logging.shouldlog с текущим журнализатором, принимающим минимальную информацию (уровень, модуль, группа, идентификатор), которая может быть вычислена статически. Наиболее полезно, что shouldlog получает событие id, которое может использоваться для раннего отбрасывания событий на основе кэшированного предиката.

Если все эти проверки пройдены, сообщение и пары «ключ-значение» полностью оцениваются и передаются текущему журнализатору через функцию Logging.handle_message. handle_message() может выполнить дополнительную фильтрацию по мере необходимости и отобразить событие на экране, сохранить его в файле и т. д.

Исключения, возникающие при генерации события журнала, по умолчанию捕获 и регистрируются. Это предотвращает сбой приложения отдельными сломанными событиями, что полезно при включении малоиспользуемых отладочных событий в рабочей системе. Это поведение можно настроить для каждого типа журнализатора, расширив Logging.catch_exceptions.

Тестирование событий журнала

События журнала являются побочным эффектом выполнения обычного кода, но вы можете захотеть протестировать определенные информационные сообщения и предупреждения. Модуль Test предоставляет макрос @test_logs, который можно использовать для сопоставления шаблонов с потоком событий журнала.

Переменные среды

Фильтрация сообщений может влиять через переменную среды JULIA_DEBUG, и служит простым способом включения отладочного ведения журнала для файла или модуля. Загрузка Julia с JULIA_DEBUG=loading активирует @debug сообщения журнала в loading.jl. Например, в оболочках Linux:

$ JULIA_DEBUG=loading julia -e 'using OhMyREPL'
┌ Debug: Rejecting cache file /home/user/.julia/compiled/v0.7/OhMyREPL.ji due to it containing an invalid cache header
└ @ Base loading.jl:1328
[ Info: Recompiling stale cache file /home/user/.julia/compiled/v0.7/OhMyREPL.ji for module OhMyREPL
┌ Debug: Rejecting cache file /home/user/.julia/compiled/v0.7/Tokenize.ji due to it containing an invalid cache header
└ @ Base loading.jl:1328
...

В Windows то же самое можно сделать в CMD после выполнения set JULIA_DEBUG="loading" и в Powershell с помощью $env:JULIA_DEBUG="loading".

Аналогично, переменную среды можно использовать для включения отладочного ведения журнала модулей, таких как Pkg, или корней модулей (см. Base.moduleroot). Для включения всего отладочного ведения журнала используйте специальное значение all.

Чтобы включить отладочное ведение журнала из REPL, установите ENV["JULIA_DEBUG"] в имя интересующего модуля. Функции, определенные в REPL, принадлежат модулю Main; ведение журнала для них можно включить следующим образом:

julia> foo() = @debug "foo"
foo (generic function with 1 method)

julia> foo()

julia> ENV["JULIA_DEBUG"] = Main
Main

julia> foo()
┌ Debug: foo
└ @ Main REPL[1]:1

Используйте разделитель запятыми, чтобы включить отладку для нескольких модулей: JULIA_DEBUG=loading,Main.

Примеры

Пример: запись событий журнала в файл

Иногда может быть полезно записывать события журнала в файл. Вот пример того, как использовать локальный и глобальный журнализатор для записи информации в текстовый файл:

# Load the logging module
julia> using Logging

# Open a textfile for writing
julia> io = open("log.txt", "w+")
IOStream(<file log.txt>)

# Create a simple logger
julia> logger = SimpleLogger(io)
SimpleLogger(IOStream(<file log.txt>), Info, Dict{Any,Int64}())

# Log a task-specific message
julia> with_logger(logger) do
           @info("a context specific log message")
       end

# Write all buffered messages to the file
julia> flush(io)

# Set the global logger to logger
julia> global_logger(logger)
SimpleLogger(IOStream(<file log.txt>), Info, Dict{Any,Int64}())

# This message will now also be written to the file
julia> @info("a global log message")

# Close the file
julia> close(io)

Пример: Включение сообщений уровня отладки

Вот пример создания ConsoleLogger, который пропускает все сообщения с уровнем журнала, равным или превышающим Logging.Debug.

julia> using Logging

# Create a ConsoleLogger that prints any log messages with level >= Debug to stderr
julia> debuglogger = ConsoleLogger(stderr, Logging.Debug)

# Enable debuglogger for a task
julia> with_logger(debuglogger) do
           @debug "a context specific log message"
       end

# Set the global logger
julia> global_logger(debuglogger)

Справочник

Модуль ведения журнала

Logging.LoggingМодуль

Утилиты для захвата, фильтрации и представления потоков событий журнала. Обычно вам не нужно импортировать Logging для создания событий журнала; для этого стандартные макросы ведения журнала, такие как @info, уже экспортируются Base и доступны по умолчанию.

Создание событий

Logging.@logmsgМакрос

@debug message  [key=value | value ...]
@info  message  [key=value | value ...]
@warn  message  [key=value | value ...]
@error message  [key=value | value ...]

@logmsg level message [key=value | value ...]

Создайте запись журнала с информационным message. Для удобства определены четыре макроса ведения журнала @debug, @info, @warn и @error, которые ведут журнал на стандартных уровнях серьезности Debug, Info, Warn и Error. @logmsg позволяет программно установить level на любой LogLevel или пользовательский тип уровня журнала.

message должно быть выражением, которое вычисляется в строку, представляющую собой удобочитаемое описание события журнала. По соглашению, эта строка будет отформатирована как разметка Markdown при представлении.

Необязательный список key=value пар поддерживает произвольные пользовательские метаданные, которые будут переданы в бэкенд ведения журнала в качестве части записи журнала. Если предоставлено только выражение value, будет сгенерирован ключ, представляющий выражение, с использованием Symbol. Например, x преобразуется в x=x, а foo(10) преобразуется в Symbol("foo(10)")=foo(10). Для распаковки списка пар ключ-значение используйте обычный синтаксис распаковки @info "blah" kws....

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

  • _module=mod может использоваться для указания другого исходного модуля из расположения источника сообщения.
  • _group=symbol может использоваться для переопределения группы сообщений (обычно она выводится из базового имени файла источника).
  • _id=symbol может использоваться для переопределения автоматически сгенерированного уникального идентификатора сообщения. Это полезно, если вам нужно очень тесно связать сообщения, сгенерированные на разных строках источника.
  • _file=string и _line=integer могут использоваться для переопределения видимого расположения источника сообщения журнала.

Также есть пары ключ-значение, имеющие общепринятое значение:

  • maxlog=integer следует использовать как подсказку для бэкенда, что сообщение должно отображаться не более maxlog раз.
  • exception=ex следует использовать для передачи исключения с сообщением журнала, часто используемого с @error. Сопутствующий стек вызовов bt может быть добавлен с использованием кортежа exception=(ex,bt).

Примеры

@debug "Verbose debugging information.  Invisible by default"
@info  "An informational message"
@warn  "Something was odd.  You should pay attention"
@error "A non fatal error occurred"

x = 10
@info "Some variables attached to the message" x a=42.0

@debug begin
    sA = sum(A)
    "sum(A) = $sA is an expensive operation, evaluated only when `shouldlog` returns true"
end

for i=1:10000
    @info "With the default backend, you will only see (i = $i) ten times"  maxlog=10
    @debug "Algorithm1" i progress=i/10000
end
исходный код

Logging.LogLevelТип

LogLevel(level)

Уровень серьезности/подробности записи журнала.

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

Примеры

julia> Logging.LogLevel(0) == Logging.Info
true
исходный код

Logging.DebugКонстанта

Debug

Псевдоним для LogLevel(-1000).

Logging.InfoКонстанта

Info

Псевдоним для LogLevel(0).

Logging.WarnКонстанта

Warn

Псевдоним для LogLevel(1000).

Logging.ErrorКонстанта

Error

Псевдоним для LogLevel(2000).

Обработка событий с AbstractLogger

Обработка событий контролируется путем переопределения функций, связанных с AbstractLogger:

Методы для реализации Краткое описание
Logging.handle_message Обработка события журнала
Logging.shouldlog Ранняя фильтрация событий
Logging.min_enabled_level Нижняя граница уровня журнала для принимаемых событий
Необязательные методы Определения по умолчанию Краткое описание
Logging.catch_exceptions true Перехват исключений во время оценки события

Logging.AbstractLoggerТип

Журнализатор управляет фильтрацией и отправкой записей журнала. Когда генерируется запись журнала, журнализатор — это первый фрагмент настраиваемого кода пользователя, который получает возможность просмотреть запись и решить, что с ней делать.

исходный код

Logging.handle_messageФункция

handle_message(logger, level, message, _module, group, id, file, line; key1=val1, ...)

Записать сообщение в logger в level. Логическое расположение, в котором было сгенерировано сообщение, задается модулем _module и group; расположение источника — file и line. id — произвольное уникальное значение (обычно Symbol), используемое в качестве ключа для идентификации оператора записи журнала при фильтрации.

исходный код

Logging.shouldlogФункция

shouldlog(logger, level, _module, group, id)

Возвращает true, когда logger принимает сообщение в level, сгенерированное для _module, group и с уникальным идентификатором журнала id.

исходный код

Logging.min_enabled_levelФункция

min_enabled_level(logger)

Возвращает минимальный включенный уровень для logger для ранней фильтрации. То есть, уровень журнала, ниже или равный которому, все сообщения отфильтровываются.

исходный код

Logging.catch_exceptionsФункция

catch_exceptions(logger)

Возвращает true, если журнализатор должен перехватывать исключения, которые возникают во время построения записи журнала. По умолчанию сообщения перехватываются

По умолчанию все исключения перехватываются, чтобы предотвратить сбой программы из-за генерации сообщений журнала. Это позволяет пользователям уверенно включать малоиспользуемую функциональность, такую как отладочная запись журнала, в рабочей системе.

Если вы хотите использовать журнал как аудиторский след, вам следует отключить это для вашего типа журнализатора.

исходный код

Logging.disable_loggingФункция

disable_logging(level)

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

Примеры

Logging.disable_logging(Logging.Info) # Disable debug and info
исходный код

Использование журнализаторов

Установка и проверка журнализатора:

Logging.global_loggerФункция

global_logger()

Возвращает глобальный журнализатор, используемый для получения сообщений, когда для текущей задачи нет конкретного журнализатора.

global_logger(logger)

Установить глобальный журнализатор в logger, и вернуть предыдущий глобальный журнализатор.

исходный код

Logging.with_loggerФункция

with_logger(function, logger)

Выполните function, направляя все сообщения журнала в logger.

Пример

function test(x)
    @info "x = $x"
end

with_logger(logger) do
    test(1)
    test([1,2])
end
исходный код

Logging.current_loggerФункция

current_logger()

Возвращает регистратор для текущей задачи или глобальный регистратор, если к задаче не прикреплён ни один.

исходный код

Регистраторы, поставляемые с системой:

Logging.NullLoggerТип

NullLogger()

Регистратор, который отключает все сообщения и не производит вывод — аналог /dev/null для регистраторов.

исходный код

Logging.ConsoleLoggerТип

ConsoleLogger([stream,] min_level=Info; meta_formatter=default_metafmt,
              show_limited=true, right_justify=0)

Регистратор с форматированием, оптимизированным для удобочитаемости в текстовом консоли, например, при интерактивной работе с Julia REPL.

Уровни логов ниже min_level отфильтровываются.

Форматирование сообщений можно настроить, установив ключевые аргументы:

  • meta_formatter — функция, которая принимает метаданные события лога (level, _module, group, id, file, line) и возвращает цвет (как передается в printstyled), префикс и суффикс для сообщения журнала. По умолчанию используется префикс с уровнем журнала и суффикс, содержащий модуль, файл и строку местоположения.
  • show_limited ограничивает вывод больших структур данных тем, что поместится на экране, устанавливая ключ :limit IOContext во время форматирования.
  • right_justify — целочисленный столбец, в котором метаданные лога выравниваются по правому краю. По умолчанию ноль (метаданные выводятся на отдельной строке).

Logging.SimpleLoggerТип

SimpleLogger([stream,] min_level=Info)

Упрощённый регистратор для записи всех сообщений с уровнем, большим или равным min_level в stream. Если поток закрыт, то сообщения с уровнем лога больше или равным Warn будут записаны в stderr, а сообщения с меньшим уровнем — в stdout.

исходный код

© 2009–2024 Jeff Bezanson, Stefan Karpinski, Viral B. Shah, and other contributors
Licensed under the MIT License.
https://docs.julialang.org/en/v1.10/stdlib/Logging/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API