Перейти к содержимому

Свой Sentry для крашей Qt-приложения: сервер, sentry-native и символы

Sentry — открытая платформа для сбора ошибок и крашей. Облачная версия удобна, но у неё два минуса: данные уходят из вашей инфраструктуры, а цена растёт вместе с числом событий.

Собственный Sentry снимает оба. Вы сами управляете данными, не платите за каждое событие и подключаете внутренние инструменты, не открывая их наружу.

Ниже — весь путь от пустого сервера до первого краша в дашборде:

  1. Поднимите Sentry на Ubuntu за Nginx с сертификатом Let’s Encrypt.
  2. Соберите клиент sentry-native в Visual Studio 2022.
  3. Подключите его к Qt/CMake-проекту.
  4. Загрузите отладочные символы, чтобы стеки читались.

Часть 1. Sentry на своём сервере

1. Установите Sentry

Подготовьте сервер на Ubuntu: добавьте пользователя и установите Docker.

Минимальные требования Sentry:

  • 4 ядра процессора;
  • 16 ГБ RAM и 16 ГБ swap;
  • 20 ГБ свободного места на диске.

1.1 Скачайте Sentry из репозитория и установите

VERSION=$(curl -Ls -o /dev/null -w %{url_effective} https://github.com/getsentry/self-hosted/releases/latest)
VERSION=${VERSION##*/}
git clone https://github.com/getsentry/self-hosted.git
cd self-hosted
git checkout ${VERSION}
./install.sh

1.2 Настройте sentry/config.yml

Укажите адрес сервера:

system.url-prefix: 'https://sentry.example.com'

Настройте отправку почты. В примере — SMTP2GO:

###############
# Mail Server #
###############

mail.backend: 'smtp'
mail.host: 'mail.smtp2go.com'
mail.port: 587
mail.username: '<USERNAME>'
mail.password: '<PWD>'

1.3 Настройте sentry/sentry.conf.py

Добавьте адрес сервера в доверенные источники CSRF:

CSRF_TRUSTED_ORIGINS = ["https://sentry.example.com"]

Включите SSL/TLS:

###########
# SSL/TLS #
###########

# If you're using a reverse SSL proxy, you should enable the X-Forwarded-Proto
# header and enable the settings below

SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
USE_X_FORWARDED_HOST = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SOCIAL_AUTH_REDIRECT_IS_HTTPS = True

1.4 Перезапустите Docker

docker compose down
docker compose up -d

2. Установите Nginx

2.1 Установите пакеты

sudo apt install nginx certbot python3-certbot-nginx

2.2 Создайте начальную конфигурацию

Откройте файл:

sudo nano /etc/nginx/sites-available/sentry

Вставьте минимальную конфигурацию. Она нужна, чтобы Let’s Encrypt проверил домен:

server {
    listen 80;
    listen [::]:80;
    server_name sentry.example.com;
    root /var/www/html;

    location /.well-known/acme-challenge/ {
        root /var/www/html;
    }

    location / {
        return 404;
    }
}

Включите конфигурацию:

sudo ln -s /etc/nginx/sites-available/sentry /etc/nginx/sites-enabled/sentry

2.3 Проверьте конфигурацию и перезагрузите Nginx

sudo nginx -t
sudo systemctl reload nginx

2.4 Запросите сертификат

sudo mkdir -p /var/www/html
sudo certbot certonly --webroot -w /var/www/html -d sentry.example.com --email support@example.com --agree-tos --no-eff-email

2.5 Скачайте параметры Диффи — Хеллмана (dhparam)

sudo ls /etc/letsencrypt/ffdhe2048.txt || sudo curl -o /etc/letsencrypt/ffdhe2048.txt https://ssl-config.mozilla.org/ffdhe2048.txt

2.6 Замените конфигурацию на полную

Откройте файл:

sudo nano /etc/nginx/sites-available/sentry

Вставьте:

error_log /var/log/nginx/error.log warn;

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name sentry.example.com;

    ssl_certificate     /etc/letsencrypt/live/sentry.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/sentry.example.com/privkey.pem;
    ssl_session_timeout 1d;
    ssl_session_cache   shared:MozSSL:10m;
    ssl_session_tickets off;
    ssl_dhparam         /etc/letsencrypt/ffdhe2048.txt;
    ssl_protocols       TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers off;

    add_header Strict-Transport-Security "max-age=63072000" always;

    client_max_body_size 100m;

    proxy_buffering   on;
    proxy_buffer_size 128k;
    proxy_buffers     4 256k;
    proxy_read_timeout 300s;
    proxy_send_timeout 300s;

    location ~ ^/api/[0-9]+/(envelope|minidump|security|store|unreal)/ {
        add_header Access-Control-Allow-Origin      * always;
        add_header Access-Control-Allow-Credentials false always;
        add_header Access-Control-Allow-Methods     GET,POST,PUT always;
        add_header Access-Control-Allow-Headers     sentry-trace,baggage always;
        add_header Access-Control-Expose-Headers    sentry-trace,baggage always;

        include proxy_params;
        proxy_pass http://127.0.0.1:9000;
    }

    location / {
        include proxy_params;
        proxy_pass http://127.0.0.1:9000;
    }
}

server {
    listen 80;
    listen [::]:80;
    server_name sentry.example.com;
    root /var/www/html;

    location /.well-known/ {
        try_files $uri =404;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

Что делает эта конфигурация:

  • Блок location ~ ^/api/... принимает события от SDK и отдаёт CORS-заголовки. Они нужны браузерным SDK.
  • client_max_body_size 100m поднимает лимит запроса до 100 МБ: минидампы и вложения бывают большими.
  • Блок на порту 80 оставляет доступным /.well-known/ для продления сертификата, а остальной трафик перенаправляет на HTTPS.
  • ssl_protocols TLSv1.2 TLSv1.3 оставляет TLS 1.2 для клиентов на Windows 10. Системный TLS-стек (Schannel) в ней не поддерживает TLS 1.3, а sentry-native с транспортом winhttp использует именно его (в части 2 клиент собран так). Если все ваши клиенты на Windows 11 или Linux, можно оставить только TLSv1.3.

2.7 Проверьте конфигурацию и перезагрузите Nginx

sudo nginx -t
sudo systemctl reload nginx
curl -I https://sentry.example.com

2.8 Перезапустите Sentry

docker compose down
docker compose up -d

Часть 2. Qt-приложение

Sentry Native SDK — клиент для сбора крашей и ошибок в приложениях на C и C++. Он поддерживает теги, breadcrumbs и произвольный контекст. Эти данные помогают воспроизвести и исправить проблему, которая случилась у пользователя.

Сервер из части 1 уже работает. Теперь подключите к нему приложение.

3. Соберите sentry-native в VS 2022 Community x64

Клонируйте исходники вместе с подмодулями:

git clone --recurse-submodules git@github.com:getsentry/sentry-native.git
cd sentry-native

Подготовьте окружение Visual Studio:

"C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat"

Настройте проект:

cmake -G "Visual Studio 17 2022" -A x64 -B build-x64-release -DSENTRY_BUILD_RUNTIMESTATIC=OFF -DSENTRY_BUILD_SHARED_LIBS=ON -DSENTRY_BACKEND=crashpad -DSENTRY_TRANSPORT=winhttp -DCMAKE_BUILD_TYPE=release -DSENTRY_BUILD_TESTS=OFF -DSENTRY_BUILD_EXAMPLES=OFF

Что означают ключевые параметры:

Параметр Что делает
SENTRY_BACKEND=crashpad Ловит краши отдельным процессом crashpad_handler
SENTRY_TRANSPORT=winhttp Отправляет события через WinHTTP
SENTRY_BUILD_SHARED_LIBS=ON Собирает sentry.dll вместо статической библиотеки
SENTRY_BUILD_RUNTIMESTATIC=OFF Использует динамическую CRT (/MD), как стандартные сборки Qt

Соберите проект:

cmake --build build-x64-release --config release --parallel

Установите:

cmake --install build-x64-release --config release --prefix C:/sentry-install/x64/release

4. Подключите Sentry в Qt-проекте

Пример проекта на Qt и CMake: https://github.com/danilevsky/sentry-native-qt-demo

Создайте проект в своём Sentry, если ещё не создали. Затем скопируйте DSN: Settings → Project → [ваш проект] → Client Keys (DSN) → DSN.

#include "mainview.h"

#include <QApplication>
#include <QScopeGuard>
#include <sentry.h>

int main(int argc, char *argv[])
{
	const std::string sDSN = "<SENTRY_DSN_VALUE>";

	sentry_options_t *options = sentry_options_new();
	sentry_options_set_dsn(options, sDSN.c_str());
	sentry_options_set_database_path(options, ".sentry-native");
	sentry_options_set_release(options, "test-app@1.0.1");
	sentry_options_set_debug(options, 1);
	sentry_init(options);

	auto sentryClose = qScopeGuard([] { sentry_close(); });

	QApplication a(argc, argv);
	MainView w;
	w.show();

	sentry_capture_event(sentry_value_new_message_event(
		/*   level */ SENTRY_LEVEL_INFO,
		/*  logger */ "custom",
		/* message */ "It works!"
		));

	return QCoreApplication::exec();
}

Что здесь важно:

  • sentry_options_set_release задаёт версию в формате имя@версия. По ней Sentry группирует краши по релизам.
  • sentry_options_set_debug включает отладочный вывод SDK. В релизной сборке выключайте.
  • qScopeGuard и sentry_close() отправляют накопленные события при выходе из программы. Без этого вызова часть событий может не уйти.

Положите crashpad_handler.exe рядом с приложением. SDK запускает его при инициализации и по умолчанию ищет в каталоге исполняемого файла. После cmake --install он лежит в каталоге bin установки рядом с sentry.dll. Копируйте оба файла в каталог приложения. Документация Sentry советует задавать пути явно:

sentry_options_set_handler_path(options, "path/to/crashpad_handler.exe");
sentry_options_set_database_path(options, "sentry-db-directory");

5. Загрузите отладочные символы

Чтобы стек в Sentry показывал функции и строки, загрузите файлы .pdb и .exe утилитой sentry-cli.

sentry-cli нужен токен организации, иначе файлы отладочной информации не загрузятся:

  1. Откройте Settings → Integrations → Create New Integration → Internal Integration.
  2. В разделе Token создайте токен для SENTRY_AUTH_TOKEN.

Задайте переменные и загрузите файлы:

SET SENTRY_END_POINT=<your_sentry_url>
SET SENTRY_AUTH_TOKEN=<token>
SET SENTRY_ORG=<org>
SET SENTRY_LOG_LEVEL=warn
SET SENTRY_PROJECT_NAME=<project_name>

sentry-cli --url %SENTRY_END_POINT% --auth-token %SENTRY_AUTH_TOKEN% debug-files upload -o %SENTRY_ORG% -p %SENTRY_PROJECT_NAME% "test_app.pdb" --log-level %SENTRY_LOG_LEVEL% --include-sources

sentry-cli --url %SENTRY_END_POINT% --auth-token %SENTRY_AUTH_TOKEN% debug-files upload -o %SENTRY_ORG% -p %SENTRY_PROJECT_NAME% "test_app.exe" --log-level %SENTRY_LOG_LEVEL%

6. Вызовите тестовый краш

Добавьте в приложение обработчик с обращением к нулевому указателю:

void MainView::OnClick()
{
	int *p = nullptr;
	*p = 42;
}

Запустите приложение, нажмите кнопку и откройте дашборд проекта. Вы увидите две записи:

  • сообщение «It works!»;
  • исключение со стеком вызовов и минидампом во вложениях (Attachments).

Ссылки