Ведение журнала
Модуль 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сообщения, передаваемые в качестве сообщений, предполагаются в формате Markdown. Другие типы будут отображаться с помощьюprint(io, obj)илиstring(obj)для текстового вывода и, возможно,show(io,mime,obj)для других мультимедийных дисплеев, используемых в установленной системе ведения журнала.Дополнительные пары ключ-значение позволяют присоединять произвольные данные к каждому событию. Некоторые ключи имеют общепринятое значение, которое может влиять на способ интерпретации события (см.
@logmsg).
Система также генерирует некоторую стандартную информацию для каждого события:
- Место в коде, где макрос ведения журнала был развернут.
- Строка и столбец, где происходит макрос ведения журнала в исходном коде.
- Идентификатор сообщения
id, который представляет собой уникальный, фиксированный идентификатор для выражения исходного кода, где появляется макрос ведения журнала. Этот идентификатор разработан для достаточно стабильной работы даже если исходный код файла изменяется, при условии, что само выражение ведения журнала остается неизменным. - Группа
groupдля события, которая по умолчанию устанавливается в имя файла без расширения. Это можно использовать для группирования сообщений в категории более точно, чем уровень журнала (например, все предупреждения об устаревании имеют группу:depwarn), или в логические группы в пределах или между модулями.
Обратите внимание, что некоторые полезные данные, такие как время события, по умолчанию не включены. Это связано с тем, что извлечение такой информации может быть дорогостоящим, и она также динамически доступна для текущего системы ведения журнала. Легко определить пользовательскую систему ведения журнала, чтобы дополнить данные события временем, трассировкой стека вызовов, значениями глобальных переменных и другой полезной информацией по мере необходимости.
Обработка событий журнала
Как видно из примеров, операторы ведения журнала не упоминают, куда отправляются события журнала или как они обрабатываются. Это ключевая особенность дизайна, которая делает систему составной и естественной для одновременного использования. Она достигается путем разделения двух разных задач:
- Создание событий журнала — это задача автора модуля, который должен решить, где запускаются события и какую информацию включить.
- Обработка событий журнала — это задача автора приложения, которое должно объединить несколько модулей в работающее приложение.
Системы ведения журнала
Обработка событий выполняется системой ведения журнала, которая является первой настраиваемой частью кода, которая видит событие. Все системы ведения журнала должны быть подтипами AbstractLogger.
При возникновении события система находит соответствующую систему ведения журнала, ища локальную систему ведения журнала для задачи, с глобальной системой ведения журнала в качестве резервного варианта. Идея заключается в том, что код приложения знает, как обрабатывать события журнала и существует где-то в верхней части стека вызовов. Поэтому мы должны искать вверх по стеку вызовов, чтобы обнаружить систему ведения журнала — то есть система ведения журнала должна быть динамически глобальной. (Это отличие от систем ведения журналов, где система ведения журнала лексически глобальная; явно предоставлена автором модуля или как простая глобальная переменная. В такой системе неудобно управлять ведением журнала при объединении функциональности из нескольких модулей.)
Глобальная система ведения журнала может быть установлена с помощью global_logger, а локальные системы ведения журнала для задач — с помощью with_logger. Новые задачи наследуют систему ведения журнала родительской задачи.
Библиотека предоставляет три типа систем ведения журнала. ConsoleLogger — это система ведения журнала по умолчанию, которую вы видите при запуске REPL. Она отображает события в удобочитаемом текстовом формате и пытается предоставить простой, но удобный для пользователя контроль над форматированием и фильтрацией. NullLogger — удобный способ отбросить все сообщения, где это необходимо; это аналог потока devnull для ведения журнала. SimpleLogger — очень простая система ведения журнала с форматированием текста, которая полезна в основном для отладки самой системы ведения журнала.
Пользовательские системы ведения журнала должны содержать перегрузки для функций, описанных в разделе справочника.
Предварительная фильтрация и обработка сообщений
При возникновении события происходит несколько этапов предварительной фильтрации, чтобы избежать генерации сообщений, которые будут отброшены:
- Проверяется уровень сообщения журнала относительно глобального минимального уровня (установленного с помощью
disable_logging). Это грубая, но чрезвычайно быстрая глобальная настройка. - Проверяется текущее состояние системы ведения журнала, а уровень сообщения проверяется относительно минимального уровня системы ведения журнала, найденного при вызове
Logging.min_enabled_level. Это поведение может быть переопределено с помощью переменных среды (подробнее об этом позже). - Вызывается функция
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 incompatible 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 incompatible 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).
Logging.BelowMinLevelКонстанта
BelowMinLevel
Псевдоним для LogLevel(-1_000_001).
Logging.AboveMaxLevelКонстанта
AboveMaxLevel
Псевдоним для LogLevel(1_000_001).
Обработка событий с 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.
исходный код
Base.CoreLogging.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ограничивает вывод больших структур данных чем-то, что может поместиться на экране, устанавливая ключевое значение:limitIOContextво время форматирования. -
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.11/stdlib/Logging/