Learning Platform
Глоссарий Troubleshooting
Урок 19.03 · 22 мин
Начальный
DebuggingLogsdbt.logStack traceError patterns

В терминале dbt показывает только финальные строки результата (PASS/FAIL/SKIP). Но полная картина запуска — каждое SQL-statement, каждый Jinja-warning, каждый stack trace — лежит в logs/dbt.log. Когда dbt run падает с лаконичным «Database Error» в терминале, ответ почти всегда в логе.

Где живёт лог

По умолчанию dbt пишет лог в папку logs/ в корне проекта:

my_project/
├── dbt_project.yml
├── models/
└── logs/
    └── dbt.log      # последний (и единственный по умолчанию) файл

Лог-файл append-only: каждый новый запуск дописывается в конец. Со временем он растёт, и удобство падает. Помогает:

# Очистить лог (можно безопасно — это просто диагностика)
> logs/dbt.log

# Удалить старые
rm logs/dbt.log

Или настроить ротацию в dbt_project.yml:

flags:
  log_file_max_bytes: 10485760  # 10 MB, после ротация
  log_format: text              # text | json

json-формат полезен для интеграций с Datadog/Splunk/Grafana Loki — каждая строка валидный JSON.


Структура строки лога

Стандартная text-строка выглядит так:

14:23:15.123456 [info ] [MainThread]: Running with dbt=1.10.2
14:23:15.234567 [debug] [Thread-2  ]: On model.jaffle_shop.customers: /* {"app": "dbt", "dbt_version": ...} */
14:23:15.234890 [debug] [Thread-2  ]: SQL status: SUCCESS in 0.083s

Компоненты:

  • 14:23:15.123456 — точное время с микросекундами
  • [info ] / [debug] / [warn ] / [error] — уровень
  • [MainThread] / [Thread-2] — какой воркер
  • Сообщение — остальное

Уровни от тихого к шумному:

  • error — что-то сломалось безоговорочно
  • warn — потенциальная проблема, не блокирующая
  • info — стандартные шаги (как в терминале)
  • debug — каждый SQL, каждое Jinja-развёртывание, всё

По умолчанию терминал показывает только info+. В лог пишется всё, начиная с debug.


Конфигурация логов

В dbt_project.yml или через CLI:

# Полный verbose в терминал
dbt --debug run --select customers

# Тишина — только ошибки в терминале
dbt --quiet run --select customers

# Уровень логирования в файл
dbt --log-level debug run     # debug в файл (по умолчанию)
dbt --log-level info run      # только info+ в файл

--debug в CLI = тот же verbose, что в dbt.log, но в терминал. Помогает когда не хочешь переключаться между окнами:

$ dbt --debug run --select customers
14:23:15  Running with dbt=1.10.2
14:23:15  Loaded profile from /Users/me/.dbt/profiles.yml
14:23:15  Building catalog
14:23:15  Connection 'master' created with target 'dev'
14:23:15  Acquiring new duckdb connection 'master'
14:23:15  Began compiling node model.jaffle_shop.customers
14:23:15  On model.jaffle_shop.customers: /* {"app": "dbt", "dbt_version": "1.10.2", ...} */
14:23:15  create or replace table "jaffle_shop"."main"."customers" as
14:23:15    (select c.customer_id, c.first_name, ...);
14:23:15  SQL status: SUCCESS 1000 in 0.083s
14:23:15  Finished compiling and running node ...

Видно каждый SQL, который dbt отправил, и его статус.


Что искать в логе

Допустим, dbt run --select customers упал. Открой logs/dbt.log, нажми Ctrl+End (или tail -n 200 logs/dbt.log в shell). Стандартный паттерн — три уровня поиска:

1. Find the error

Search по слову Error или ERROR:

14:23:17  ERROR: Database Error in model customers (models/marts/customers.sql)
14:23:17    Catalog Error: Table "stg_orders" does not exist!
14:23:17    Did you mean "main.stg_orders"?

Здесь сразу видно:

  • Уровень: Database Error (не Compilation, не Runtime)
  • В какой модели: customers
  • Файл: models/marts/customers.sql
  • Точное сообщение от warehouse: Catalog Error: Table "stg_orders" does not exist!
  • Подсказка от DuckDB: Did you mean "main.stg_orders"?

2. Find the SQL

Прокрути выше — увидишь точный SQL, который dbt пытался выполнить:

14:23:17  On model.jaffle_shop.customers: 
14:23:17  create or replace table "jaffle_shop"."main"."customers" as (
14:23:17    select c.customer_id, count(o.order_id) as order_count
14:23:17    from stg_orders o  -- БАГ! Прямое имя без main.
14:23:17    ...
14:23:17  );

