forked from hinterland/documentation
207 lines
9.5 KiB
Markdown
207 lines
9.5 KiB
Markdown
# PostgreSQL `RAISE` Levels Usage Specification
|
||
This document outlines the recommended guidelines for using RAISE statements in PL/pgSQL to ensure consistent logging, troubleshooting, and auditing for our system. Proper usage of these levels helps track anomalies such as potential money leaks, unauthorized goods movement, or general system irregularities.
|
||
## Overview
|
||
In PL/pgSQL, the following RAISE levels are supported (from lowest to highest severity):
|
||
|
||
1. `EXCEPTION`
|
||
2. `WARNING`
|
||
3. `NOTICE`
|
||
4. `INFO`
|
||
5. `LOG`
|
||
6. `DEBUG`
|
||
Each level signals the importance and urgency of the message. This specification provides recommendations on when and how to use each level in our system.
|
||
|
||
## Levels and Recomended Usage
|
||
### RAISE DEBUG
|
||
Use for:
|
||
- Development-time detailed insights
|
||
- Verbose debugging information not required in production logs
|
||
Characteristics:
|
||
- Messages are only logged if the client or server log level is set to `DEBUG` (rarely used in high-throughput production environments).
|
||
- Should not be used for permanent debugging statements in production code.
|
||
Example scenarios
|
||
- Printing variable states during development or testing.
|
||
- Showing performance metrics during local development.
|
||
### RAISE LOG
|
||
Use for:
|
||
- General runtime events or states that you want to capture in logs but do not necessarily indicate an issue.
|
||
- System events that might be important for auditing, but not so frequent as to clutter logs excessively.
|
||
Characteristics:
|
||
- Messages are always written to the database server log.
|
||
- Not displayed to the client by default.
|
||
- Useful for capturing normal but important operational details that help diagnose issues post-hoc (e.g., when you suspect a financial or inventory anomaly).
|
||
Example scenarios:
|
||
- Logging the start or completion of critical procedures or batch jobs.
|
||
- Tracking user actions that have significance for auditing (e.g., when a user triggers a large data export).
|
||
### RAISE INFO
|
||
Use for:
|
||
- Informational messages that can be visible to the client or used in logs for short-term review.
|
||
- Less critical than `NOTICE`, but should be used sparingly to avoid spamming logs.
|
||
Characteristics:
|
||
- Output is returned to the client application, if configured.
|
||
- Helpful for letting the calling application know about certain progress or status updates without rising to the level of a warning or notice.
|
||
Example scenarios:
|
||
- Indicating non-critical steps in a process workflow.
|
||
- Displaying occasional progress updates or user-facing messages in stored procedures.
|
||
### RAISE NOTICE
|
||
Use for:
|
||
- More prominent messages than `INFO` that are still not errors, but the user should be aware of them.
|
||
- Minor issues or important checkpoints that shouldn’t be ignored.
|
||
Characteristics:
|
||
- By default, displayed to the client application.
|
||
- Typical for informational messages that might require attention but do not threaten application correctness.
|
||
Example scenarios:
|
||
- Notifying about a less significant but noteworthy condition (e.g., partial data updates).
|
||
- Indicating a fallback or defaulting scenario (e.g., “No user preference set; default value used.”).
|
||
### RAISE WARNING
|
||
Use for:
|
||
- Situations that are unexpected and could lead to problems but not severe enough to stop processing.
|
||
- When you suspect potential data or logic issues that need investigating.
|
||
Characteristics:
|
||
- Alerts both the client and the server log.
|
||
- Useful to highlight potential pitfalls or partial failures that do not warrant transaction rollback.
|
||
Example scenarios:
|
||
- Suspicious data input that could lead to inaccurate results (e.g., a missing currency conversion rate).
|
||
- Unusual user behavior or system states that suggest deeper analysis (e.g., user attempts to withdraw beyond a set limit).
|
||
### RAISE EXCEPTION
|
||
Use for:
|
||
- Serious errors that require aborting the current transaction (or subtransaction) immediately.
|
||
- Preventing the system from executing with corrupted or invalid data that can cause irreparable harm.
|
||
Characteristics:
|
||
- Terminates the current transaction (unless caught by an inner block).
|
||
- Should be used sparingly, only when continuing the transaction is not safe or logical.
|
||
Example scenarios:
|
||
- Violations of critical business rules (e.g., negative inventory after a transaction).
|
||
- Database integrity violations or missing required system configurations.
|
||
- Security or permission checks failing in ways that demand an immediate stop.
|
||
## Usage Guidelines
|
||
1. Minimize Noise
|
||
- Overusing `NOTICE`, `WARNING`, or even `LOG` leads to “log spam” and can obscure real issues.
|
||
- Use `DEBUG` for fine-grained development logs and remove or wrap them in conditions for production.
|
||
2. Balance Audit Requirements
|
||
- Certain financial or goods movement operations might require a clear log trail. Use `LOG` or higher for these.
|
||
- For sensitive actions (like large withdrawals, inventory adjustments, refunds), `LOG` or `NOTICE` ensures they appear in logs for later auditing.
|
||
3. Use Contextual Messages
|
||
- Always include helpful context, such as function names, key variable values, or user identifiers.
|
||
- For critical operations, reference any relevant transaction or user session ID.
|
||
4. Avoid Using RAISE for Normal Flow
|
||
- Don’t rely on `RAISE` levels to control logic; prefer returning values or using conditional statements where possible.
|
||
5. Exceptions Should Be Exceptional
|
||
- Use `RAISE EXCEPTION` only when continuing is impossible or dangerous to data integrity.
|
||
## Detailed Examples
|
||
### Debugging Variable States in Development
|
||
```
|
||
CREATE OR REPLACE FUNCTION debug_example(p_input INT)
|
||
RETURNS VOID AS $$
|
||
DECLARE
|
||
v_computed_value INT;
|
||
BEGIN
|
||
v_computed_value := p_input * 42;
|
||
RAISE DEBUG 'Debug info: p_input=%, v_computed_value=%', p_input, v_computed_value;
|
||
-- Further logic...
|
||
END;
|
||
$$ LANGUAGE plpgsql;
|
||
```
|
||
- **Explanation**: Use `DEBUG` during development or testing to see intermediate values.
|
||
- **Production caution**: Remove or wrap these statements in `IF client_min_messages = 'DEBUG' THEN RAISE ...` to prevent production spam.
|
||
|
||
### Logging Significant Operations
|
||
```
|
||
CREATE OR REPLACE FUNCTION log_transaction(p_user_id INT, p_amount NUMERIC)
|
||
RETURNS VOID AS $$
|
||
BEGIN
|
||
-- Some logic to process the transaction...
|
||
|
||
RAISE LOG 'Transaction processed: user_id=%, amount=%', p_user_id, p_amount;
|
||
END;
|
||
$$ LANGUAGE plpgsql;
|
||
```
|
||
- **Explanation**: Captures the event in the server log, ensures it’s available for auditing.
|
||
- **Typical use**: Movement of funds, completion of important background jobs, etc.
|
||
|
||
## Providing Informational Messages
|
||
```
|
||
CREATE OR REPLACE FUNCTION info_example(p_param TEXT)
|
||
RETURNS VOID AS $$
|
||
BEGIN
|
||
IF p_param IS NULL THEN
|
||
RAISE INFO 'Parameter is null, proceeding with default setting.';
|
||
ELSE
|
||
RAISE INFO 'Parameter provided: %', p_param;
|
||
END IF;
|
||
|
||
-- Further logic...
|
||
END;
|
||
$$ LANGUAGE plpgsql;
|
||
```
|
||
- **Explanation**: `INFO` is user-facing but less critical than a notice or warning.
|
||
- **Typical use**: Indicate intermediate or final results that might be useful to the end user or client application.
|
||
|
||
## Notice for Important Checkpoints
|
||
```
|
||
CREATE OR REPLACE FUNCTION notice_example(p_order_id INT)
|
||
RETURNS VOID AS $$
|
||
BEGIN
|
||
IF p_order_id < 1000 THEN
|
||
RAISE NOTICE 'Using fallback logic for old order ID: %', p_order_id;
|
||
END IF;
|
||
|
||
-- Continue the rest of logic...
|
||
END;
|
||
$$ LANGUAGE plpgsql;
|
||
```
|
||
- **Explanation**: `NOTICE` is a good choice to inform about unusual but not critical conditions.
|
||
|
||
## Warning on Suspicious Activity
|
||
```
|
||
CREATE OR REPLACE FUNCTION warning_example(p_inventory_change INT)
|
||
RETURNS VOID AS $$
|
||
BEGIN
|
||
IF p_inventory_change < 0 THEN
|
||
RAISE WARNING 'Negative inventory change: %', p_inventory_change;
|
||
-- Additional logic to handle a negative inventory adjustment
|
||
END IF;
|
||
|
||
-- Continue normal processing
|
||
END;
|
||
$$ LANGUAGE plpgsql;
|
||
```
|
||
- **Explanation**: Use `WARNING` to highlight a problematic scenario that doesn’t necessarily break business logic but should be investigated.
|
||
|
||
## Exception for Critical Violations
|
||
```
|
||
CREATE OR REPLACE FUNCTION exception_example(p_user_id INT, p_amount NUMERIC)
|
||
RETURNS VOID AS $$
|
||
DECLARE
|
||
v_balance NUMERIC;
|
||
BEGIN
|
||
SELECT balance INTO v_balance FROM account WHERE user_id = p_user_id;
|
||
|
||
IF v_balance IS NULL THEN
|
||
RAISE EXCEPTION 'No account balance found for user_id=%', p_user_id;
|
||
END IF;
|
||
|
||
IF v_balance < p_amount THEN
|
||
RAISE EXCEPTION 'Insufficient funds for user_id=%. Available=%; Requested=%',
|
||
p_user_id, v_balance, p_amount;
|
||
END IF;
|
||
|
||
-- Proceed with valid transaction...
|
||
END;
|
||
$$ LANGUAGE plpgsql;
|
||
```
|
||
- **Explanation**: `EXCEPTION` is appropriate here to abort a transaction that violates a critical business rule (insufficient funds).
|
||
|
||
## Additional Notes
|
||
- **Audit Logging**:
|
||
- For high-stakes operations (money transfers, inventory changes, sensitive data access), use `RAISE LOG` or `NOTICE` to ensure they appear in system logs for easy review later.
|
||
- Consider using PostgreSQL’s built-in **audit extensions** or setting up triggers on critical tables for an additional layer of audit.
|
||
|
||
- **Performance Implications**:
|
||
- Logging heavily, especially at higher levels, can affect performance. Use with caution in high-throughput code paths.
|
||
- If debugging statements are needed in high-traffic areas, guard them behind conditions or configuration flags.
|
||
|
||
- **Consistency**:
|
||
- Always keep your log messages consistent, e.g., “Function X performed Y for user Z,” so searching log files is straightforward.
|
||
- Use parameters in `RAISE` statements (e.g., `RAISE LOG 'Message: %', var;`) for clarity and safety—avoid string concatenation to reduce risk of SQL injection or malformed logs.
|