Лог - это первое, куда смотрит инженер, когда инстанс ведёт себя не так. Аудит - это отдельный поток, который отвечает на вопрос "кто и что сделал с данными и правами". В этом уроке мы разбираем, как внутри Tarantool устроен конвейер от события до строки в файле: куда, в каком формате и с какой детальностью пишутся сообщения, как это настраивается в двух треках (классический box.cfg и декларативный config.yaml 3.x), и где проходит граница между обычным логом и аудитом (последний - компонент Enterprise Edition).
Механика: как событие превращается в строку
Внутри ядра есть один общий логгер (say-подсистема, написанная на C). Любое сообщение - и от самого ядра (репликация, WAL, чекпойнты), и от вашего Lua-кода через модуль log - проходит через одну воронку из трёх решений:
- Уровень (log_level). У каждого сообщения есть severity. Логгер сравнивает его с порогом и отбрасывает всё, что ниже. Числовая шкала фиксирована: 0 fatal, 1 syserror, 2 error, 3 crit, 4 warn, 5 info, 6 verbose, 7 debug. Пишутся события с severity ВЫШЕ ИЛИ РАВНО порогу. По умолчанию порог 5 (info), то есть verbose и debug молчат.
- Назначение (log_to / log). Поток направляется в одно из трёх мест: файл, syslog или внешняя программа через пайп. Выбор взаимоисключающий - один логгер пишет в одно место.
- Формат (log_format). Строка форматируется либо как plain (человекочитаемая), либо как json (машиночитаемая, по одному JSON-объекту на строку - удобно для сборщиков типа ELK/Loki).
Код: Выделить всё
2024-04-09 17:34:38.905 [49502] main/116/console/unix/:/tarantool I> Hello
время pid поток/файбер/роль ^уровень сообщение
Неблокирующая запись (log_nonblock). Тонкий, но важный момент внутренней механики. При log_nonblock=true запись в лог не блокирует файбер: если буфер занят, сообщение может быть ПОТЕРЯНО, зато инстанс не встаёт. Для файла по умолчанию false (надёжность важнее), для syslog и пайпа - true. Это объясняет классический сюрприз: "часть сообщений из-под нагрузки не доехала".
Per-module уровни (log_modules). Можно задать общий порог пониже, а для шумного модуля - повыше (или наоборот). Внутри это таблица "имя модуля -> уровень", которую логгер сверяет до общего порога.

Конвейер логов и потока аудита
Ключевые команды и конфиг
Трек 1 - классический box.cfg (1.x/2.x, работает и в 3.x).
Код: Выделить всё
box.cfg{
log = 'file:/var/log/tarantool/instance.log',
log_level = 'verbose', -- можно строкой или числом 6
log_format = 'json',
log_nonblock = false,
log_modules = { replication = 'debug', memtx = 'warn' },
}
-- меняем порог на лету, без рестарта:
box.cfg{ log_level = 'debug' }
Код: Выделить всё
log:
to: file
file: /var/log/tarantool/instance.log
level: verbose
format: json
nonblock: false
modules:
replication: debug
# отправка в syslog вместо файла:
# log:
# to: syslog
# syslog:
# server: 127.0.0.1:514
# identity: tarantool
Код: Выделить всё
local log = require('log')
log.info('user %d logged in', 42) -- printf-стиль форматирования
log.warn('cache miss for key %q', k)
log.error({code = 500, msg = 'boom'}) -- таблица сериализуется в структуру
-- свой модуль с отдельным уровнем:
local mylog = require('log').new('billing')
mylog.verbose('charge started')
Код: Выделить всё
# из Lua после ротации:
require('log').rotate()
# или через tt (вызывается из postrotate logrotate):
tt logrotate
Аудит - это НЕ обычный лог. Это отдельный поток с собственным файлом, форматом и фильтром, заточенный под безопасность и комплаенс. Записываются не "репликация догоняет мастер", а события вроде: вход пользователя (auth_ok/auth_fail), создание/удаление пользователя, выдача и отзыв прав, создание/дроп спейса, по желанию - доступ к данным конкретных спейсов.
Код: Выделить всё
box.cfg{
audit_log = 'file:/var/log/tarantool/audit.log',
audit_format = 'json', -- json | csv (по умолчанию)
audit_filter = 'auth,user_create,user_drop,grant,revoke',
audit_spaces = { 'bands' }, -- логировать доступ только к этим спейсам
audit_nonblock = true,
}
Частые заблуждения и грабли
- "Поставил log_level=debug, а debug не пишется." Проверьте, что не перебивает per-module уровень или TT_LOG_LEVEL из окружения. Переменные окружения и log.cfg() до box.cfg() имеют приоритет на раннем старте.
- "Лог растёт, диск кончился, ротация не сработала." logrotate переименовал файл, но без log.rotate()/tt logrotate инстанс пишет в старый inode. Файл "исчез", а место не освободилось, пока дескриптор открыт.
- "Часть сообщений теряется под нагрузкой." Это log_nonblock=true для syslog/пайпа. Для гарантий используйте файл с nonblock=false.
- "Включу аудит в Community Edition." Модуль аудита есть только в Enterprise. В CE этих опций нет.
- "Аудит = просто очень подробный лог." Нет, это раздельные конвейеры: разный файл, формат, фильтр и семантика. Смешивать их в один файл не нужно.
- Пишут в лог в горячем цикле на уровне info. Для частых событий существует verbose - его можно держать выключенным в проде и включать точечно.
Мини-лабаПравило большого пальца: в проде держите общий уровень info, формат json (для сборщиков), файл с nonblock=false, ротацию через logrotate + tt logrotate. Аудит - отдельным файлом с узким audit_filter, иначе он станет неотличим от обычного лога по объёму.
Задание: на тестовом инстансе переключите формат лога на JSON и проверьте, что severity действительно фильтрует.
Код: Выделить всё
-- 1. посмотрите текущий порог
box.cfg.log_level --> 5 (info)
-- 2. включите json и поднимите детальность
box.cfg{ log_format = 'json', log_level = 'verbose' }
-- 3. сгенерируйте три сообщения разной важности
require('log').verbose('v-line')
require('log').info('i-line')
require('log').debug('d-line') -- НЕ должно попасть в файл при verbose
-- 4. откройте лог-файл: найдите v-line и i-line в JSON, убедитесь что d-line нет
Контрольные вопросы
- Какому числовому уровню соответствует verbose и почему при log_level=5 он не пишется в лог?
- Чем грозит log_nonblock=true и для каких назначений (file/syslog/pipe) он включён по умолчанию?
- Почему после работы logrotate инстансу обязательно нужен log.rotate() или tt logrotate? Что произойдёт, если этого не сделать?
- Назовите минимум три отличия потока аудита от обычного лога и объясните, почему их разносят по разным файлам.