Наверняка у каждого бэкендера или девопса была такая ситуация:
Нужно срочно посмотреть, что случилось на проде. Вы скачиваете server.log, по привычке кликаете на него в VS Code... и всё.
Редактор зависает, кулеры ноутбука взлетают, интерфейс не отвечает. Файл весит всего-то 2-3 ГБ, но для редактора на базе Electron/DOM это приговор.

Обычно в этот момент мы убиваем процесс VS Code и идём в терминал писать less или tail -f. Это работает, но лишает нас комфорта: нормального поиска, копипаста мышкой, подсветки синтаксиса.

Я перепробовал кучу расширений, но все они либо пытались загрузить файл в память (и падали), либо работали слишком медленно. Поэтому я решил написать своё решение — с Rust на бэкенде, memory-mapping и виртуализацией всего, что только можно.

Под катом расскажу, как скрестить VS Code с нативным бинарником, обойти лимиты браузера на высоту скролла и сделать grep по гигабайтам текста мгновенным.

Архитектура: Sidecar Pattern

VS Code — это веб-технологии. Держать в памяти JS-кучи 5 ГБ строк — плохая идея (V8 просто не даст).
Поэтому я выбрал архитектуру Sidecar (сайдкар):

  1. Frontend (Extension Host + Webview): Отвечает только за отрисовку того, что видит глаз пользователя.

  2. Backend (Rust Binary): Отвечает за I/O, поиск и хранение индекса.

Они общаются через стандартный stdin/stdout с помощью JSON-сообщений.

Rust Backend: mmap и индексы

Главная фишка — VS Code никогда не загружает файл целиком. Этим занимается Rust-процесс (log-core), и даже он не читает его в хип.

Я использую крейт memmap2. Он проецирует файл с диска в виртуальное адресное пространство процесса.

  • Плюс: ОС сама занимается пэйджингом. Если вы смотрите строки с 1000 по 1100, ОС подгрузит в RAM только эти страницы.

  • Результат: Открытие лога на 10 ГБ потребляет ничтожно мало оперативной памяти.

Индексация

Чтобы быстро прыгать по файлу (например, на строку 5 000 000), нам нужен индекс. При открытии файла Rust пробегает по нему один раз (O(n)), находя все символы \n.
Мы сохраняем смещения (offsets) в Vec.

// Упрощенная логика
let mmap = unsafe { MmapOptions::new().map(&file)? };
let mut offsets = vec![0];
for (i, byte) in mmap.iter().enumerate() {
    if *byte == b'\n' {
        offsets.push((i + 1) as u64);
    }
}

Теперь, когда фронтенд просит строки с 100 по 200, мы просто берем слайс из mmap по смещениям из вектора и отдаем байты. Никаких лишних аллокаций.

Поиск

Поиск реализован на Rust. Для ��бычного текста — поиск подстроки, для регулярок — крейт regex.
Важный нюанс: если файл огромный, а совпадений миллионы, мы не можем отдать их все сразу (JSON забьет канал). Поэтому поиск стримится пачками (chunks), а для UI есть лимит на количество отображаемых результатов (чтобы не повесить DOM).


Frontend: Виртуализация и боль с DOM

На стороне VS Code мы имеем Webview. И тут всплывает главная проблема браузеров — лимит высоты элемента.
Браузеры (Chrome/Chromium) не умеют рисовать div высотой в 100 миллионов пикселей (а именно столько займет лог на 5 млн строк). Интерфейс просто ломается или перестает скроллиться.

Решение: Фейковый скролл

Я реализовал свою математику скролла:

  1. Есть MAX_SCROLL_HEIGHT (безопасное число пикселей, около 10M).

  2. Если реальная высота лога больше, я применяю коэффициент масштабирования.

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

Рендеринг

В DOM попадает только то, что сейчас во вьюпорте + небольшой буфер сверху и снизу (BUFFER_LINES).
Состояние хранится в Map<line_num, content>. Как только строка уходит далеко за пределы экрана — она удаляется из мапы (cache eviction), чтобы не текла память в JS.


Фичи, за которые пришлось попотеть

1. Умная фильтрация и Гаттер (Gutter)

Сделать grep легко. Сложно сделать так, чтобы это было удобно в UI.
Допустим, у вас есть лог на 1 млн строк. Вы фильтруете по слову ERROR. Нашлось 500 строк.

Пользователь видит список из 500 строк. Но если он найдет баг, ему нужно знать, на какой строке оригинального файла этот баг произошел.

Мое решение:

  • Виртуальное представление (View Index) идет от 0 до 499.

  • Но в гаттере (полоска слева) я рисую реальные номера строк (например, 542, 1201, 8900).

  • Для этого поддерживается маппинг viewIndex -> actualLine.

Это позволяет копировать строку, кидать коллеге «смотри строку 15420» и при этом не видеть мусор вокруг.

2. Follow Mode (Tail -f)

Сделать аналог tail -f внутри редактора.

  • Polling: Раз в 500 мс фронтенд пинает бэкенд: «файл изменился?».

  • Rust: Проверяет размер файла. Если вырос — делает remap (это дешево) и дописывает индекс новых строк.

  • Frontend: Если включен режим Follow, скролл сам прыгает вниз.

Если вы начали скроллить вверх вручную — автоскролл отключается, чтобы не мешать чтению. Всё как в лучших терминалах.


Кроссплатформенная сборка

Так как бэкенд на Rust, его нужно компилировать под каждую ОС. Заставлять пользователя ставить cargo я не мог.
Поэтому я настроил GitHub Actions, которые собирают бинарники под:

  • win32-x64

  • linux-x64

  • darwin-x64 (Intel Mac)

  • darwin-arm64 (Apple Silicon)

VS Code Marketplace позволяет публиковать "Platform-specific extensions". Пользователь на Windows автоматически скачает VSIX, внутри которого лежит .exe, а маковод — версию с бинарником под Mach-O. Размер расширения при этом всего ~1 МБ.


Итог

Что получилось в итоге:

  • Скорость: Открытие логов на 5-10 ГБ занимает секунды (время уходит только на индексацию \n).

  • Память: VS Code больше не падает с OOM.

  • Удобство: Работают Ctrl+F, Ctrl+G (переход к строке), выделение мышкой и копирование.

Проект полностью Open Source. Если вы устали страдать от лагов редактора при просмотре логов — буду рад, если попробуете.

Ссылки:

Комментарии (5)


  1. Heggi
    15.12.2025 06:37

    Для больших файлов я обычно использую notepad++ (под винду), работает достаточно шустро.


  1. AndrewBond
    15.12.2025 06:37

    В похожей ситуации набросал tkinter GUI для питоновского polars. Строки не индексирую, но бонусом получил lazy loading и SQL для запросов к логам. До 30 секунд на практически любой запрос. Запускается и под виндой (python portable) и под линуксами.


  1. skovpen
    15.12.2025 06:37

    В mcview всё, что написано в "Итог", есть, но открытие происходит быстрее, потому что "\n" индексировать не требуется.


  1. domanskiy
    15.12.2025 06:37

    vim/nvim для больших файлов. Плюс, сразу на сервере можно глянуть не качая
    Говорят, Zed неплохо справляется.

    Но как я понял, тут нужда именно в реализации на основе VSC. Отличное решение.


  1. leonidv
    15.12.2025 06:37

    Пошел посмотреть на github как устроено кросскомпиляция, а ссылка видет на gitlab :(