This is not exactly “incredibly clever nerd stuff” but it works and you are most welcome to suggest improvements.

This “fat beauty” collects all information about error using GET STACKED DIAGNOSTICS command. Then checks error details and create summary in JSON format – which you can insert into error log etc. Check of error details is this case was created especially for deadlock situation – it found blocking process and stores query which caused deadlock. So for other error you have to change it.

This beauty cannot be placed totally into separate function. At least first part with GET must be in exception handling blocks.

Analysis of the error text is of course mutual for all other functions and therefore in real live place it into special function.

do $$
declare
begin
	begin
              .... here some very incredible program which can cause very incredible error you need to check ....
	end;

exception when others then
        raise notice 'ERROR: % - %', sqlstate, sqlerrm;
	declare
		_error_sqlstate text;
		_error_column_name text;
		_error_constraint_name text;
		_error_pg_datatype_name text;
		_error_message_text text;
		_error_table_name text;
		_error_schema_name text;
		_error_pg_exception_detail text;
		_error_pg_exception_hint text;
		_error_pg_exception_context text;
		_processes text;
		_transactions text;
		_error_data text;
		_lock_data text;
	begin
		get stacked diagnostics
			_error_sqlstate=RETURNED_SQLSTATE,
			_error_column_name=COLUMN_NAME,
			_error_constraint_name=CONSTRAINT_NAME,
			_error_pg_datatype_name=PG_DATATYPE_NAME,
			_error_message_text=MESSAGE_TEXT,
			_error_table_name=TABLE_NAME,
			_error_schema_name=SCHEMA_NAME,
			_error_pg_exception_detail=PG_EXCEPTION_DETAIL,
			_error_pg_exception_hint=PG_EXCEPTION_HINT,
			_error_pg_exception_context=PG_EXCEPTION_CONTEXT;

		if position('PROCESS' in upper(_error_pg_exception_detail) ) > 0 then
			select
			array_to_string(array_agg(distinct case when _part='PROCESS' then _next_part else null end),',') as _process,
			array_to_string(array_agg(distinct case when _part='TRANSACTION' then _next_part else null end),',') as _transaction
			into _processes, _transactions
			from (
			select _part, lead(_part,1) over (order by _row) as _next_part from (
			select row_number() over () as _row,
			regexp_split_to_table (translate(upper(_error_pg_exception_detail),'.;',''), E'\\s+') as _part
			) src
			) src1 where _part in ('PROCESS', 'TRANSACTION');

			select array_to_string( array_agg(locks._locks),',') into _lock_data
			from (
			select row_to_json(src)::text as _locks
			from (
				select * from (
					SELECT bl.pid,
					a.usename,
					d.datname as database_name,
					bl.relation,
					c.relname,
					case when c.relkind='r' then 'ordinary table'
					when c.relkind='i' then 'index'
					when c.relkind='S' then 'sequence'
					when c.relkind='v' then 'view'
					when c.relkind='m' then 'materialized view'
					when c.relkind='c' then 'composite type'
					when c.relkind='t' then 'TOAST table'
					when c.relkind='f' then 'foreign table'
					else '???' end reltype,
					bl.virtualxid,
					coalesce(bl.transactionid, first_value(bl.transactionid) over (partition by bl.virtualtransaction order by coalesce(bl.transactionid::text,'0') desc) ) as transactionid,
					bl.virtualtransaction, bl.locktype, bl.mode, bl.granted,
					to_json(replace(replace(coalesce(a.query,'null'),'''',''''''),'"','')) as query
					FROM  pg_catalog.pg_locks         bl
					left JOIN pg_catalog.pg_stat_activity a  ON a.pid = bl.pid
					left join pg_catalog.pg_database d on bl.database=d.oid
					left join pg_catalog.pg_class c on bl.relation=c.oid
					) src0
				where src0.pid::text in (select unnest(string_to_array(_processes,','))) and
				src0.transactionid::text in (select unnest(string_to_array(_transactions,','))) and
				src0.relation is not null
			) src ) locks;

		end if;

		_error_data := '{ "pid":'||to_json(pg_backend_pid())||
		', "processes":'||to_json(coalesce(_processes,'null'))||
		', "transactions":'||to_json(coalesce(_transactions,'null'))||
		', "sqlstate":'||to_json(coalesce(_error_sqlstate,'null'))||
		', "column_name":'||to_json(coalesce(_error_column_name,'null'))||
		', "constraint_name":'||to_json(coalesce(_error_constraint_name,'null'))||
		', "pg_datatype_name":'||to_json(coalesce(_error_pg_datatype_name,'null'))||
		', "message_text":'||to_json(coalesce(_error_message_text,'null'))||
		', "table_name":'||to_json(coalesce(_error_table_name,'null'))||
		', "schema_name":'||to_json(coalesce(_error_schema_name,'null'))||
		', "pg_exception_detail":'||to_json(coalesce(_error_pg_exception_detail,'null'))||
		', "pg_exception_hint":'||to_json(coalesce(_error_pg_exception_hint,'null'))||
		', "pg_exception_context":'||
		to_json(replace(replace(coalesce(_error_pg_exception_context,'null'),'''',''''''),'"',' '))||
		', "locks": ['||coalesce(_lock_data,'null')||'] }';

		raise notice '_error_data= %', _error_data;
	end;

end;
$$