log stacktrace of current python interpreter via postgresql trigger

  • Last Update :
  • Techknowledgy :
Is there a way to log the Python / Django traceback from within a PostgreSQL trigger ?

Given this setup

(django / python) - [SQL connection] - > (PostgreSQL server)

If you use psycopg2, you can use this

# Overwriting connetion.notices via Django
class MyAppConfig(AppConfig):

   def ready(self):
   connection_created.connect(connection_created_check_for_notice_in_connection)

class ConnectionNoticeList(object):
   def append(self, message):
   if not 'some_magic_of_db_trigger' in message:
   return
logger.warn('%s %s' % (message, ''.join(traceback.format_stack())))

def connection_created_check_for_notice_in_connection(sender, connection, ** kwargs):
   connection.connection.notices = ConnectionNoticeList()

Suggestion : 2

Продукты Postgres Pro Standard Postgres Pro Certified Postgres Pro Enterprise Postgres Pro Enterprise Certified Postgres Pro для 1С PostgreSQL для Windows

I am hunting a non reproducible in a production environment.

I can detect the buggy change in a postgres trigger.

Since it is production code I must no raise an exception.I can
only use logging.

If I could see the stacktrace of the python interpreter, I could
see which codes the change which I am hunting.

But how to get this interpreter stacktrace,
if the condition is
detect in the db trigger ?

   https : //stackoverflow.com/questions/51873708/log-stacktrace-of-python-in-postgresql-trigger

   Maybe there is a psycopg2 feature which I don 't know up to now.

I guess LISTEN + NOTIFY could get used.
Or setting a connection variable which I check after each SQL statement.

Ideas welcome,

Thomas Güttler

--
Thomas Guettler http: //www.thomas-guettler.de/
   I am looking
for feedback: https: //github.com/guettli/programming-guidelines
Am 20.05 .19 um 12: 19 schrieb Daniele Varrazzo:
   >
   If you use postgres logging in stored procedures you can retrieve the logs in 'connection.notices'. >
   >
   http: //initd.org/psycopg/docs/connection.html#connection.notices

   This sound great.Unfortunately I can 't extract the whole stacktrace.
I only get the lines below psycopg, not the above(lines of the callers).

Here is my code:

   class MyAppConfig(AppConfig):

   def ready(self):
   connection_created.connect(connection_created_check_for_notice_in_connection)

class ConnectionNoticeList(object):
   def append(self, message):
   if not 'some_magic_of_db_trigger' in message:
   return
logger.warn('%s %s' % (message, ''.join(traceback.format_stack())))

def connection_created_check_for_notice_in_connection(sender, connection, ** kwargs):
   connection.connection.notices = ConnectionNoticeList()

I see this in the logs:

   'NOTICE:  some_magic_of_db_trigger: 17909
