powered by simpleCommunicator - 2.0.61     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / Как можно реализовать логи?
21 сообщений из 21, страница 1 из 1
Как можно реализовать логи?
    #39006099
PCContra
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Задача стоит такая:
пусть есть простенькая таблица
Код: sql
1.
TABLE t (id INT PRIMARY KEY);


Необходимо вставлять в таблицу логов следующие данные:
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
TABLE log_t (
log_id INT PRIMARY KEY,         --PK
id INT                          -- берется из таблицы t перед внесением изменений
user                            -- пусть это какая-то переменная, передается в sql, наподобие как в функции ($1, $2...)
ip TEXT                         -- есть ли в пг с какого айпи зашел пользователь, если нет, то также внешняя переменная как и user
change_date TIMESTAMP           -- NOW()
type VARCHAR                    -- 'U' для апдейта, 'I' для вставки, 'D' для удаления
)



т.к. во внешнем приложении писать такое лень, есть идея повесить триггер на таблицу (оно и правильнее), но, насколько я понимаю, в триггер никакие переменные передать нельзя . А мне нужно обязательно поле USER. Так вот, нужны идеи, как такое реализовать.
Например, мне приходит в голову (я, правда, не знаю, есть ли такое или нет) при коннекте к базе из внешнего приложения (php) передать USER в глобальную переменную, к которой триггер будет потом обращаться.

PS: Триггеры не создавал ни разу. Прошу дать совет, возможно ли такая реализация, или есть более грамотные способы. Может быть, кто-нибудь делал подобное.
PPS: где найти мануал знаю)
...
Рейтинг: 0 / 0
Как можно реализовать логи?
    #39006218
ARTURV
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
PCContra,

current_user
...
Рейтинг: 0 / 0
Как можно реализовать логи?
    #39006410
drsm
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
PCContra,

Понадобилось на днях примерно тоже, думаю сделать в главной таблице пару фейковых полей типа твоих user ip с default null и в бефоре триггере сностить полученные обычным образом значения из new, записав в лог. Костыль но в моем случае то, что надо ))
...
Рейтинг: 0 / 0
Как можно реализовать логи?
    #39006509
PCContra
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
drsmPCContra,

Понадобилось на днях примерно тоже, думаю сделать в главной таблице пару фейковых полей типа твоих user ip с default null и в бефоре триггере сностить полученные обычным образом значения из new, записав в лог. Костыль но в моем случае то, что надо ))
Ну у Вас тогда история изменений не будет вестись
...
Рейтинг: 0 / 0
Как можно реализовать логи?
    #39006529
PCContra
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ARTURVPCContra,

current_user
У меня разные пользователи заходят каждый под своей учеткой в ПО, но к базе коннектятся под одним юзером. Поэтому current_user не подойдет.

Если при каждом запросе в СУБД передается логин/пароль, и в СУБД никакой сессии, наподобие php-ных, не существует, то вариант, предложенный ARTURV, получается, единственный?
...
Рейтинг: 0 / 0
Как можно реализовать логи?
    #39006586
p2.
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
PCContra,

Сделай конфиг-переменную и сохраняй туда данные прикладной сессии при авторизации в приложении.
...
Рейтинг: 0 / 0
Как можно реализовать логи?
    #39006643
PCContra
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
p2.PCContra,

Сделай конфиг-переменную и сохраняй туда данные прикладной сессии при авторизации в приложении.
А пример можно?
"Сделай конфиг-переменную" во внешнем приложении?
"сохраняй туда данные прикладной сессии" тоже во внешнем?
задача-то стоит использовать триггеры или правила в СУБД, поэтому и пользоваться нужно тем, что там есть. Я бы рад сделать что-то типа:
Код: sql
1.
2.
3.
CREATE RULE my_rule AS ON update
TO main_table
DO INSERT INTO log_main_table (id, user, ip, change_date, type) VALUES (OLD.id, current_user, current_timestamp, 'U');



Как вместо current_user вставить свое значение?
...
Рейтинг: 0 / 0
Как можно реализовать логи?
    #39006799
drsm
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
PCContradrsmPCContra,

Понадобилось на днях примерно тоже, думаю сделать в главной таблице пару фейковых полей типа твоих user ip с default null и в бефоре триггере сностить полученные обычным образом значения из new, записав в лог. Костыль но в моем случае то, что надо ))
Ну у Вас тогда история изменений не будет вестись

ох, как-то так, идея думаю ясна:

Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
create table olo (
	pk serial primary key,
	usr text default null,
	ip text default null
);

create table log (
	pk serial primary key,
	usr text not null,
	ip text not null,
	t timestamp with time zone default now()
);

create or replace function ololog()
  returns trigger as
