system.query_log
Содержит информацию о выполняемых запросах, например, время начала обработки, продолжительность обработки, сообщения об ошибках.
Таблица не содержит входных данных для запросов INSERT .
Настойки логгирования можно изменить в секции серверной конфигурации query_log.
Можно отключить логгирование настройкой log_queries = 0. По-возможности, не отключайте логгирование, поскольку информация из таблицы важна при решении проблем.
Период сброса данных в таблицу задаётся параметром flush_interval_milliseconds в конфигурационной секции query_log. Чтобы принудительно записать логи из буффера памяти в таблицу, используйте запрос SYSTEM FLUSH LOGS.
ClickHouse не удаляет данные из таблица автоматически. Смотрите Введение.
Таблица system.query_log содержит информацию о двух видах запросов:
- Первоначальные запросы, которые были выполнены непосредственно клиентом.
- Дочерние запросы, инициированные другими запросами (для выполнения распределенных запросов). Для дочерних запросов информация о первоначальном запросе содержится в столбцах initial_* .
В зависимости от статуса (столбец type ) каждый запрос создаёт одну или две строки в таблице query_log :
- Если запрос выполнен успешно, создаются два события типа QueryStart и QueryFinish .
- Если во время обработки запроса возникла ошибка, создаются два события с типами QueryStart и ExceptionWhileProcessing .
- Если ошибка произошла ещё до запуска запроса, создается одно событие с типом ExceptionBeforeStart .
Чтобы уменьшить количество запросов, регистрирующихся в таблице query_log , вы можете использовать настройку log_queries_probability.
Чтобы регистрировать отформатированные запросы в столбце formatted_query , вы можете использовать настройку log_formatted_queries.
- type (Enum8) — тип события, произошедшего при выполнении запроса. Значения:
- ‘QueryStart’ = 1 — успешное начало выполнения запроса.
- ‘QueryFinish’ = 2 — успешное завершение выполнения запроса.
- ‘ExceptionBeforeStart’ = 3 — исключение перед началом обработки запроса.
- ‘ExceptionWhileProcessing’ = 4 — исключение во время обработки запроса.
- 1 — запрос был инициирован клиентом.
- 0 — запрос был инициирован другим запросом при выполнении распределенного запроса.
- 1 — TCP.
- 2 — HTTP.
- 0 — запрос запущен с интерфейса TCP.
- 1 — GET .
- 2 — POST .
Пример
SELECT * FROM system.query_log WHERE type = 'QueryFinish' ORDER BY query_start_time DESC LIMIT 1 FORMAT Vertical;
Row 1: ────── type: QueryFinish event_date: 2021-11-03 event_time: 2021-11-03 16:13:54 event_time_microseconds: 2021-11-03 16:13:54.953024 query_start_time: 2021-11-03 16:13:54 query_start_time_microseconds: 2021-11-03 16:13:54.952325 query_duration_ms: 0 read_rows: 69 read_bytes: 6187 written_rows: 0 written_bytes: 0 result_rows: 69 result_bytes: 48256 memory_usage: 0 current_database: default query: DESCRIBE TABLE system.query_log formatted_query: normalized_query_hash: 8274064835331539124 query_kind: databases: [] tables: [] columns: [] projections: [] views: [] exception_code: 0 exception: stack_trace: is_initial_query: 1 user: default query_id: 7c28bbbb-753b-4eba-98b1-efcbe2b9bdf6 address: ::ffff:127.0.0.1 port: 40452 initial_user: default initial_query_id: 7c28bbbb-753b-4eba-98b1-efcbe2b9bdf6 initial_address: ::ffff:127.0.0.1 initial_port: 40452 initial_query_start_time: 2021-11-03 16:13:54 initial_query_start_time_microseconds: 2021-11-03 16:13:54.952325 interface: 1 os_user: sevirov client_hostname: clickhouse.ru-central1.internal client_name: ClickHouse client_revision: 54449 client_version_major: 21 client_version_minor: 10 client_version_patch: 1 http_method: 0 http_user_agent: http_referer: forwarded_for: quota_key: revision: 54456 log_comment: thread_ids: [30776,31174] ProfileEvents: Settings: used_aggregate_functions: [] used_aggregate_function_combinators: [] used_database_engines: [] used_data_type_families: [] used_dictionaries: [] used_formats: [] used_functions: [] used_storages: [] used_table_functions: []
См. также
- system.query_thread_log — в этой таблице содержится информация о цепочке каждого выполненного запроса.
Сервис для логов за 5 минут
Во время разработки под мобильные устройства появилась проблема наблюдать и сравнивать несколько характеристик производительности и параметров на разных устройствах. (iPad/Samasung Galaxy Note 10.1/Nexus и т.д.). Можно было бы просто записывать логи в файл, потом свести их воедино, но хочется, чтобы информация с устройств поступала сразу после отладки в единую таблицу, да и не все устройства находятся у разработчиков на руках.
Единственным быстрым решением, приходящим на ум, был небольшой сервис на flask/bottle, но для этого пришлось бы поднять хранилище данных. Сказать честно, даже использование облачных решений на Azure/Heroku/AWS — это небольшая кучка дополнительных проблем для такой простой задачи: пароли, пути, зависимости и т.д. Нам же надо вести одну небольшую таблицу с несколькими параметрами, которые поступают с устройств. К тому же, данная утилита требовалась исключительно для удобства разработки, а не для продуктивного использования с тысячами пользователей.
Я постоянно записываю в свою базу знаний в Evernote различные хаки для повседневных задач, и недавно нашел там пример кода из какого-то open source проекта, где идет обращение с формой Google Docs через post запросы. И понеслось.
Google Docs и его формы
Необходимо было отследить три параметра: read time, processing time и название устройства.
Создаем форму в Google Docs и прописываем там нужные нам поля: read time, processing time, device. Получаем ссылку на форму: https://docs.google.com/forms/d/156UppB2Byfq-gdsDxr-DUU9_YBviBbt2Gelhx5W5MsI/viewform.curl https://docs.google.com/forms/d/156UppB2Byfq-gdsDxr-DUU9_YBviBbt2Gelhx5W5MsI/viewform | grep --color entry
Тут видим три входных параметра, которые можно передать через POST запрос.
curl -d "entry.1882636933=2.75&entry.454434040=11.43&entry.444705398=Galaxy Note 10.1" https://docs.google.com/forms/d/156UppB2Byfq-gdsDxr-DUU9_YBviBbt2Gelhx5W5MsI/formResponse
Теперь в нашей форме делаем связь с таблицей. И раздаем на эту таблицу нужные права.
Результат
«
Каждый запрос в таблице получает отметку времени, что является приятным бонусом. Получившаяся таблица с логами.
Ответы в таблице появляются в режиме реального времени и мы можем дальше делать с этой информацией все, что только пожелаем.
Думаю, это очень быстрое решение для небольших практических задач.
Тестирование через логирование: способы и примеры
Уже около года мы с командой разрабатываем продукт — софт для работы с графикой. Мы пытаемся экономить деньги и время, поэтому оптимизируем все, в том числе тестирование. В ходе разработки продукта мы достигли некоторых успехов, которыми я поделюсь с вами в этой статье.
В чем проблема
Если сравнить программу с живым организмом, то баг в ней — это болезнь. На возникновение «болезни» может повлиять целый ряд факторов и окружение, особенно, если мы рассматриваем веб-платформу в качестве запуска. Иногда причинно-следственная связь очень сложная, и баг, который нашли при тестировании, — результат целого ряда событий.
Как и при человеческих недугах, где лучше пациента никто не объяснит свои симптомы, ни один тестировщик не сможет рассказать, что произошло, лучше, чем сама программа.
Что же делать
Для того чтобы наша программа сама нам могла сообщить, что у неё «болит», мы возьмем пару готовых и бесплатных решений — ElasticSearch, LogStash и Kibana — и свяжем их воедино.
ElasticSearch — самонастраивающаяся база данных с хорошим поиском по тексту. Можете посмотреть тутор по ElasticSearch.
LogStash — синхронизатор с ElasticSearch и сбор логов из источников.
Kibana — веб-интерфейс с большим количеством дополнений.
Как это работает
Наше приложение сохраняет логи на сервере. Logstash инкрементально передает данные на ElasticSearch, в базу данных. Пользователь заходит в Kibana и видит сохраненные логи.
Хорошо настроенная Kibana может отображать данные в виде таблиц, графиков, карт и т. д.
Пример #1: социальная сеть
В 2016 году мы с нуля работали над закрытой социальной сетью для нашего клиента. Она была реалтайм, на сокетах, много сервисов и данных. За основу приложения мы взяли React + Redux, но в целом подход логирования не привязан к фреймворку.
Мы решили попробовать логировать приложение для того, чтобы сократить время на тестирование.
Что нужно сделать
- Подобрать логгер.
- Создать класс для хранения действий пользователя, которые предшествуют ошибке.
- Логировать взаимодействие с сервером.
- Логировать сокет соединения.
- Отправить данные на ElasticStack.
Начнем!
Подберем логгер для нашего приложения. Если это бекенд, я рекомендую использовать Winston. Для фронта я беру js-logger, он поддерживает основные методы логирования — log, info, warn, debug, error.
Логгер должен передавать данные в коллекцию с лимитом. Если мы превысим лимит, то первый элемент будет удален. Это сделано для того, чтобы мы не пересылали слишком большие данные.
export default class CollectionWithLimit < constructor(props) < this.limit = props.limit || 10; this.data = []; >checkLimit() < if (this.data.length === this.limit) < this.data.shift(); return true; >> add(data) < this.data.push(data); return this.checkLimit(); >>
В стек мы можем добавить метаданные: текущий язык, локализацию, текущую тему, информацию о браузере и системе, последние действия, userID.
UserID важен, благодаря ему мы будем понимать, у кого из тестировщиков произошла ошибка.
this.stack.session.start = getCurrentDateISO(); this.stack.actions = actions; this.stack.env.lang = lang; this.stack.env.href = href; this.stack.env.localization = getItem('localization'); this.stack.env.theme = getItem('theme'); this.stack.env.userID = getItem('userID');
Мы использовали для нашей социальной сети Redux. Он позволяет нам импортировать в код логгер через middleware, что упрощает сбор информации.
Префикс redux дает понять, на каком слое нашего приложения произошла ошибка.
Мы записываем тип действия с пометкой redux и те данные, которые пришли с действием.
const logActionsMiddleware = store => next => action => < let payload = Object.keys(action) .filter(key =>key !== 'type') .reduce((payload, key) => < payload[key] = action[key]; return payload; >, <>); logger.log(`redux|$|$`); return next(action); >;
Для того, чтобы покрыть логами наш сервер, мы использовали axios. Он позволяет вставить middleware в обработку всех запросов. Подвесим наш логгер на все ошибки сервера. Теперь все запросы обрабатываются, и если сервер умер или что-то не то прислал, мы об этом узнаем.
rest.interceptors.request.use( config => config, error => < if (error.message) < logger.error(error.message); >return Promise.reject(error); > ); rest.interceptors.response.use( response => response.data, error => < if (error.message) < logger.error(error.message); >return Promise.reject(error); > );
С сокетами все просто. По контракту мы договариваемся, что каждое сообщение будет иметь свой статус. Если статус пришел с ошибкой, мы его обрабатываем.
this.socketManager.io.on(SOCKET_EVENTS.NOTIFICATION, notification => < if (notification.status === 'error') < logger.info(`socket|Error $`); this.props.onAuthUpdate(); > else < this.props.onAddNotification(data); >>);
Также не забываем:
- использовать понятные сообщения;
- дробить сложные алгоритмы и разбавлять их логами;
- не допускать избыточность логов;
- логировать компоненты, особенно ошибки.
Мы можем по необходимости проставлять логи в компонентах, в catch методах React.
Рекомендую ставить имя компонента, чтобы при минифицированной версии понимать, в каком компоненте произошла ошибка.
static displayName = 'MyComponent'; . componentDidCatch(err) < logger.error(`react.$|$`) >
Сложные алгоритмы необходимо разбавлять логами, покрывать ими узкие места приложения.
Затем мы подписываемся на onerror и, в случае возникновения ошибки, шлем в наш Elastic информацию со всеми данными из стека.
window.onerror = (errorMsg, url, lineNumber, lineCount, trace) => < // create critical error let critical = <>; critical[CRITICAL] = serializeError(trace, lineNumber); let criticalData = mixParams.call(this, critical, true); this.stackCollection.add(criticalData); // get stack data let stackData = this.getStackData(); // send log to server this.sendLogToServer(stackData); >;
Что мы получили
- Мы сохраняем все действия, которые предшествовали ошибке, а также прикрепляем метаданные.
- В то время, когда произошла ошибка, данные отправляются на Elastic.
- Тестировщик, который нашел ошибку, прикрепляет к тикету ID с ошибкой.
- Мы заходим в Kibana, фильтруем по ID, понимаем, какие действия произошли, и фиксим баг.
Это хорошо, но не супер. Нет данных бекенда, не слишком развернутая информация. Мы можем сделать так, чтобы стало супер.
Пример #2: CleverBrush
Как я говорил, мы уже почти год пилим свой продукт — CleverBrush. Наша команда разрабатывает софт для работы с графикой — коллажи и редакторы.
Мы решили вышеописанный подход прокачать.
На нашем проекте не было никакого Redux, что же нам делать в таком случае? Есть 3 подхода, как организовать качественное логирование приложения:
- @ — декораторы — способ обернуть наш метод в функцию, в которой мы можем логировать состояние до выполнения метода и после. Такой подход подойдет, если у вас legacy код.
- Proxy — отличный способ, интегрирует любой код в методы взаимодействия с объектом. К сожалению, поддерживается не всеми браузерами.
- Писать код сразу с логами — хороший вариант при разработке с нуля. В таком случае вы ничего не пропустите, и код будет максимально покрыт логированием.
Так как у нас стартап, требований у нас нет, и иногда у нас не все логично.
Если тестировщик не понимает поведение — это баг, который нужно переработать. К тому же не все ошибки приводят к критическим последствиям. Для этих целей на стейджинге можно вывести кнопку в хедер для принудительной отправки логов. Тестировщик видит, что что-то работает не так, нажимает на кнопку и триггерит то же действие, что и на onerror.
Если все-таки критическая ошибка произошла, мы должны блокировать интерфейс, чтобы тестировщик не кликал дальше и не заходил в тупик.
window.onerror = (errorMsg, url, lineNumber, lineCount, trace) => < . // show BSOD let bsod = BSOD(stackData); document.body.appendChild(bsod); . >;
Если на стейджинге произошла критическая ошибка, мы выводим синий экран смерти.
Мы видим вверху текст со стеком этой критической ошибки, а внизу — действия, которые ей предшествовали. Еще мы получаем ID ошибки, тестировщику достаточно его выделить и прикрепить к тикету.
Наше приложение тесно взаимодействует с бекендом. Так как бекенд мы полностью писали с нуля, мы его тоже покрыли логированием. Для этого использовали winston + запись в файл через middleware Express. Logstash парсит логи из файла и отправляет в ElasticSearch. Чтобы объединить логи бекенда и фронта, мы можем генерировать ID сессии и отправлять в хедере каждого запроса.
rest.defaults.headers.common.sessionId = global.__session_id__;
Таким образом, у нас будет понимание: мы отправили запрос, и этот запрос выполняется на сервере. Приходит ответ, мы продолжаем работу на клиенте. В Kibana мы будем фильтровать по ID запросам и смотреть, что произошло.
Когда мы отправляем стек действий на Elastic, тестировщику приходит ID, и он его прикрепляет к тикету.
Что мы получили
- Мы сохраняем действия в нашей лимитированной коллекции, которые предшествовали ошибке, собираем метаданные по приложению.
- Линкуем сессию с фронтенда к бекенду через хедеры запросов.
- Блокируем интерфейс через синий экран смерти, если у тестировщика возникла ошибка.
- Выводим кнопку ошибки по требованию.
Логируйте приложения в том числе и на продакшене, потому что лучше, чем реальные пользователи, узкие места никакой тестировщик не найдет.
Альтернативы
В качестве альтернативных подходов я выделяю:
- Rollbar хорошо настраиваемый. Позволяет залогировать 500 тысяч ошибок за 150$ в месяц. Рекомендую использовать, если вы разрабатываете приложение с нуля.
- Sentry более прост в интеграции, но менее кастомизируем. Позволяет залогировать 1 миллион событий за 200$ в месяц.
Оба сервиса позволяют делать почти тоже самое и интегрироваться в бекенд.
Что дальше
Логирование — это не только поиск ошибок, это еще и мониторинг действий пользователя, сбор данных. Логирование может быть хорошим дополнением к Google Analytics и проверкой User Experience.
Выводы
Когда вы релизите приложение, для него жизнь только начинается. Будьте ответственны за свое детище, получайте отзывы, следите за логами и улучшайте ваше приложение. Пишите качественный софт и процветайте 🙂
На GitHub я выложил пример, созданный на React + Redux, где прикрутил простой логгер и собрал все то, о чем говорил в статье.
Детальнее о логировании вы можете узнать из видео моего доклада или в презентации на митапе «Съесть собаку».
Все про українське ІТ в телеграмі — підписуйтеся на канал DOU
Подобається Сподобалось 0
До обраного В обраному 2
Просмотреть логи изменений
При работе в Creatio может возникнуть необходимость получения информации об истории изменения данных в системе. Например, вы можете просмотреть записи контактов, которые редактировались в течение месяца.
Для этого используйте раздел Журнал изменений ( Рис. 1 ).
На заметку. По умолчанию логирование журнала изменений отключено. Чтобы изменения логировались, выполните настройки, описанные в статье Настроить журнал изменений.
Рис. 1 — Интерфейс раздела Журнал изменений
Журнал изменений логирует добавление, изменение и удаление значений в таблицах базы данных. Это могут быть разделы, детали, справочники и другие объекты системы.
Существуют такие способы получить информацию об изменении данных:
- Логи по всем записям раздела, детали или справочника, в том числе, удаленным, доступны в журнале изменений.
- Логи изменений по определенной записи можно открыть непосредственно с ее страницы.
Способ 1. Просмотреть логи записи из журнала изменений
Пример. Необходимо просмотреть все записи контактов, которые менялись в течение прошлого месяца.
- Перейдите в дизайнер системы, например, по кнопке .
- В блоке “Пользователи и администрирование” перейдите по ссылке “Журнал изменений”.
На заметку. Для просмотра журнала изменений у вас должны быть настроены права доступа на выполнение системной операции “Просмотр журнала изменений” (код “CanViewChangeLog”). Подробнее: Права доступа на системные операции .
Рис. 2 — Настройка фильтрации по дате для логов записи
Рис. 3 — Быстрый поиск записи
В результате отобразится перечень записей, в которые вносились изменения в текущем месяце ( Рис. 4 ). Пиктограммы возле даты изменения записи показывают тип выполненных операций: удаление, добавление или редактирование.
Рис. 4 — Просмотр журнала изменений раздела
Способ 2. Просмотреть логи со страницы записи
Пример. Необходимо просмотреть историю изменения данных на странице определенного контакта за прошлый месяц.
На заметку. Если у вас не отображается действие Открыть журнал изменений , проверьте настройку прав доступа на выполнение системной операции “Просмотр журнала изменений” (код “CanViewChangeLog”). Подробнее: Права доступа на системные операции .
- Перейдите на страницу нужной записи.
- Нажмите Действия —> Открыть журнал изменений ( Рис. 5 ).
Рис. 5 — Действие Открыть журнал изменений
- даты изменений;
- авторы изменений;
- название записи;
- список колонок, значения которых менялись;
- старые значения колонок;
- новые значения колонок.
Рис. 7 — Настройка фильтрации по дате для логов записи
В результате вы увидите изменения, которые вносились в течение установленного периода в те поля записи, которые были настроены для логирования (Рис. 7).