File "/snap/pycharm-community/128/helpers/pycharm/_jb_pytest_runner....ork/foo/apps.py", line 47, in append
logger.warn(\'%s %s\' % (message, \'\'.join(traceback.format_stack())))
      '

      traceback.format_stack() inside ConnectionNoticeList.append() extracts not the callers.

      Is there a way to get the callers lines ?

      --
      Thomas Guettler http : //www.thomas-guettler.de/
      I am looking
      for feedback: https: //github.com/guettli/programming-guidelines
Am 20.05 .19 um 14: 43 schrieb Thomas Güttler:
   >
   Am 20.05 .19 um 12: 19 schrieb Daniele Varrazzo:
   >>
   If you use postgres logging in stored procedures you can retrieve the logs in 'connection.notices'. >>
   >>
   http: //initd.org/psycopg/docs/connection.html#connection.notices
   >
   >
   This sound great.Unfortunately I can 't extract the whole stacktrace. >
   I only get the lines below psycopg, not the above(lines of the callers). >
   >
   Here is my code:
   >
   >
   class MyAppConfig(AppConfig):
   >
   >
   def ready(self):
   >
   connection_created.connect(connection_created_check_for_notice_in_connection) >
   >
   class ConnectionNoticeList(object):
   >
   def append(self, message):
   >
   if not 'some_magic_of_db_trigger' in message:
   >
   return >
   logger.warn('%s %s' % (message, ''.join(traceback.format_stack()))) >
   >
   >
   def connection_created_check_for_notice_in_connection(sender, connection, ** kwargs):
   >
   connection.connection.notices = ConnectionNoticeList() >
   >
   >
   I see this in the logs:
   >
   >
   'NOTICE:  some_magic_of_db_trigger: 17909 >
   File "/snap/pycharm-community/128/helpers/pycharm/_jb_pytest_runner....ork/foo/apps.py", line 47, in append >
   logger.warn(\'%s %s\' % (message, \'\'.join(traceback.format_stack()))) >
      ' >
      >
      >
      traceback.format_stack() inside ConnectionNoticeList.append() extracts not the callers. >
      >
      Is there a way to get the callers lines ?

      Above code works.I see the whole traceback.

      I don 't know why the traceback was cut in PyCharm. In production I could see the whole traceback and I could find the 
      broken code which modified the data in way which should not happen.

      Many thanks to Daniele Varrazzo who provided the hint to overwrite connection.notices.

      Regards,
      Thomas Güttler

      --
      Thomas Guettler http : //www.thomas-guettler.de/
      I am looking
      for feedback: https: //github.com/guettli/programming-guidelines

Suggestion : 3

Log Stacktrace of current Python Interpreter via PostgreSQL trigger,PostgreSQL log trigger optimalization,PostgreSQL Trigger Error : control reached end of trigger procedure without RETURN,Calling external program from PostgreSQL trigger

I haven't thoroughly tested this, let alone benchmarked it, but here's the gist of it:

CREATE OR REPLACE FUNCTION table_log_trig() RETURNS trigger AS
$$
DECLARE
OldJson JSONB = NULL;
BEGIN
IF TG_OP < > 'INSERT'
THEN
OldJson: = to_jsonb(old);
END IF;

INSERT INTO tab_logs(record_id, field_name, old_value, new_value, created_at, created_by, action)
SELECT new.id, key, OldValues.value, NewValues.value, now(), 999, 'O'
FROM jsonb_each(to_jsonb(new)) NewValues
LEFT JOIN jsonb_each(OldJson) OldValues USING(key)
WHERE
   (
      (TG_ARGV[0] IS NULL AND key NOT IN('id', 'created_at', 'updated_at')) OR(TG_ARGV[0] IS NOT NULL AND key = ANY(string_to_array(TG_ARGV[0], ',')))
   ) AND
OldValues.value::text IS DISTINCT FROM NewValues.value::text;

RETURN NULL;
END
$$
LANGUAGE plpgsql VOLATILE;

Suggestion : 4

If inspectdb cannot map a column’s type to a model field type, it’ll use TextField and will insert the Python comment 'This field type is a guess.' next to the field in the generated model. The recognized fields may depend on apps listed in INSTALLED_APPS. For example, django.contrib.postgres adds recognition for several PostgreSQL-specific field types.,If the database column name is a Python reserved word (such as 'pass', 'class' or 'for'), inspectdb will append '_field' to the attribute name. For example, if a table has a column 'for', the generated model will have a field 'for_field', with the db_column attribute set to 'for'. inspectdb will insert the Python comment 'Field renamed because it was a Python reserved word.' next to the field.,You may choose what tables or views to inspect by passing their names as arguments. If no arguments are provided, models are created for views only if the --include-views option is used. Models for partition tables are created on PostgreSQL if the --include-partitions option is used.,Support for calling makemigrations without an active database connection was added. In that case, check for a consistent migration history is skipped.

$ django-admin <command> [options]
   $ manage.py <command> [options]
      $ python -m django <command> [options]
...\> django-admin <command> [options]
   ...\> manage.py <command> [options]
      ...\> py -m django <command> [options]
1.4.dev17026
1.4 a1
1.4
django - admin check auth admin myapp
django - admin check--tag models--tag compatibility
django - admin check--database
default --database other