Теперь видно: в SQL stg_orders без префикса схемы, и DuckDB не находит её в текущем search_path. Скорее всего, в исходнике вместо {{ ref('stg_orders') }} написано просто stg_orders.

3. Find the Jinja trace (если ошибка компиляции)

Если ошибка типа Compilation Error, обычно есть Python stack trace:

14:23:18  Traceback (most recent call last):
14:23:18    File ".../dbt/cli/main.py", line 234, in cli_main
14:23:18    File ".../dbt/parser/manifest.py", line 567, in parse
14:23:18    jinja2.exceptions.TemplateError: 'dict object' has no attribute 'order_id'

Это говорит, что в Jinja-условии ты пытался достучаться до атрибута, которого в словаре нет. Обычно — опечатка в имени переменной или работа с пустым результатом run_query.


Полезные shell-фильтры

Лог большой. Используй grep, awk, tail:

# Только ошибки последнего запуска
grep -E "(ERROR|FAIL)" logs/dbt.log | tail -50

# Конкретная модель
grep "customers" logs/dbt.log | tail -30

# Все SQL, отправленные в БД (verbose mode)
grep -A 5 "create or replace" logs/dbt.log

# Тайминги — что было медленным
grep "SQL status: SUCCESS" logs/dbt.log | awk '{print $NF, $(NF-1)}' | sort -rn

# Только нужный thread
grep "Thread-2" logs/dbt.log

# Последний запуск (по timestamp)
sed -n '/2026-05-19 14:23:00/,$p' logs/dbt.log

В реальной работе у тебя в .bashrc будет пара алиасов:

alias dlog-err='grep -E "(ERROR|FAIL|warn)" logs/dbt.log | tail -50'
alias dlog-last='tail -200 logs/dbt.log'
alias dlog-sql='grep -A 5 "create or replace\|insert into" logs/dbt.log | tail -60'

Типичные паттерны ошибок в логе

Паттерн 1: Connection issue

14:25:01  ERROR: Database Error in model customers
14:25:01    Could not connect to database: Permission denied
14:25:01  Connection 'master' to duckdb at path './jaffle_shop.duckdb' failed

-> Либо path неверный, либо файл заблокирован другим процессом (DuckDB single-writer). Закрой все открытые psql/DataGrip-сессии и попробуй заново.

Паттерн 2: ref() to missing model

14:26:14  Compilation Error in rpc request (from remote system)
14:26:14    Model 'stg_orders' (models/staging/stg_orders.sql) depends on a node named 'stg_jaffle__orders' which was not found

-> В customers.sql есть {{ ref('stg_jaffle__orders') }}, но в проекте файла stg_jaffle__orders.sql нет. Опечатка или забыли создать модель.

Паттерн 3: Source not declared

14:27:00  Compilation Error
14:27:00    Source 'raw_jaffle.customers' was not found

-> В SQL {{ source('raw_jaffle', 'customers') }}, но в sources.yml source raw_jaffle не объявлен. Открыть models/staging/_sources.yml и проверить.

Паттерн 4: Test failures

14:28:11  Failure in test not_null_customers_customer_id
14:28:11  Got 12 results, configured to fail if != 0
14:28:11  
14:28:11  Compiled at target/compiled/jaffle_shop/tests/...sql

-> Тест провалился: 12 строк с NULL в customer_id. Открой target/compiled/.../<test>.sql, прогони руками — увидишь, какие именно строки нарушают.

Паттерн 5: Macro undefined

14:29:14  Compilation Error in model customers
14:29:14    'get_currency_symbol' is undefined
14:29:14    Line 4: amount * {{ get_currency_symbol('USD') }}

-> Либо макрос определён, но dbt deps не запускали (если из пакета), либо опечатка в имени, либо файл macros/...sql лежит вне macro-paths.

Паттерн 6: YAML parsing error

14:30:11  Compilation Error
14:30:11    mapping values are not allowed here at line 8, column 12 in models/staging/_models.yml

-> Битый YAML. Чаще всего — неправильный отступ (tabs вместо spaces) или незакрытая кавычка. Открой файл, проверь : на правильной строке.

Паттерн 7: Permission denied (warehouse)

14:31:18  Database Error
14:31:18    permission denied for table customers

-> dbt-юзер не имеет прав на CREATE/INSERT в этой схеме. Для DuckDB это редкость, для облачных warehouses — постоянная история. Решение: настроить grant’ы (см. модуль 14).

Паттерн 8: Out of memory (DuckDB)

14:32:14  Out of Memory Error: failed to allocate 2147483648 bytes, used: 8589934592 bytes
14:32:14  Memory limit reached: 8GB

-> Тяжёлая агрегация не помещается. Решения:

  • Увеличить memory_limit в profiles.yml
  • Добавить temp_directory для spill на диск
  • Упростить запрос (меньше джойнов, меньше groupBy)

