

How to log errors in sql server stored procedures: Best Practices, Error Handling Patterns, TRY…CATCH, RAISERROR, and THROW
Use TRY…CATCH blocks and log errors to a table or external store.
In this guide, you’ll get a practical, battle-tested approach to error handling in SQL Server stored procedures. We’ll cover patterns, concrete code you can copy, performance considerations, and how to monitor and alert on failures. Expect a mix of step-by-step instructions, code samples, checklists, and real‑world tips to keep your SQL layer reliable and observable.
Useful URLs and Resources text only:
– Microsoft Docs – Error Handling Transact-SQL
– Microsoft Docs – TRY…CATCH Transact-SQL
– Microsoft Docs – THROW Transact-SQL
– SQL Server Logging Best Practices
– Stack Overflow – Error Handling in SQL Server
– Simple Talk – Centralized Logging in SQL Server
Table of contents
– Why error logging matters
– Core concepts you’ll use
– Logging targets: table vs file vs external services
– Designing an error log table
– Implementing robust error handling in stored procedures
– Centralized vs local logging strategies
– Performance and security considerations
– Testing and validating error handling
– Real-world patterns and recipes
– Monitoring, alerts, and dashboards
Why error logging matters
Error logging isn’t just about catching bugs. it’s about making incidents diagnosable and preventing silent failures from cascading. When stored procedures fail in production, you want:
– Immediate visibility: who failed, when, and why
– Enough context to reproduce the failure locally
– A safe mechanism to prevent data corruption or inconsistent states
– A strategy to alert the right teammates without spamming on every error
Beyond that, mature teams implement a centralized approach so that a single error feed can be used across multiple databases and environments.
Core concepts you’ll use
– TRY…CATCH: The core pattern for handling errors in SQL Server.
– ERROR_NUMBER, ERROR_SEVERITY, ERROR_STATE, ERROR_PROCEDURE, ERROR_MESSAGE, ERROR_LINE: Useful error context inside CATCH.
– THROW vs RAISERROR: THROW is the modern, simpler option that preserves error stacks. RAISERROR is older but still used in some legacy code.
– Logging target: a dedicated ErrorLog table, a file, or an external system Azure Monitor/Log Analytics, SIEM, etc..
– Transactions and error handling: using XACT_STATE to decide whether to commit or rollback.
Logging targets: table vs file vs external services
– Table-based logging recommended for most SQL Server setups:
– Pros: Easy to query, joins with business data, durable, supports indexing and retention policies.
– Cons: Requires careful schema design and maintenance. may add some overhead.
– File-based logging:
– Pros: Simple append-only logs. good for quick debugging on a single server.
– Cons: Harder to query. not centralized. potential disk pressure.
– External services Azure Monitor, Log Analytics, SIEM:
– Pros: Centralized across environments. powerful alerting and dashboards. scalable.
– Cons: Requires network connectivity and cost. more complex setup.
Recommendation: start with a centralized ErrorLog table and a lightweight external log sink for long-term analytics.
Designing an error log table
A solid error log table should capture enough context to diagnose issues without exposing sensitive data. Here’s a pragmatic starter schema:
CREATE TABLE dbo.ErrorLog
LogId BIGINT IDENTITY1,1 PRIMARY KEY,
ErrorNumber INT NULL,
ErrorSeverity INT NULL,
ErrorState INT NULL,
ErrorProcedure SYSNAME NULL,
ErrorMessage NVARCHARMAX NULL,
ErrorLine INT NULL,
LoggedAt DATETIME23 NOT NULL DEFAULT SYSUTCDATETIME,
ServerName SYSNAME NULL,
DatabaseName SYSNAME NULL,
UserName SYSNAME NULL,
ApplicationName SYSNAME NULL,
AdditionalInfo NVARCHARMAX NULL
.
Tips:
– Use NVARCHARMAX for ErrorMessage to avoid truncation.
– Include LoggedAt, DatabaseName, ServerName, UserName, and ApplicationName for context.
– Consider partitioning by year or by a critical tenant if you’ve multi-tenant apps.
– Mask or redact sensitive data in AdditionalInfo if needed.
Implementing robust error handling in stored procedures
Here’s a practical pattern you can adapt. It demonstrates logging inside a TRY…CATCH and optionally rethrowing with THROW.
1 Basic pattern: log and rethrow
BEGIN TRY
— Your main logic here
INSERT INTO dbo.Sales… VALUES ….
— other statements
END TRY
BEGIN CATCH
— Log the error
INSERT INTO dbo.ErrorLog
ErrorNumber, ErrorSeverity, ErrorState, ErrorProcedure, ErrorMessage,
ErrorLine, LoggedAt, ServerName, DatabaseName, UserName, ApplicationName
VALUES
ERROR_NUMBER,
ERROR_SEVERITY,
ERROR_STATE,
ERROR_PROCEDURE,
ERROR_MESSAGE,
ERROR_LINE,
SYSUTCDATETIME,
CAST SERVERPROPERTY’ServerName’ AS SYSNAME,
DB_NAME,
ORIGINAL_LOGIN,
APP_NAME
.
— Optional: enrich the error and rethrow
THROW. — Re-raises the original error with the same stack
END CATCH.
2 Logging with a helper stored procedure centralized
CREATE PROCEDURE dbo.usp_LogError
@ErrorNumber INT,
@ErrorSeverity INT,
@ErrorState INT,
@ErrorProcedure SYSNAME,
@ErrorMessage NVARCHARMAX,
@ErrorLine INT,
@LoggedAt DATETIME23,
@ServerName SYSNAME,
@DatabaseName SYSNAME,
@UserName SYSNAME,
@ApplicationName SYSNAME,
@AdditionalInfo NVARCHARMAX = NULL
AS
BEGIN
INSERT INTO dbo.ErrorLog …
VALUES …. — map parameters to columns
END.
GO
Then in your main procedure:
— main work
EXEC dbo.usp_LogError
@ErrorNumber = ERROR_NUMBER,
@ErrorSeverity = ERROR_SEVERITY,
@ErrorState = ERROR_STATE,
@ErrorProcedure = ERROR_PROCEDURE,
@ErrorMessage = ERROR_MESSAGE,
@ErrorLine = ERROR_LINE,
@LoggedAt = SYSUTCDATETIME,
@ServerName = CAST SERVERPROPERTY’ServerName’ AS SYSNAME,
@DatabaseName = DB_NAME,
@UserName = ORIGINAL_LOGIN,
@ApplicationName = APP_NAME,
@AdditionalInfo = NULL.
THROW.
3 Handling nested calls and transactions
If your procedure calls other procedures, you can:
– Log at the top-level procedure only, to avoid duplicate logs.
– Or log in every layer if you need traceability across calls.
– Use XACT_STATE to decide whether you can rollback safely within CATCH.
BEGIN TRANSACTION.
— work that may cause errors
EXEC dbo.usp_DoWork.
COMMIT TRANSACTION.
IF XACT_STATE <> 0
ROLLBACK TRANSACTION.
EXEC dbo.usp_LogError …. — as above
4 Logging optional data and preventing log failures
– Wrap logging in its own TRY…CATCH to avoid log failures aborting business logic.
– Consider storing large error messages in a separate table or truncating with care.
– If you must log sensitive data, apply masking rules or use a secure logging channel.
Centralized vs local logging strategies
– Centralized logging:
– Use a single ErrorLog table or a central logging stored procedure that all databases call.
– Pros: consistent structure, easier monitoring, cleaner analytics.
– Cons: can be a single point of failure. require cross-database permissions.
– Local logging:
– Each database or schema maintains its own error log.
– Pros: simpler permissions, autonomy, offline resilience.
– Cons: harder to aggregate across environments.
Hybrid approach:
– Core errors are logged centrally, while domain-specific messages are stored locally with a reference to the central log entry.
Performance and security considerations
– Performance:
– Logging can add overhead. batch or asynchronous logging e.g., queue into a SQL Server Agent job or an asynchronous service can help.
– Use indexed columns on ErrorLog ErrorNumber, LoggedAt, DatabaseName, ServerName for faster querying.
– Security:
– Do not log password hashes, connection strings, or PII in plain text.
– Implement least-privilege permissions for the logging mechanism.
– If you expose logs to developers, consider row-level security or masked views.
– Reliability:
– Ensure log table has proper retention policies to prevent unbounded growth.
– Consider archiving old logs to a separate database or storage.
Testing and validating error handling
– Create a controlled set of failure scenarios deadlocks, constraint violations, timeouts.
– Use a test suite to simulate failures in different layers:
– Database-level errors
– Application-level errors
– Network-related errors
– Verify that:
– Errors are captured in ErrorLog with complete context
– Transactions roll back as expected when necessary
– Throw rethrows the original error or a sanitized version, if needed
– Use DBCC DROPCLEANBUFFERS to simulate realistic caching behavior during tests.
Real-world patterns and recipes
– Pattern A: Minimal logging with added context
– Log key details, but avoid verbose data. rely on application layers to enrich visuals or UI.
– Pattern B: Full-stack logging with correlation IDs
– Generate a CorrelationId at the start of a user request and thread it through all procedures. include in ErrorLog for traceability.
– Pattern C: Conditional logging
– Only log errors above a certain severity or those that occur in production vs development.
– Pattern D: Async/log batching
– Write to an in-memory queue and flush to ErrorLog periodically or via a background job to reduce latency impact.
Code snippet: correlation ID example
— At the start of a request
DECLARE @CorrelationId UNIQUEIDENTIFIER = NEWSEQUENTIALID.
— Perform operations, pass @CorrelationId through to all downstream calls
INSERT INTO dbo.ErrorLog ErrorNumber, …, AdditionalInfo
VALUES ERROR_NUMBER, …, CAST@CorrelationId AS NVARCHAR36.
Monitoring, alerts, and dashboards
– Alerts:
– Trigger when ErrorLog count exceeds a threshold in a given period.
– Alert on spikes in ErrorSeverity 20+ or repeated errors from the same procedure.
– Dashboards:
– Track top failing procedures, failure counts by database, and peak error times.
– Show mean time to detect and mean time to recovery if you have incident data.
– Observability tips:
– Include a human-friendly ErrorMessage prefix and a structured JSON field for AdditionalInfo if supported.
Practical step-by-step guide to implement now
1 Create an error log table or confirm you have one.
2 Create a centralized logging procedure optional but recommended.
3 Add TRY…CATCH to your stored procedures and call the logging routine.
4 Add optional correlation IDs and enrichment data.
5 Set up a light-weight monitoring/alerting rule on ErrorLog.
6 Review and adjust retention policies and security controls.
7 Test thoroughly with realistic failure scenarios.
Code example: complete integration
— Step 1: ensure log table exists
CREATE TABLE IF NOT EXISTS dbo.ErrorLog
CorrelationId UNIQUEIDENTIFIER NULL,
— Step 2: centralized logging procedure
CREATE OR ALTER PROCEDURE dbo.usp_LogError
@CorrelationId UNIQUEIDENTIFIER,
INSERT INTO dbo.ErrorLog
ErrorNumber, ErrorSeverity, ErrorState, ErrorProcedure, ErrorMessage, ErrorLine,
LoggedAt, ServerName, DatabaseName, UserName, ApplicationName, CorrelationId, AdditionalInfo
VALUES
@ErrorNumber, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorMessage, @ErrorLine,
@LoggedAt, @ServerName, @DatabaseName, @UserName, @ApplicationName, @CorrelationId, @AdditionalInfo.
— Step 3: sample stored procedure with logging
CREATE OR ALTER PROCEDURE dbo.usp_SampleProcedure
@CorrelationId UNIQUEIDENTIFIER = NULL
SET NOCOUNT ON.
BEGIN TRY
— Simulate some work
INSERT INTO dbo.Sales … VALUES ….
END TRY
BEGIN CATCH
IF @CorrelationId IS NULL
SET @CorrelationId = NEWSEQUENTIALID.
EXEC dbo.usp_LogError
@CorrelationId = @CorrelationId,
THROW.
END CATCH.
Note: adjust the example to your actual schema and environment. The goal is to keep errors observable, traceable, and fast to diagnose.
Frequently Asked Questions
# What is TRY…CATCH and why use it?
TRY…CATCH is SQL Server’s structure for handling runtime errors. It lets you run code and, if something goes wrong, jump to CATCH to log the error, clean up, and optionally rethrow or translate the error for the caller.
# How do I log errors to a table?
Create a dedicated ErrorLog table as shown and write error details inside the CATCH block using ERROR_NUMBER, ERROR_MESSAGE, and related functions. Consider a centralized logging stored procedure to standardize the format.
# Should I log every error or only critical ones?
Start by logging all unexpected errors ERROR_SEVERITY >= 11. You can add conditional logging for less severe errors, but ensure you don’t flood logs with benign messages.
# When should I use RAISERROR vs THROW?
THROW is the modern option in SQL Server 2012+. It preserves the original error and stack. RAISERROR is older and can be used for custom messages, but THROW is generally preferred for new code.
# Can I log errors without impacting performance?
Yes. Use lightweight logging on the hot path minimal columns, indexed log table. For high-volume systems, consider asynchronous logging or buffering logs to a queue for later processing.
# How do I handle nested procedure calls?
Log at the top level to avoid duplicate entries, or implement a centralized logger that’s idempotent. Use TRY…CATCH in each layer if you need granular visibility.
# How to protect sensitive data in logs?
Mask or redact sensitive fields in ErrorMessage or AdditionalInfo. Consider storing only non-sensitive metadata and keep sensitive details in secure systems with restricted access.
# How to log errors in Azure SQL vs on-prem SQL Server?
The approach is the same, but you might extend with Azure Monitor, Log Analytics, or Event Hubs for centralized observability. Ensure network and security configurations allow the logging path.
# How do I test error handling?
Write unit tests or integration tests that deliberately cause failures constraint violations, timeouts, deadlocks. Verify that errors land in ErrorLog with correct context and that transactions rollback correctly when needed.
# How can I automate alerts from error logs?
Hook ErrorLog updates to a monitoring tool or SQL Server Agent job that runs queries like “errors in last hour” and triggers alerts if thresholds are exceeded. Use dashboards to visualize trends over time.
# What about performance tuning for error logging?
Benchmark logging latency and throughput, especially under peak load. If you see contention on the ErrorLog table, add a dedicated filegroup or use partitioning and indexing strategies. Consider asynchronous pipelines for heavy workloads.
# How can I migrate existing error logging to this pattern?
Plan a staged rollout:
– Add the ErrorLog table and optional central logger.
– Refactor procedures to call the logger in CATCH blocks.
– Run parallel validation: compare pre- and post-logging behavior.
– Roll out gradually to avoid large, risky changes in production.
# How should I handle sensitive or regulatory data in logs?
Implement data masking or redaction policies. Store only non-sensitive metadata timestamps, error codes, procedures and route sensitive data to secure storage with proper access controls.
# Can I log errors for batch jobs or ETL processes?
Yes. Treat batch jobs like a single logical unit and capture error metadata with a correlation ID. Centralize logs so you can correlate job failures across multiple steps.
# Is logging required for performance-critical procedures?
Not always. Use a lightweight logging approach for hot paths, and defer deeper logging to off-peak windows or background processes if needed.
If you’d like more concrete examples tailored to your environment Azure SQL, on-prem, specific ER diagrams, or integration with a SIEM, tell me your stack and I’ll tailor the patterns and code snippets for you.
Sources:
Edge free download for windows 7 VPN safety guide, setup, and best alternatives in 2025
科学上网配置:VPN、代理与加密通道全解析与实操指南,快速稳定跨区域访问的技巧
上外网的完整指南:使用VPN翻墙、隐私保护与高速连接 How to run ftp server in windows a step by step guide for beginners: Setup, Security, and Best Practices