$body$
begin
	insert into log(usr, ip) values (new.usr, new.ip);
	new.usr := null;
	new.ip := null;

	return new;
end;
$body$
  language plpgsql volatile
  cost 100;

create trigger on_ololog
  before insert or update
  on olo
  for each row
  execute procedure ololog();

insert into olo(usr, ip) values ('one', '111.111.111.11'),('two', '222.222.222.22'),('three', '333.333.333.33');
...
Рейтинг: 0 / 0
Как можно реализовать логи?
    #39006973
PCContra
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
drsm , до меня дошло, наконец-то. Да, идея простая. Это даже лучшее решение, по мне, чем создавать кучу ролей в СУБД
...
Рейтинг: 0 / 0
Как можно реализовать логи?
    #39007027
Фотография vyegorov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
drsm
Код: sql
1.
2.
	new.usr := null;
	new.ip := null;


А зачем пользователь и IP обнуляются?

Если это необходимо, то вместо таблицы я бы сделал представление (view) и повесил бы триггер на него,
переводя реальные данные в нужные таблицы и ведя лог. Без фейковых полей.

Служебную информацию можно получить стандартными функциями :
Код: sql
1.
select current_user, inet_client_addr(), inet_client_port(), inet_server_addr(), inet_server_port();


И если это то, что надо, то создавать дополнительные поля в основной таблице смысла нет.
...
Рейтинг: 0 / 0
Как можно реализовать логи?
    #39007571
PCContra
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
vyegorovЕсли это необходимо, то вместо таблицы я бы сделал представление (view) и повесил бы триггер на него,
переводя реальные данные в нужные таблицы и ведя лог. Без фейковых полей.

А вот это интересно. Как без фейковых полей, можно пример в студию. На самом деле, очень интересно
...
Рейтинг: 0 / 0
Как можно реализовать логи?
    #39007626
Фотография vyegorov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
PCContra,

Совсем без фейковых полей не получиться, но они присутствуют только в представлениях.

Это схема для syslog'а (кажется, я не помню), которого настроили писать логи postfix'а в базу.
Самая большая триггерная функция (последняя) делает всю работу по выделению получателей, адресов и изменению статуса сообщения.

Мы потом еще что-то переделывали и добавляли, но не суть.

Таблицы
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
CREATE SEQUENCE seq_email_id;
CREATE TABLE email (
    email_id      int8 NOT NULL DEFAULT nextval('seq_email_id'),
    email_address text NOT NULL
);
ALTER SEQUENCE seq_email_id OWNED BY email.email_id;
ALTER TABLE email ADD CONSTRAINT p_email PRIMARY KEY (email_id);
ALTER TABLE email ADD CONSTRAINT u_email UNIQUE (email_address);

CREATE SEQUENCE seq_message_id;
CREATE TABLE message (
    message_id      int8 NOT NULL DEFAULT nextval('seq_message_id'),
    queue_id        int8 NOT NULL,
    rfc_822_code    text,
    sender_ip       inet,
    sender_email_id int8,
    rsyslog_ip      inet NOT NULL DEFAULT inet_client_addr(),
    is_complete     boolean NOT NULL DEFAULT false
)
WITH (fillfactor=50);
ALTER SEQUENCE seq_message_id OWNED BY message.message_id;

CREATE TABLE recipient(
    message_id         int8,
    recipient_email_id int8
);

CREATE TYPE sendstatus
    AS ENUM ('deferred', 'bounced', 'expired', 'rejected', 'sent');
CREATE TABLE attempt (
    message_id         int8 NOT NULL,
    recipient_email_id int8 NOT NULL,
    attempt_no         int2,
    tstamp             timestamptz NOT NULL,
    server_response    text NOT NULL,
    status             sendstatus NOT NULL
);


Функции
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
CREATE OR REPLACE FUNCTION hex2int8(text) RETURNS bigint IMMUTABLE STRICT AS $hex2int8$
    SELECT ('x'||$1)::bit(48)::bigint;
$hex2int8$ LANGUAGE sql;

CREATE OR REPLACE FUNCTION get_email_id(text) RETURNS int8 AS $get_email_id$
DECLARE
    vemail_address text;
    vemail_id      int8;

BEGIN
    vemail_address := split_part(split_part($1, '>,', 1), '<', 2);
    IF vemail_address = '' THEN
        vemail_address := '<>';
    END IF;
    SELECT email_id INTO vemail_id FROM email 
        WHERE email_address = vemail_address;
    IF vemail_id IS NULL THEN
        vemail_id := nextval('seq_email_id');
        INSERT INTO email(email_id, email_address) 
            VALUES (vemail_id, vemail_address);
    END IF;
    RETURN vemail_id;
