Skip to main content

How to See Every Statement MySQL Receives with the General Query Log

· 5 min read
Jagdish Kumawat
Founder @ Dewiride

Your application's logs say what it meant to send. MySQL's general query log says what arrived, including what the driver adds by itself, such as BEGIN, SET and savepoints. It's the tool I reach for when something about a transaction makes no sense. Use it for a moment, and turn it off again.

Prerequisites​

  • MySQL, and an administrator account. The examples use root on MySQL 9.7.4 in Docker. These settings affect the whole server, so do this on a development server. The log can hold sensitive values, and it grows quickly.

Step 1: Check the Settings​

mysql: root session
SELECT @@general_log, @@general_log_file, @@log_output;
Output
+---------------+---------------------------------+--------------+
| @@general_log | @@general_log_file | @@log_output |
+---------------+---------------------------------+--------------+
| 0 | /var/lib/mysql/aca0a83423c6.log | FILE |
+---------------+---------------------------------+--------------+
1 row in set (0.000 sec)

It's off, and it would write to a file. MySQL's query log page describes it as "a general record of what mysqld is doing."

Step 2: Send It to a Table​

A table is easier to read and filter than a file inside a container:

mysql: root session
SET GLOBAL log_output = 'TABLE';
TRUNCATE TABLE mysql.general_log;
SET GLOBAL general_log = ON;

Step 3: Do Something, Then Read It​

Run anything from another session, or from your application:

mysql: another session
START TRANSACTION;
UPDATE products SET ReorderLevel = 5 WHERE Id = 3;
COMMIT;

Turn it off first, and then look, so your own SELECT doesn't join the list:

mysql: root session
SET GLOBAL general_log = OFF;

SELECT TIME(event_time) AS time, thread_id, command_type,
CONVERT(argument USING utf8mb4) AS statement
FROM mysql.general_log
WHERE user_host LIKE 'inventory_app%'
ORDER BY event_time;
Output
+-----------------+-----------+--------------+---------------------------------------------------+
| time | thread_id | command_type | statement |
+-----------------+-----------+--------------+---------------------------------------------------+
| 05:27:35.258504 | 148 | Query | START TRANSACTION |
| 05:27:35.281217 | 148 | Query | UPDATE products SET ReorderLevel = 5 WHERE Id = 3 |
| 05:27:35.305636 | 148 | Query | COMMIT |
+-----------------+-----------+--------------+---------------------------------------------------+
3 rows in set (0.001 sec)

The mysql.general_log table showing START TRANSACTION, the UPDATE and COMMIT from one connection, with their times.

CONVERT is there because argument is stored as binary and would otherwise print as hexadecimal. thread_id is the connection, so you can tell two clients apart.

Here's what it shows for a real application. EF Core's SaveChanges sent this, on Oracle's provider, and none of those transaction statements were in my C#:

Output (the statements an insert through EF Core sends)
+----------------------------------------------------------------------------+
| statement |
+----------------------------------------------------------------------------+
| SET TRANSACTION ISOLATION LEVEL REPEATABLE READ |
| BEGIN |
| INSERT INTO `categories` (`CreatedAtUtc`, `Name`) VALUES (timestamp('2026- |
| SELECT `Id` FROM `categories` WHERE ROW_COUNT() = 1 AND `Id` = LAST_INSERT |
| COMMIT |
+----------------------------------------------------------------------------+

Values appear in the log with the parameters already filled in, so this is the place to check what really reached the server.

Step 4: Or Read It from the File​

The file is the default, and the Docker image keeps it in the data folder. Here's its end, through the container:

mysql: root session
SET GLOBAL log_output = 'FILE';
SET GLOBAL general_log = ON;
Output (the last lines of the file)
Time Id Command Argument
2026-11-05T05:27:35.549986Z 148 Query SELECT COUNT(*) FROM categories
2026-11-05T05:27:35.569592Z 147 Query SET GLOBAL general_log = OFF

Step 5: Turn It Off​

mysql: root session
SET GLOBAL general_log = OFF;
SELECT @@general_log, @@log_output;
Output (the SELECT)
+---------------+--------------+
| @@general_log | @@log_output |
+---------------+--------------+
| 0 | FILE |
+---------------+--------------+
1 row in set (0.000 sec)

Common Mistakes​

MistakeWhat happensDo this instead
Leaving it onIt records every statement, and it grows without limitTurn it on, look, and turn it off
Sharing the logIt can contain data and even passwordsTreat it as sensitive
Reading argument without CONVERTThe statement prints as hexadecimalCONVERT(argument USING utf8mb4)
Reading it while it's onYour own query lands in itTurn it off first, or filter by user_host

Troubleshooting​

SymptomLikely causeFix
The table is emptylog_output is FILE, or the log is offCheck all three settings
Access denied; you need (at least one of) the SUPER or SYSTEM_VARIABLES_ADMINIt needs an administratorThe fix
SELECT from the table is slowIt has grown largeTRUNCATE TABLE mysql.general_log

FAQ​

How is this different from EF Core's SQL logs?​

EF Core logs what it asked for, with placeholders such as @p0. The general log shows what MySQL received, with the values, and what the driver added around it, such as SET TRANSACTION ISOLATION LEVEL. How to See the SQL That EF Core Sends to the Database covers the first one.

Conclusion​

SET GLOBAL log_output = 'TABLE', SET GLOBAL general_log = ON, do the thing, OFF, and read mysql.general_log. It's the most direct way to see what your program really sends, and it's a tool for a few minutes on a development server.

Additional Resources​

Stay Updated

Subscribe to our newsletter for the latest tutorials, tech insights, and developer news.

By subscribing, you agree to our privacy policy. Unsubscribe at any time.