Logging to PostgreSQL from PL/Rust
PL/Rust provides the ability to log details using PostgreSQL's logging system. This functionality is exposed from pgrx via plrust/plrust-trusted-pgrx/src/lib.rs.
The macros available for logging are defined:
#![allow(unused)] fn main() { pub use ::pgrx::{ debug1, debug2, debug3, debug4, debug5, ereport, error, info, log, notice, warning, }; }
Basic logging
Using the log!()
macro will send the message defined in the function to the
PostgreSQL logs defined by your postgresql.conf
. Running the following
example of plrust.one()
creates a LOG
record.
CREATE OR REPLACE FUNCTION plrust.one()
RETURNS INT
LANGUAGE plrust
AS
$$
log!("Hello! Friendly log message here.");
Ok(Some(1))
$$
;
Running plrust.one()
will run normally and the client running the query will
be presented with the results. The log!()
macro adds the defined log
message to the PostgreSQL log file.
The exact contents on the log line created in PostgreSQL's log file depends
on your postgresql.conf
settings related to logging. The following example
is what it may look like.
2023-03-04 16:06:40 UTC [8109]: [15-1] user=postgres,db=plrust,app=psql,client=[local],query_id=-2211430114177040240 LOG: Hello! Friendly log message here.
The remainder of logging examples will only show the details controlled by PL/Rust like the following example.
LOG: Hello! Friendly log message here.
Logging is not limited to static messages. Values from the function can be included
using the {variable}
notation. Beware of data types, the i32
value returned
by the plrust.one()
function needs to be converted .to_string()
to include
in the logged message string.
CREATE OR REPLACE FUNCTION plrust.one()
RETURNS INT
LANGUAGE plrust
AS
$$
let one_val = 1_i32;
log!("The plrust.one() function is returning: {one_val}");
Ok(Some(one_val))
$$
;
When the above function runs, the resulting log line looks like the following.
LOG: The plrust.one() function is returning: 1
Warnings
Use the warning!()
macro to log a more severe message.
Warnings are sent to the log file as well as being returned to the client as a
WARNING
.
CREATE OR REPLACE FUNCTION plrust.one()
RETURNS INT
LANGUAGE plrust
AS
$$
let expected_val = 1_i32;
let one_val = 2_i32;
if expected_val != one_val {
warning!("The value for plrust.one() is unexpected. Found {one_val}")
} else {
log!("The plrust.one() function is returning: {one_val}");
}
Ok(Some(one_val))
$$
;
The following WARNING
message is sent to the PostgreSQL log and to the client.
WARNING: The value for plrust.one() is unexpected. Found 2
Running the above in psql
looks like the following example.
You can see the user is presented with the warning message as well as the results
showing the one
function returning the value 2
.
plrust=# select plrust.one();
WARNING: The value for plrust.one() is unexpected. Found 2
DETAIL:
one
-----
2
(1 row)
Errors
There are cases when a function simply cannot proceed and these errors need to
be logged. The following example changes the warning
from the previous example
to an error
.
CREATE OR REPLACE FUNCTION plrust.one()
RETURNS INT
LANGUAGE plrust
AS
$$
let expected_val = 1_i32;
let one_val = 2_i32;
let one_val_str = one_val.to_string();
if expected_val != one_val {
error!("Invalid for plrust.one(). Found {one_val_str}")
} else {
log!("The plrust.one() function is returning: {one_val_str}");
}
Ok(Some(one_val))
$$
;
When PL/Rust runs the error!()
macro the message is logged to the log file,
returned to the client, and the execution of the function is terminated with a panic.
In psql
the user sees:
plrust=# select plrust.one();
ERROR: Invalid for plrust.one(). Found 2
DETAIL:
plrust=#
In the PostgreSQL logs the following output is recorded. Notice the panicked
line prior to the ERROR
reported by the PL/Rust function.
thread '<unnamed>' panicked at 'Box<dyn Any>', /var/lib/postgresql/.cargo/registry/src/github.com-1ecc6299db9ec823/pgrx-pg-sys-0.7.2/src/submodules/panic.rs:160:13
ERROR: Invalid for plrust.one(). Found 2
Notifying the user
Using notice!()
and info!()
macros return the message to the client running
the query, allowing functions to provide feedback to the user running
the query. These options do not log the message to the PostgreSQL logs.
CREATE OR REPLACE FUNCTION plrust.one()
RETURNS INT
LANGUAGE plrust
AS
$$
notice!("Hello, this is a notice");
Ok(Some(1))
$$
;
Running SELECT plrust.one()
returns the expected value of 1
along with the defined notice. Using psql
returns and example
like the following code block.
NOTICE: Hello, this is a notice
DETAIL:
┌─────┐
│ one │
╞═════╡
│ 1 │
└─────┘
Using ereport
For the most control over logging you can use the ereport!()
macro.
This is not necessary for most use cases.
CREATE FUNCTION one()
RETURNS INT
LANGUAGE plrust
AS
$$
ereport!(PgLogLevel::LOG,
PgSqlErrorCode::ERRCODE_SUCCESSFUL_COMPLETION,
"A user ran the one() function");
Ok(Some(1))
$$
;