--log-format json

Для облачных интеграций ставят JSON:

# dbt_project.yml
flags:
  log_format: json

Тогда строки в dbt.log:

{"data": {"msg": "Running with dbt=1.10.2"}, "level": "info", "ts": "2026-05-19T14:23:15.123Z", "thread": "MainThread"}
{"data": {"msg": "SQL status: SUCCESS 1000 in 0.083s", "node_info": {"node_path": "marts/customers.sql", ...}}, "level": "debug", "ts": "...", "thread": "Thread-2"}

Каждая строка — валидный JSON. Можно агрегировать через jq:

# Сколько эррор-строк в логе?
cat logs/dbt.log | jq -r 'select(.level == "error")' | wc -l

# Какие модели самые медленные?
cat logs/dbt.log | jq -r 'select(.data.msg | startswith("SQL status: SUCCESS")) | "\(.data.node_info.node_path) \(.data.msg)"' | sort

Параметры запуска: что искать вверху лога

Каждый запуск начинается с конфигурационного блока:

14:23:15  Running with dbt=1.10.2
14:23:15  Loaded profile from /Users/me/.dbt/profiles.yml
14:23:15  target: dev
14:23:15  threads: 4
14:23:15  path: ./jaffle_shop.duckdb

Здесь видно: какой target, сколько threads, какой профиль. Если ты случайно запустил с --target prod в local-проекте, это будет видно в первых строках.


Лог — не место для секретов

В большинстве случаев dbt не пишет креды в лог — даже в debug mode. Но env_var-значения могут попасть в SQL, если ты их встраиваешь руками. Не пиши '{{ env_var("DB_PASSWORD") }}' прямо в модели — иди через secure secrets manager.

logs/ в .gitignore — стандартно. Никогда не комить логи.


Попробуй сам

  1. Запусти dbt --debug run --select customers. Сравни в терминале вывод с tail -100 logs/dbt.log. Найди SQL-statement, который dbt отправил.

  2. Намеренно сломай модель — добавь SELECT * FORM customers (опечатка FORM). Запусти dbt run --select customers. Открой лог, найди Error, найди исходный SQL.

  3. Запусти grep -E "ERROR|FAIL|warn" logs/dbt.log | tail -20. Просмотри последние warning-и.

  4. Установи log_format: json в dbt_project.yml. Запусти любую команду. Открой лог — он теперь JSON. Попробуй jq '.data.msg' logs/dbt.log | head -20.

  5. Найди свой самый медленный SQL: grep "SQL status: SUCCESS" logs/dbt.log | sort -k <last>.


Чек-лист

  • logs/dbt.log — полный verbose с каждым SQL и каждым шагом.
  • Уровни: error, warn, info, debug. В лог пишется всё, в терминале — info+.
  • dbt --debug run — вывести verbose в терминал в дополнение к логу.
  • При ошибке: ищи ERROR в логе, скролл вверх до SQL-statement, ещё выше — до Jinja-trace.
  • Полезные filter’ы: grep ERROR | tail, grep "model_name", tail -200.
  • Типичные паттерны: connection, ref to missing, source not declared, test failure, macro undefined, YAML, permission, OOM.
  • log_format: json для интеграций с Datadog/Splunk/Loki.
  • logs/ в .gitignore. Никогда не комить.
Observability: логи, метрики, lineage
Проверка знанийKnowledge check
У вас CI упал ночью. В терминале только 'Database Error in model orders'. Какие три места в logs/dbt.log тебе нужно последовательно посмотреть, и какую информацию каждое даст?
ОтветAnswer
1) tail -100 logs/dbt.log — последние строки, найти точный текст ошибки от warehouse (Database Error: ..., Catalog Error: ..., и т.д.). Это покажет, что warehouse сам сообщил — например, 'Column X does not exist' или 'Permission denied'. 2) grep -B 30 "Error in model orders" logs/dbt.log — 30 строк ВЫШЕ ошибки. Там лежит точный SQL-statement, который dbt отправил в warehouse (create or replace table ... as select ...). Можно скопировать этот SQL в SQL-клиент и прогнать вручную, чтобы воспроизвести. 3) grep "Running with" logs/dbt.log | tail -1 — параметры запуска: версия dbt, target, потоки. Полезно убедиться, что CI запустил тот target, что ожидалось (а не случайно prod в dev-job). Дополнительно: grep "Thread-" логи лога нужного треда — для параллельных запусков, чтобы видеть только конкретный воркер.

Проверьте понимание

Результат: 0 из 0
Концептуальный
Вопрос 1 из 6. По умолчанию терминал dbt показывает только info+ строки. Но logs/dbt.log хранит больше. Какие уровни и зачем читать debug-уровень?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 4