END;
$get_email_id$ LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION attempt_t() RETURNS trigger AS $attempts_t$
BEGIN
    SELECT count(*) + 1 INTO NEW.attempt_no FROM attempt WHERE message_id = NEW.message_id;
    NEW.status = split_part(NEW.server_response, ' ', 1);
    NEW.server_response = regexp_replace(NEW.server_response, '^[^ ]* \((.*)\)$', '\1');
    RETURN NEW;
END;
$attempts_t$ LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION parsemsg_t() RETURNS trigger AS $parsemsg_t$
DECLARE
    vqueue_id           int8;
    vmessage_id         int8;
    vip_address         inet;
    vrecipient_email_id int8;
    message_body        text;
    filter_re           text DEFAULT '^ ([0-9A-F]+): (.*)$';

BEGIN
    IF NOT NEW.vrawmessage ~ filter_re THEN
        RETURN NULL;
    END IF;

    vqueue_id := hex2int8(regexp_replace(NEW.vrawmessage, filter_re, '\1'));
    message_body := regexp_replace(NEW.vrawmessage, filter_re, '\2');

    SELECT message_id INTO vmessage_id FROM message
        WHERE queue_id = vqueue_id AND is_complete = false;
    IF vmessage_id IS NULL THEN
        vmessage_id := nextval('seq_message_id');
        INSERT INTO message(message_id, queue_id)
            VALUES (vmessage_id, vqueue_id);
    END IF;

    IF message_body = 'removed' THEN
        UPDATE message SET is_complete = true WHERE message_id = vmessage_id;
        RETURN NULL;
    END IF;

    IF message_body ~ '^client=' THEN
        UPDATE message SET sender_ip = rtrim(split_part(message_body, '[', 2), ']')::inet
            WHERE message_id = vmessage_id;
    ELSIF message_body ~ '^message-id=' THEN
        UPDATE message
            SET rfc_822_code = rtrim(split_part(message_body, '<', 2), '>')
            WHERE message_id = vmessage_id;

    ELSIF message_body ~ '^from=' THEN
        UPDATE message SET sender_email_id = get_email_id(message_body)
            WHERE message_id = vmessage_id;

    ELSIF message_body ~ '^to=' THEN
        vrecipient_email_id := get_email_id(message_body);
        INSERT INTO recipient(message_id, recipient_email_id)
        SELECT vmessage_id, vrecipient_email_id
         WHERE NOT EXISTS (
            SELECT 1 FROM recipient WHERE message_id=vmessage_id
               AND recipient_email_id=vrecipient_email_id);
        INSERT INTO attempt(message_id, recipient_email_id, tstamp, server_response)
        SELECT vmessage_id, vrecipient_email_id, NEW.vnow, split_part(fld,'=',2)
          FROM regexp_split_to_table(NEW.vrawmessage, ', ') AS t(fld)
         WHERE split_part(fld,'=',1)='status';
    END IF;

    RETURN NULL;
END;
$parsemsg_t$ LANGUAGE plpgsql;


Ну и пара триггеров:
Код: sql
1.
2.
CREATE TRIGGER t_attempt BEFORE INSERT ON attempt FOR EACH ROW EXECUTE PROCEDURE attempt_t();
CREATE TRIGGER t_parselog INSTEAD OF INSERT ON unparsed FOR EACH ROW EXECUTE PROCEDURE parsemsg_t();


Та самая вьюшка:
Код: sql
1.
2.
3.
CREATE VIEW unparsed AS
    SELECT cast(now() AS timestamptz) AS vnow,
        cast('rawmessage' AS text) AS vrawmessage;
...
Рейтинг: 0 / 0
Как можно реализовать логи?
    #39013158
PCContra
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Решил дополнить

Таким образом не получится реализовать логи при удалении записи. Если запись удаляем, то NEW.user, да и вообще, NEW.* не определено. А мне надо логи update и delete.

Как реализовать логи удаления? Вариант "дописывать везде ручками, где происходим удаление" - не вариант
...
Рейтинг: 0 / 0
Как можно реализовать логи?
    #39013371
Dimitry Sibiryakov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
PCContraЕсли запись удаляем, то NEW.user, да и вообще, NEW.* не определено.
Зато, наверное, определено OLD.*?..
...
Рейтинг: 0 / 0
Как можно реализовать логи?
    #39013399
PCContra
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
OLD.* конечно, определен. Но мы тут как решили (читайте выше): будем вставлять в исходную таблицу при UPDATE'е ник юзера, который изменил строку, в соответствующее поле.
Таким образом получается, что в исходной таблице виден сотрудник, совершивший текущие изменения строки.
При последующем изменении вся строку идет в лог.
Таки образом можно реализовать логи INSERT'ов и UPDATE'ов, но вот логи DELETE'ов реализовать не получится
...
Рейтинг: 0 / 0
Как можно реализовать логи?
    #39013464
