Logging
Velocity uses log4net for all logging operations. This allows for flexible, configurable logging that can be tailored to your application's needs.
Configuration
Logging configuration is the responsibility of the application developer. While Velocity provides a default configuration, you should configure log4net according to your application's needs.
Default Configuration
If you do not specify a custom log4net configuration file, Velocity uses an internal default configuration:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<log4net debug="false" update="Merge" threshold="ALL">
<appender name="FileAppender" type="log4net.Appender.FileAppender">
<file value="velocity.log" />
<appendToFile value="false" />
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date{yyyy-MM-dd HH:mm:ss.fff zzz} [%-5level] %message%newline" />
</layout>
</appender>
<root>
<level value="DEBUG" />
<appender-ref ref="FileAppender" />
</root>
</log4net>
</configuration>
This default configuration:
- Logs to a file named
velocity.login the application's working directory - Uses the DEBUG log level (all messages including SQL statements)
- Does not append to existing log files (overwrites on each run)
- Includes timestamps with milliseconds and timezone information
Setting a Custom Log Configuration File
You can specify the path to your own log4net configuration file using the static LogConfigFile property on the Manager class:
Manager.LogConfigFile = "path/to/log.config";
This should be set before creating any Manager instances to ensure your configuration is used from the start.
Example Configuration File
A basic log4net configuration file for Velocity might look like this:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<log4net debug="false" update="Merge" threshold="ALL">
<appender name="FileAppender" type="log4net.Appender.FileAppender">
<file value="velocity.log" />
<appendToFile value="false" />
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="[%level] %date %logger{1} - %message%newline" />
</layout>
</appender>
<root>
<level value="DEBUG" />
<appender-ref ref="FileAppender" />
</root>
</log4net>
</configuration>
You can download this example log.config file to use as a starting point.
HTML Output
For easier log viewing with color-coded output, you can add an HTML appender alongside the standard file appender:
<appender name="HtmlFileAppender" type="log4net.Appender.FileAppender">
<file value="velocity.html" />
<appendToFile value="false" />
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
<layout type="log4net.Layout.XmlLayoutSchemaLog4j">
<locationInfo value="true" />
</layout>
</appender>
Then reference it in your root configuration:
<root>
<level value="DEBUG" />
<appender-ref ref="FileAppender" />
<appender-ref ref="HtmlFileAppender" />
</root>
This will produce both a .log file and an .html file that can be opened in a web browser for easier viewing.
Log Levels
Velocity uses the standard log4net logging levels. Understanding what each level logs will help you configure appropriate logging for your needs.
DEBUG
The DEBUG level is used for SQL statement logging and detailed diagnostic information. This is the most verbose logging level.
What gets logged at DEBUG level:
- All SQL statements executed against the database
- SQL statements are automatically formatted for readability
- Each SQL statement is prefixed with the abbreviated class name that executed it
Example DEBUG output:
[DEBUG] 2025-12-19 10:23:45.123 y.v.c.CreateTableCommand - CREATE TABLE "test_schema"."users" ( "id" INTEGER PRIMARY KEY AUTOINCREMENT, "username" TEXT NOT NULL, "email" TEXT )
[DEBUG] 2025-12-19 10:23:45.456 y.v.d.s.SqliteBuilder - ATTACH DATABASE 'C:\data\test_schema.db' AS "test_schema"
Note
SQL logging only occurs when the log level is set to DEBUG. If you want to see SQL statements, you must configure your log4net appender with <level value="DEBUG" />.
The class names in the logs use an abbreviated namespace format where the first three parts of the namespace are reduced to their first letter (lowercase), followed by the full class name. For example:
YndigoBlue.Velocity.Commands.CreateTableCommandbecomesy.v.c.CreateTableCommandYndigoBlue.Velocity.Data.Sqlite.SqliteBuilderbecomesy.v.d.s.SqliteBuilder
INFO
The INFO level logs informational messages about major operations and command execution.
What gets logged at INFO level:
- Start of command execution (e.g., "Creating table...", "Building schema...", "Adding record...")
- Completion of major operations
- Schema operations (create, update, drop)
- Data operations (add, update, delete, retrieve)
- Import/export operations
Example INFO output:
[INFO] 2025-12-19 10:23:45.123 y.v.c.CreateTableCommand - Creating table users
[INFO] 2025-12-19 10:23:45.789 y.v.c.BuildSchemaCommand - Building schema test_schema
[INFO] 2025-12-19 10:23:46.234 y.v.c.AddRecordCommand - Adding record to users
WARN
The WARN level logs warnings about operations that may not succeed or features that are unsupported.
What gets logged at WARN level:
- Attempts to create unsupported constraints (e.g., certain constraint types on specific databases)
- Attempts to create unsupported full-text indexes
- Attempts to create unsupported spatial indexes
- Operations that partially succeed but have limitations
Example WARN output:
[WARN] 2025-12-19 10:23:45.456 y.v.c.BuildSchemaCommand - Ignoring failure to create unsupported constraint.
[WARN] 2025-12-19 10:23:45.567 y.v.c.BuildSchemaCommand - Ignoring failure to create unsupported full text index.
ERROR
The ERROR level logs errors and exceptions that occur during operations.
What gets logged at ERROR level:
- Exception messages and stack traces
- Operation failures
- Database connection errors
- SQL execution errors
Example ERROR output:
[ERROR] 2025-12-19 10:23:45.789 y.v.c.CreateTableCommand - Table 'users' already exists
FATAL
The FATAL level is reserved for critical errors that prevent the application from continuing.
What gets logged at FATAL level:
- Unrecoverable errors
- Critical system failures
- Situations requiring immediate attention
Logger Names
Each log entry includes a logger name that indicates which Velocity class generated the log entry. The logger names use an abbreviated namespace format for brevity:
- Commands:
y.v.c.{CommandName}(e.g.,y.v.c.CreateTableCommand) - Builders:
y.v.d.{database}.{BuilderName}(e.g.,y.v.d.s.SqliteBuilder) - Adaptors:
y.v.d.{database}.{AdaptorName}(e.g.,y.v.d.s.SqliteAdaptor) - Manager:
y.v.e.Manager
This naming convention makes it easy to filter logs by component or database type when troubleshooting.
SQL Formatting
All SQL statements logged at the DEBUG level are automatically formatted for readability using the SQL Formatter library. This formatting:
- Adds appropriate indentation
- Places SQL keywords on separate lines where appropriate
- Formats column lists and values
- Makes complex queries easier to read
The formatting is database-aware and respects the SQL dialect of the database you're connected to.
Recommended Configuration
For development and troubleshooting:
<root>
<level value="DEBUG" />
<appender-ref ref="FileAppender" />
<appender-ref ref="HtmlFileAppender" />
</root>
For production environments:
<root>
<level value="INFO" />
<appender-ref ref="FileAppender" />
</root>
For production with warning awareness:
<root>
<level value="WARN" />
<appender-ref ref="FileAppender" />
</root>
Cross-Platform Considerations
The log4net pattern %logger{1} is used in all example configurations because it works consistently across Windows, Linux, and macOS. Some other patterns (like %type{1}) may not work correctly on all platforms.
The recommended conversion pattern is:
<conversionPattern value="[%level] %date %logger{1} - %message%newline" />
For more precise timestamps with timezone information:
<conversionPattern value="%date{yyyy-MM-dd HH:mm:ss.fff zzz} [%-5level] %message%newline" />
Performance Considerations
Logging at the DEBUG level with SQL statement logging enabled can impact performance, especially in high-throughput scenarios. The framework includes optimizations:
- Early exit checks to avoid formatting SQL when DEBUG is not enabled
- Lazy evaluation of log messages
- Efficient namespace abbreviation
However, for production environments handling high volumes of operations, consider using INFO or WARN level to reduce logging overhead.