Lightweight routes every interesting database event — connections opening and closing, statements
being prepared, parameters being bound, rows being fetched, warnings and errors — through a single
SqlLogger interface. Installing a logger is how you see what the library is actually sending to
the database, and it is the first tool to reach for when a query misbehaves on one DBMS but not
another.
There is exactly one active logger process-wide. It is retrieved with SqlLogger::GetLogger() and
replaced with SqlLogger::SetLogger().
| Logger | Use it for |
|---|---|
SqlLogger::NullLogger() |
Discards everything. The default — logging costs nothing unless you opt in. |
SqlLogger::StandardLogger() |
Warnings and errors only. A sensible production choice. |
SqlLogger::TraceLogger() |
Everything: prepare, bind, execute, fetch, connection lifecycle, scoped timers. Use while diagnosing. |
Switching to full tracing is a single call:
#include <Lightweight/Lightweight.hpp>
Light::SqlLogger::SetLogger(Light::SqlLogger::TraceLogger());The test binary exposes this as --trace-sql (see src/tests/Utils.hpp), which is the same switch
behind a command-line flag.
Both built-in loggers write through a MessageWriter sink, which you can replace to route messages
into your own logging framework instead of the default output:
auto& logger = Light::SqlLogger::TraceLogger();
logger.SetLoggingSink([](std::string message) {
MyApp::Log::Debug("{}", message);
});Call SetLoggingSink({}) to restore the default sink.
For anything beyond redirecting text — counting queries, feeding metrics, failing a test when an
unexpected statement runs — derive from SqlLogger::Null and override just the hooks you care
about. Null supplies empty implementations for all of them, so you only write what you need:
class QueryCounter final: public Light::SqlLogger::Null
{
public:
size_t executed = 0;
size_t rowsFetched = 0;
void OnExecute(std::string_view const& /*query*/) override { ++executed; }
void OnFetchRow() override { ++rowsFetched; }
};
QueryCounter counter;
Light::SqlLogger::SetLogger(counter);Deriving from SqlLogger directly is also possible, but almost every hook is pure virtual — you
would have to implement all of them.
The hooks available are:
| Hook | Fires when |
|---|---|
OnWarning / OnError |
A warning or an ODBC error is raised. Two OnError overloads: one for a bare SqlError, one for a full SqlErrorInfo with SQL state and native code. |
OnConnectionOpened / OnConnectionClosed |
A connection is established or torn down. |
OnConnectionIdle / OnConnectionReuse |
A pooled connection goes idle or is handed out again. |
OnExecuteDirect |
A statement is executed without preparation. |
OnPrepare / OnExecute / OnExecuteBatch |
A statement is prepared, executed, or executed as a batch. |
OnBind |
A parameter is bound. Only called when the logger was constructed with SupportBindLogging::Yes. |
OnFetchRow / OnFetchBlock / OnFetchEnd |
Rows are fetched one at a time, in a prefetch block, or the result set is exhausted. |
OnScopedTimerStart / OnScopedTimerStop |
A scoped timing region opens or closes. |
OnFetchBlock has a default no-op implementation, so an existing logger keeps compiling when
block-prefetch reporting is added.
SetLogger does not take ownership, so the logger must outlive its installation. In tests, where
loggers are swapped repeatedly, an RAII guard keeps this honest:
struct LoggerSwap
{
Light::SqlLogger* previous;
explicit LoggerSwap(Light::SqlLogger& replacement):
previous { &Light::SqlLogger::GetLogger() }
{
Light::SqlLogger::SetLogger(replacement);
}
~LoggerSwap() { Light::SqlLogger::SetLogger(*previous); }
LoggerSwap(LoggerSwap const&) = delete;
LoggerSwap& operator=(LoggerSwap const&) = delete;
};- statistics.md — aggregated counters, latency histograms and pool metrics. These
are collected by compile-time instrumentation rather than through the
SqlLoggerhooks above, so installing a collector does not displace your logger. - best-practices.md — when tracing points at a real performance problem.
- dbtool.md — the CLI, which reports migration and backup progress directly.