If you've read older Drupal tutorials, blog posts, or contributed module code, you've probably seen watchdog() — a global function that used to be the only way to log anything in Drupal 7 and earlier. It's gone as a function, but the concept and the database table behind it live on. Understanding the connection matters because you'll still see the word "watchdog" everywhere in Drupal's own documentation and admin UI, even though you'll never call a function by that name again.
What you'll learn in this lesson
- What "watchdog" actually refers to today, versus in legacy Drupal 7 code
- How the modern
logger.factorysystem relates to thewatchdogdatabase table - How to query the log programmatically rather than through the admin UI
- Why you should never write directly to the
watchdogtable yourself
"Watchdog" is a table and a page name, not a function anymore
In Drupal 7, every log call went through a single global function: watchdog($type, $message, $variables, $severity). Drupal 8 replaced it entirely with the PSR-3-compliant logger system you saw in the last two lessons — but the underlying database table Drupal's core Database Logging module writes to is still literally named watchdog, and the admin page is still informally called "the watchdog log" even though its actual UI label is "Recent log messages."
// Drupal 7 (legacy — does not exist in Drupal 9/10/11):
watchdog('my_module', 'Something happened: %detail', ['%detail' => $detail]);
// Drupal 9/10/11 (current):
\Drupal::logger('my_module')->notice('Something happened: %detail', ['%detail' => $detail]);
Behind the scenes, that notice() call is dispatched through Drupal's event-based logging architecture to every registered logger implementation — by default, the core dblog module's logger, which is what actually inserts the row into the watchdog table.
Querying the log programmatically
Sometimes you need to read log entries from code — a health-check page, a custom report, a module that reacts to repeated errors. Query the watchdog table like any other database table, through Drupal's Database API:
$query = \Drupal::database()->select('watchdog', 'w')
->fields('w', ['wid', 'type', 'message', 'severity', 'timestamp'])
->condition('type', 'my_module')
->orderBy('timestamp', 'DESC')
->range(0, 10);
$recent_entries = $query->execute()->fetchAll();
watchdog for your own reporting is fine — it's a normal database table. But never INSERT into it directly. Always log through logger.factory/\Drupal::logger() instead, since that's the only path that also notifies any OTHER registered logger (syslog, a third-party error-tracking service, and so on) — a direct database insert would only ever reach this one table.See it for yourself
Visit Reports → Recent log messages (/admin/reports/dblog) and click into any individual log entry. The detail page it takes you to is reading from that same watchdog table — every column you see (Type, Date, Message, Severity, User, Operations) maps directly to a column this table actually stores.
Quick check: a colleague suggests writing a direct SQL
INSERT INTO watchdogstatement in a custom migration script "to save a log entry quickly." What's wrong with that? It bypasses Drupal's logger event system entirely — any other configured logger (syslog, a monitoring service, a custom logger your team added later) would never see that entry, since onlylogger.factorycalls get dispatched to every registered logger implementation.
Key takeaways
- The
watchdog()function is gone as of Drupal 8 — replaced entirely by the PSR-3logger.factorysystem. - The name "watchdog" survives as the actual database table name the core Database Logging module writes to, and as informal shorthand for the log report.
- Query the
watchdogtable with the normal Database API when you need to read log entries programmatically. - Never insert into
watchdogdirectly — always log through\Drupal::logger()/logger.factoryso every registered logger sees the entry, not just this one table.
Coming up next
Logging tells you what happened after the fact — but a genuinely reliable module needs automated tests that catch problems before they ever reach a log file. Next, we move into Drupal's testing tools, starting with functional tests that drive a real simulated browser against your code.