Two log streams per run, a color-coded console mirror, automatic numbered backups, and engine-notice routing that differs by platform. Everything a CI pipeline needs from a deploy tool.
By the SchemaSmith Team · Last reviewed
Good logs are the difference between a quick diagnosis and a long night.
All CLI tools use Apache Log4Net. Each tool ships with an embedded Log4Net configuration that is loaded automatically at startup — there's nothing to configure.
Each tool writes two log files per run. SchemaQuench writes a third, SchemaQuench - Failures.log, which stays empty unless a run has failures; see Failure triage roll-up. Every SchemaQuench run also writes a deployment summary report, by default to the same directory, as a matched pair, SchemaQuench - Summary.json and SchemaQuench - Summary.md, carrying the run's outcome, exit code, duration, per-target results, and verified object counts. Read the report format on your platform's SchemaQuench reference: SQL Server, PostgreSQL, MySQL, or MariaDB.
| Tool | Progress log | Error log |
|---|---|---|
| SchemaQuench | SchemaQuench - Progress.log |
SchemaQuench - Errors.log |
| SchemaTongs | SchemaTongs - Progress.log |
SchemaTongs - Errors.log |
| DataTongs | DataTongs - Progress.log |
DataTongs - Errors.log |
| SchemaShears | SchemaShears - Progress.log |
SchemaShears - Errors.log |
The progress log receives all informational output: the startup banner, active configuration, per-object progress, and completion status. Everything written to the progress log also appears on the console in real time.
The error log receives only error-level entries (such as SQL execution errors) and does not echo to the console.
SQL Server: the errors log is populated — each fault lands there with its message and the line number (at Line: N), in addition to the progress log. It's the fastest place to read what went wrong.
PostgreSQL, MySQL, and MariaDB: the errors log is empty. The fault detail — including the SQLSTATE on PostgreSQL — is in the progress log only. Don't go looking in the errors log; it won't be there.
Both the progress and error logs are overwritten at the start of each run. Previous runs are preserved through the backup rotation described below.
The console is a live mirror of the progress log, not a separate channel. Watching the console while a quench runs lets you follow startup, per-object progress, and completion in real time without tailing a file. Log4Net colorizes the console stream by level so trouble catches your eye the moment it appears:
| Level | Console color |
|---|---|
| Informational | Green |
| Warning | Yellow |
| Error | Red |
When a script fails, you see a short error summary on the console and in the progress
log (red, so it's hard to miss): the failing script path and the engine's error
message, along with a Resolved SQL written to: pointer to the
resolved-SQL artifact. That's enough to know what failed and where to look.
The full token-expanded SQL the server rejected is written to a
separate, re-runnable .sql artifact file rather than dumped into the
console or progress stream; the progress log points you to it with a
Resolved SQL written to: line. The full detail
— the exception line numbers and the complete SQL batches SchemaSmith
submitted — lands in the error log only, so the console and progress stream
don't drown in multi-KB failed-batch text during a rough deployment. When a run
fails, the progress log tells you what broke, the error log tells you
exactly what SQL was sent when it broke, and the artifact is the copy you
re-run. See Working a failed deployment to trace
one end to end.
On multi-target runs the log stays greppable: every schema-template iteration prefixes
its lines with [Schema: <name>], and a many-database fan-out uses
[server].[database]. Filter the progress log to one prefix to isolate a
single tenant's sequence end to end.
CI agents that capture stdout get the progress stream for free. If your pipeline step only saves stdout, you still have a readable transcript of successes and error summaries; archive the error log separately to keep the failed-batch detail.
By default, logs are written to the tool's executable directory. Override this with
--LogPath:
SchemaQuench --LogPath:C:\Logs
SchemaTongs --LogPath:C:\Logs\schemasmith
DataTongs --LogPath:D:\BuildLogs
SchemaQuench --LogPath:/var/log/schemasmith
SchemaTongs --LogPath:/var/log/schemasmith
DataTongs --LogPath:/var/log/buildlogs
Immediately after loading configuration, every tool logs the resolved command-line switches followed by its complete active configuration to the progress log. This includes the tool name and version number, every switch passed on the command line, and every configuration key and its value (with sensitive values masked). This makes it straightforward to verify both the effective command line and the settings that were in effect for any given run.
Sensitive switches are scrubbed the same way configuration values are — a
--Target__Password=... or --ConnectionString=... renders its
value as *** while the switch name still prints, honoring the same
LogHygiene rules.
Any value whose name matches the built-in sensitive-name set — Password,
Pwd, Secret, ApiKey, Token,
ConnectionString, Credential (case-insensitive, substring match)
— is replaced with *** while its name still prints, so you can confirm
the setting exists without exposing it. An embedded Password= /
Pwd= inside a connection-string value is stripped even when the surrounding
name isn't sensitive. All other values are logged as-is.
You can tune what gets scrubbed — add exact names or patterns, or opt a false
positive back out — and suppress token logging entirely via the LogHygiene
settings block. See
Sensitive value masking
in the configuration reference.
2026-03-06 09:14:01,234 - SchemaQuench
2026-03-06 09:14:01,235 - Version: 5.2.1.0
2026-03-06 09:14:01,236 - Command line:
2026-03-06 09:14:01,237 - ConfigFile: production.json
2026-03-06 09:14:01,238 - Target__Server: sql-prod-01
2026-03-06 09:14:01,239 - Target__Password: ***
2026-03-06 09:14:01,240 - Configuration:
2026-03-06 09:14:01,241 - Server: sql-prod-01
2026-03-06 09:14:01,242 - User: schemasmith
2026-03-06 09:14:01,243 - Password: ***
2026-03-06 09:14:01,244 - VerboseLogging: false
When a tool finishes (whether successfully or after an error), it backs up its log files before the process exits:
--LogPath value, or the executable directory if not specified).<ToolName>.0001. If that directory already exists, it increments: .0002, .0003, and so on.<ToolName> - *.log into the new subdirectory.The base log files in the log directory are not deleted after backup. Each run overwrites the base files and writes a copy into a new numbered subdirectory. This preserves the history of every run while keeping the base files current with the latest.
Example after three SchemaQuench runs:
C:\Tools\
SchemaQuench - Progress.log (latest run)
SchemaQuench - Errors.log (latest run)
SchemaQuench.0001\ (first run backup)
SchemaQuench.0002\ (second run backup)
SchemaQuench.0003\ (third run backup)
When a run finishes with one or more failures, SchemaQuench writes a third log alongside the progress and error logs — SchemaQuench - Failures.log — that answers which targets failed at a glance. A deployment fans out to many (server, database, schema) targets in parallel and shares one interleaved progress log; the roll-up pulls every failure back into one consolidated, phase-grouped list, so a single failure among many parallel targets isn't buried in one stream.
Each entry names the failed scope, along with the engine error, the Resolved SQL written to: artifact path, and a captured tail of the lines leading up to it. A scope is one of:
[server].[database] [Schema: <name>]Before / After product script — [server]Validate phaseA loud *** FAILED banner also marks each failure live in the progress stream, so you can grep *** FAILED to jump straight to the failed scopes; the roll-up block echoes to the console at the end of the run.
A failed user script reads with the same detail as a mechanical failure. The Error: line carries the specific per-script error — Unable to quench '<path>': <error>, with a (+N more) tail when several scripts in the scope failed — and the Debug SQL: line points at that script's resolved-SQL artifact, so the roll-up sends you straight to the offending script and the SQL it produced. From there, working a failed deployment covers reading the artifact and reproducing the error.
The roll-up is always on and adds nothing to a clean run — no banner, no roll-up block, an empty Failures.log. FailureContextLines controls how many lines of lead-up context each failure captures. The default is 25; set it to 0 to disable context capture entirely, and the roll-up still lists the failed scopes and their errors.
SchemaQuench --FailureContextLines=40
A failed quench leaves two things for you: a log entry telling you what broke, and a .sql artifact file holding the exact SQL that the server rejected. Here's how to move from "deployment failed" to "I know what's wrong and I can fix it."
SchemaQuench - Progress.log) and search for Resolved SQL written to:. Every script surface — user and migration scripts, generated quench DDL (table, index, foreign-key, view, and table-JSON steps), product-level Before/After scripts, validation scripts, and data-delivery merges — reports its failure the same way, so one search string finds every kind of failure in the log.
Unable to quench 'Before/01-seed.sql': Invalid column name 'Region'.
Resolved SQL written to: C:\logs\SchemaQuench - Failed 01-seed prod.App.sql
Artifacts land in the ArtifactPath directory (default: the working directory you ran SchemaQuench from), not the log directory — deliberately, so zipping your logs for a support ticket doesn't sweep up raw SQL that may contain expanded sensitive values..sql file: a comment header identifying the server, database, schema, and which script failed, followed by every batch the engine sent, separated by GO. The batch SchemaQuench attempted last is marked:
-- >>> FAILING BATCH (#2) >>>
ALTER TABLE [dbo].[Orders] ADD [Region] NVARCHAR(50) NOT NULL ...
GO
The marker is a best-effort hint — the engine marks the last batch it attempted, usually (but not always) the one that caused the error. The values are real and fully expanded (tokens resolved, connection-string parameters substituted), which is exactly what you need to reproduce the failure.{{Token}} instead of the expanded value — a misspelled token, a token out of scope for this script's slot (a {{SchemaName}} token in a product-level script, say), or a token that was never defined.ScrubArtifacts: true before re-running: the artifact then redacts sensitive token values and inline connection-string passwords into a shareable variant. See Failure artifacts in the configuration reference.When SchemaQuench runs one of its generated procedures against your target database, it dumps the exact SQL it sent to a companion .sql file — the same artifact the Resolved SQL written to: line points at. Generated procedures cover missing tables and columns, modified tables, indexes, foreign keys, materialized views, indexed views, and the table-JSON parse step. Debug files follow the pattern SchemaQuench - <operation> <server>.<database>.sql:
SchemaQuench - Quench Missing Tables And Columns prod.NW.sql
SchemaQuench - Quench Modified Tables prod.NW.sql
SchemaQuench - Quench Indexes prod.NW.sql
SchemaQuench - Quench Foreign Keys prod.NW.sql
SchemaQuench - Quench Materialized Views prod.NW.sql
SchemaQuench - Quench Indexed Views prod.NW.sql
SchemaQuench - Parse Table Json prod.NW.sql
Each run overwrites the debug files for the operations it actually performed. Operations that don't apply to your platform (indexed views on PostgreSQL, materialized views on MySQL) produce no file. Debug files land in the ArtifactPath directory (default: current working directory) — --LogPath controls the progress and error logs, not artifact or debug SQL.
SchemaSmith surfaces the database engine's informational output — notices, prints, and server-side status messages — into the progress log so you can see what the engine is telling you. The wiring differs per platform because each driver exposes that stream differently.
PRINT output and severity-10-or-lower errors arrive through the
InfoMessage event. By default SchemaSmith promotes only
severity-above-10 errors and RAISERROR ... WITH STATE 100
notifications to the progress log; set VerboseLogging: true to
include every PRINT and informational message.
RAISE NOTICE and RAISE WARNING output arrives
through the Npgsql Notice event and lands in the progress log.
SchemaSmith filters out the "... does not exist, skipping" and
"... already exists, skipping" notices that
DROP ... IF EXISTS and CREATE ... IF NOT EXISTS
produce during normal runs, so your log stays readable.
The MySQL connector doesn't fire info-message events for long-running stored
procedures, so SchemaSmith uses a table-based status channel. A
SchemaSmith_StatusMessages table in the target database (created
automatically during kindling) holds per-session progress rows; the generated
quench procedures INSERT into it as they work, and a background
poller on a separate connection reads the new rows every 200ms and writes them
to the progress log. SessionId is scoped to
CONNECTION_ID() so concurrent runs don't cross-talk, and the
monitor deletes its rows on shutdown. There's no VerboseLogging
dial on MySQL — what you see is whatever the procedures chose to publish.
The MySQL connector, which MariaDB also uses, doesn't fire info-message events for long-running stored
procedures, so SchemaSmith uses a table-based status channel. A
SchemaSmith_StatusMessages table in the target database (created
automatically during kindling) holds per-session progress rows; the generated
quench procedures INSERT into it as they work, and a background
poller on a separate connection reads the new rows every 200ms and writes them
to the progress log. SessionId is scoped to
CONNECTION_ID() so concurrent runs don't cross-talk, and the
monitor deletes its rows on shutdown. There's no VerboseLogging
dial on MariaDB — what you see is whatever the procedures chose to publish.
VerboseLogging is a SchemaQuench setting and applies only to SQL Server's
InfoMessage stream. The PostgreSQL, MySQL, and MariaDB paths already behave the way
VerboseLogging: true behaves on SQL Server — SchemaSmith surfaces
every engine-side notice (PostgreSQL) or procedure-emitted status message (MySQL and MariaDB) by default.
The same logical problem — an orphaned foreign key, a duplicate value, a null in a required column — surfaces differently on each engine. The error text differs, the numeric code differs, and even which log file carries the detail differs. Use this table to recognize the same fault across platforms.
SQL Server usually prints the message (the number isn't always shown); PostgreSQL prints the SQLSTATE literally; MySQL and MariaDB print the classic MySQL-family error message.
| Failure | SQL Server | PostgreSQL | MySQL | MariaDB |
|---|---|---|---|---|
| Foreign-key violation (orphan) | 547 | 23503 | 1452 | 1452 |
| NOT NULL violation | 515 | 23502 | 1048 | 1048 |
| Duplicate / unique-key | 1505 (index build) / 2601 / 2627 | 23505 | 1062 | 1062 |
| String or binary truncation | 8152 | 22001 | 1406 | 1406 |
| Type / conversion mismatch | 245 / 8115 | 22P02 | 1366 | 1366 |
| Deadlock (retried automatically) | 1205 | 40P01 | — (message-matched) | — (message-matched) |
MariaDB reports through the same channel as MySQL — the SchemaSmith_StatusMessages sidecar table polled on a second connection — and uses MySQL-compatible error codes.
For the hands-on method — locating the failing phase, reading the artifact it leaves behind, and choosing a recovery — see working a failed deployment.
SchemaSmith detects the deadlock code and re-runs the operation with backoff, so a transient lock collision resolves itself rather than failing the deploy.
| Code | Condition | Recommended action |
|---|---|---|
0 |
Normal completion | None — the operation succeeded. |
2 |
One or more database quenches failed (SchemaQuench only) | Check the progress and error logs for details on which databases failed and why. Fix the failing scripts and re-run. |
3 |
Unhandled exception | An unexpected error occurred. The exception is logged to both the progress and error logs before exit. Report the error with the log contents if the cause isn't obvious. |
4 |
Log backup failure | The tool completed its main work but couldn't back up the log files. Check directory permissions and disk space in the log directory. The base log files may still be readable even though the backup failed. |
Pre-flight checks that fail before any deployment — a --TestConnection
failure, a server below the product's MinimumVersion floor, or a
--PreviewTargets run that finds a required template with zero targets
— also exit 2, so a readiness gate in CI can treat any non-zero exit
the same way.