Table of Contents

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.log in 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.CreateTableCommand becomes y.v.c.CreateTableCommand
  • YndigoBlue.Velocity.Data.Sqlite.SqliteBuilder becomes y.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.

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.