qwwq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
PCContra,

гм, у вас и молоко говенное делают
и детей без фантазии

ну сделайте фейковое поле deleted
а в instead of триггере на update вьюхи удаляйте запись таблицы
при этом NEW.* заполняйте чем хотите, и логируйте это вот всё

хотя это всё от небольшого ума и большой лени -- правильнее было бы хранимками рулить -- там всё в руках писателя
...
Рейтинг: 0 / 0
Как можно реализовать логи?
    #39013583
PCContra
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
qwwqPCContra,

гм, у вас и молоко говенное делают
и детей без фантазии

ну сделайте фейковое поле deleted
а в instead of триггере на update вьюхи удаляйте запись таблицы
при этом NEW.* заполняйте чем хотите, и логируйте это вот всё

хотя это всё от небольшого ума и большой лени -- правильнее было бы хранимками рулить -- там всё в руках писателя
А как хранимками указывать пользователя из внешней программы?

У меня есть другая идея и такой вопрос: при создании объекта PDO в постгрессе создается какой-нибудь идентификатор соединения?
...
Рейтинг: 0 / 0
Как можно реализовать логи?
    #39013672
g2099599
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
PCContra,

p2. уже советовал, но еще раз:
http://pgcookbook.ru/article/session_or_transaction_variables.html
...
Рейтинг: 0 / 0
Как можно реализовать логи?
    #39013815
qwwq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
PCContraА как хранимками указывать пользователя из внешней программы?кхмм.
какбе помяхше-то

ну вот, кака бычна, -- через параметр. а вы что подумали ?

PCContraУ меня есть другая идея и такой вопрос: при создании объекта PDO в постгрессе создается какой-нибудь идентификатор соединения?если вы ничего не пуллите -- имеете право создать переменную соединения. способов масса. (да даже если и пуллите -- и то, но техника немного иная. в том числе можно пользовать стандартную переменную пж, что-то типа application_name. она позволяет.
...
Рейтинг: 0 / 0
Как можно реализовать логи?
    #39014206
PCContra
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
qwwqPCContraА как хранимками указывать пользователя из внешней программы?кхмм.
какбе помяхше-то

ну вот, кака бычна, -- через параметр. а вы что подумали ?

PCContraУ меня есть другая идея и такой вопрос: при создании объекта PDO в постгрессе создается какой-нибудь идентификатор соединения?если вы ничего не пуллите -- имеете право создать переменную соединения. способов масса. (да даже если и пуллите -- и то, но техника немного иная. в том числе можно пользовать стандартную переменную пж, что-то типа application_name. она позволяет.
Можно попросить написать пример про переменную соединения?
У меня соединение происходит так:
Код: php
1.
$db = new PDO('pgsql:host=192.168.137.1;port=5432;dbname=anydb', 'anyuser', 'pass');


Вычитал:

Код: php
1.
2.
3.
$db = new PDO('pgsql:host=192.168.137.1;port=5432;dbname=anydb', 'anyuser', 'pass', array(
    PDO::ATTR_PERSISTENT => true
));


php.netЗамечание:

Чтобы использовать постоянные соединения, необходимо добавить константу PDO::ATTR_PERSISTENT в массив параметров драйвера, который передается конструктору PDO.

Думаю, есть вариант при создании объекта PDO указать или установить свою переменную
Ну, или в торой вариант - есть pid - pg_backend_pid()
И при создании объекта записывать связь юзера и процесс_айди можно так:
Код: php
1.
2.
$db = new PDO('pgsql:host=192.168.137.1;port=5432;dbname=anydb', 'anyuser', 'pass');
$db->query("INSERT INTO user_pid(pid, username, time) SELECT pg_backend_pid(), 'Ivan', NOW();");


А в теле триггера выбирать имя юзера по pid и использовать его в дальнейшем:
Код: sql
1.
SELECT username FROM user_pid WHERE pid = SELECT pg_backend_pid();
...
Рейтинг: 0 / 0
Как можно реализовать логи?
    #39014266
Alexius
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
PCContra,

выше же привели ссылку, где примеры установки сессионных переменных задаются. если 9.2+, то

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
postgres=# set mymegaproject.session_id = 1;
SET

postgres=# select current_setting('mymegaproject.session_id');
 current_setting
-----------------
 1
(1 row)



иначе префикс custom переменных надо в конфиге предварительно прописать в custom_variable_classes.

application_name (в случае использования pgbouncer) можно передавать в строке соединения прямо как-то так:

Код: sql
1.
$db = new PDO('pgsql:host=192.168.137.1;port=5432;dbname=anydb;application_name=xxx', 'anyuser', 'pass');
...
Рейтинг: 0 / 0
21 сообщений из 21, страница 1 из 1
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / Как можно реализовать логи?
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]