Логування в базах даних Interbase (Firebird), Різне, Програмування, статті

Автор: Рудюк Сергій, Королівство Delphi


Введення

Програмістам баз даних часто доводиться стикатися з необхідністю збереження інформації про роботу користувачів або програми, тобто Залогуватися інформацію. Дану задачу можна реалізувати велику кількість способів. Кожен спосіб має свої переваги і недоліки. Дана стаття розглядає які є способи логування інформації. Так же написано, як реалізувати логування інформації в базах даних Firebird (Interbase).

Лог в текстовому файлі

Найбільш часто зустрічається спосіб збереження інформації про роботу програми. Це найпростіший з усіх способів, тому що інформація зберігається в текстовому файлі і немає необхідності підключатися до бази даних.

Var StrTmp: TStringList;

StrTmp.Add(“Тест лога 1.”); / / Рядок лога 1

StrTmp.Add(“Тест логу 2.”); / / Рядок логу 2

StrTmp.SaveToFile(“C:FileLog.txt”); / / Збереження інформації з логу

Застосування даного способу логування інформації доцільний для не великих утиліт, які не працюють з базою даних.

Логування інформації в базі даних Firebird (Intebase)

У базах даних логування можна реалізувати кількома способами. Іноді, способи логування програмісти вибирають в залежності від своєї кваліфікації.

Логування в базі даних з програми

Створення логу в даному випадку реалізується прямо з програми. Цей спосіб простий в реалізації, в інформації про логах можна записати будь-яку інформацію.

Недоліки цього способу:
Опишемо приблизний реалізації цього способу логування інформації.
Створимо таблицю:
CREATE TABLE LOGS(
ID INT_64 NOT NULL /* INT_64 = BIGINT */,
DATE_LOG TIMESTAMP,
TEXT_LOG VARCHAR(1500)
USERNAME VARCHAR(30),
CODE_OPER INT_64 /* INT_64 = BIGINT */
);
Де:
Створимо ключ:
ALTER TABLE EVENTS ADD CONSTRAINT PKEVENTS PRIMARY KEY (ID);

Тепер для вставки інформації в лог пишемо звичайний запит вставки інформації та викликаємо його в програмі в тих місцях, коли проводяться операції вставки, видалення або зміни інформації.

Логування в базі даних

Для того, щоб не писати при кожній операції в програмі вставку інформації в лог, можна поступити іншим способом – написати ряд тригерів. Для вставки інформації – тригер на вставку інформації, для поновлення інформації – тригер на оновлення інформації, а для видалення – тригер на видалення. Так як, тригера при вставці, видалення та оновлення інформації спрацьовують автоматично, то немає необхідності додаткового програмування в самій програмі. Таким чином, один раз прописавши тригера для таблиці програміст більше не піклується про програмування вставки в лог.

Напишемо приблизний для даної реалізації логу.
/ * Створюємо генератор для ID логу * /
SET GENERATOR GENID TO 1000
^
/ * Створюємо процедуру, яка буде отримувати необхідний генератор * /
CREATE PROCEDURE PROC_GENID
RETURNS (NEWID BIGINT)
AS BEGIN
NEWID = GEN_ID(GENID, 1);
/* COMMIT;*/
SUSPEND;
END
^
/ * Тригер вставки * /
CREATE TRIGGER RTI_TABLE_NAME FOR TABLE_NAME AFTER INSERT POSITION 0 AS
DECLARE VARIABLE IDLOG BIGINT;
BEGIN
SELECT NEWID
FROM PROC_GENID
INTO :IDLOG;
INSERT INTO LOGDVIG (IDLOG, DATE_LOG, TEXT_LOG, USERNAME, CODE_OPER)
VALUES (:IDLOG, “NOW”, “Текст логу вставки”, CURRENT_USER, 1);
END
^
/ * Тригер поновлення * /
CREATE TRIGGER RTU_ TABLE_NAME FOR TABLE_NAME AFTER UPDATE POSITION 0 AS
DECLARE VARIABLE IDLOG BIGINT;
BEGIN
SELECT NEWID
FROM PROC_GENID
INTO :IDLOG;
INSERT INTO LOGDVIG (IDLOG, DATE_LOG, TEXT_LOG, USERNAME, CODE_OPER)
VALUES (:IDLOG, “NOW”, “Текст логу оновлення”, CURRENT_USER, 1);
END
^
/ * Тригер видалення * /
CREATE TRIGGER RTD_ TABLE_NAME FOR TABLE_NAME AFTER DELETE POSITION 0 AS
DECLARE VARIABLE IDLOG BIGINT;
BEGIN
SELECT NEWID
FROM PROC_GENID
INTO :IDLOG;
INSERT INTO LOGDVIG (IDLOG, DATE_LOG, TEXT_LOG, USERNAME, CODE_OPER)
VALUES (:IDLOG, “NOW”, “Текст логу видалення”, CURRENT_USER, 1);
END
^

Слід зазначити, що подібні тригера створюються для всіх таблиць, в яких необхідно враховувати логи. Даний спосіб логування хороший, але він тільки враховує які операції проводяться без можливості переглянути що конкретно додалося, змінилося чи віддалилося. Наступний спосіб не тільки фіксує всі зміни, а й фіксує саму інформацію.

Логування в базі даних зі збереженням інформації

Даний спосіб логування більш складний, ніж описаний вище. Так, як крім збереження інформації про те, що сталася якась операція, він зберігає ще й саму інформацію. Так само цей спосіб зажадає більший обсяг інформації, ніж в описаному вище.

Складнощі, з якими ми стикаємося при розробці програмної реалізації:
Для початку, розробимо модель.
Як бачите, модель складається із 4-х таблиць:
Згенеруємо скрипт для створення даної структури:
SET TERM ^;
CREATE TABLE IBE$LOG_TABLES (
ID NUMERIC(18,0) NOT NULL,
TABLE_NAME VARCHAR(67) NOT NULL,
OPERATION VARCHAR(1) NOT NULL,
DATE_TIME TIMESTAMP NOT NULL,
USER_NAME VARCHAR(67) NOT NULL
);
^
ALTER TABLE IBE$LOG_TABLES ADD PRIMARY KEY (ID);
^
CREATE TABLE IBE$LOG_KEYS (
LOG_TABLES_ID NUMERIC(18,0) NOT NULL,
KEY_FIELD VARCHAR(67) NOT NULL,
KEY_VALUE VARCHAR(255)
);
^
CREATE INDEX IBE$LOG_KEYS_IDX1 ON IBE$LOG_KEYS (LOG_TABLES_ID);
^
CREATE TABLE IBE$LOG_FIELDS (
LOG_TABLES_ID NUMERIC(18,0) NOT NULL,
FIELD_NAME VARCHAR(67) NOT NULL,
OLD_VALUE VARCHAR(255),
NEW_VALUE VARCHAR(255)
);
^
CREATE INDEX IBE$LOG_FIELDS_IDX1 ON IBE$LOG_FIELDS (LOG_TABLES_ID);
^
CREATE TABLE IBE$LOG_BLOB_FIELDS (
LOG_TABLES_ID NUMERIC(18,0) NOT NULL,
FIELD_NAME VARCHAR(67) NOT NULL,
OLD_CHAR_VALUE VARCHAR(10000),
NEW_CHAR_VALUE VARCHAR(10000),
OLD_BLOB_VALUE BLOB SUB_TYPE 0 SEGMENT SIZE 80,
NEW_BLOB_VALUE BLOB SUB_TYPE 0 SEGMENT SIZE 80
);
^
CREATE INDEX IBE$LOG_BLOB_FIELDS_IDX1 ON IBE$LOG_BLOB_FIELDS (LOG_TABLES_ID);
^

Це ми написали всього лише структуру для зберігання інформації логування в базі даних. Тепер, нам необхідно написати реакцію на зміни інформації. Для цього, чинимо як в способі описаному раніше – Створюємо ряд тригерів.

Припустимо, є у нас таблиця такої структури:
CREATE TABLE BANKDOC (
IDBANKDOC INT_64 NOT NULL /* INT_64 = BIGINT */,
SCHETID INT_64 /* INT_64 = BIGINT */,
DOCID INT_64 NOT NULL /* INT_64 = BIGINT */,
SUMMASPISAN DECIMAL(15,5),
SUMMAPRIHOD DECIMAL(15,5),
SUMMASPISANNDS DECIMAL(15,5),
SUMMAPRIHODNDS DECIMAL(15,5),
NAZNACH BLOB SUB_TYPE 0 SEGMENT SIZE 80,
KODF INT_64 /* INT_64 = BIGINT */
);
Створимо для цієї таблиці тригера вставки інформації в лог.
CREATE TRIGGER IBE$BANKDOC_AI FOR BANKDOC
ACTIVE AFTER INSERT POSITION 32767
AS
DECLARE VARIABLE TID INTEGER;
BEGIN
TID = GEN_ID(IBE$LOG_TABLES_GEN,1);
INSERT INTO IBE$LOG_TABLES (ID, TABLE_NAME, OPERATION, DATE_TIME, USER_NAME)
VALUES (:TID, “BANKDOC”, “I”, “NOW”, USER);
INSERT INTO IBE$LOG_KEYS (LOG_TABLES_ID, KEY_FIELD, KEY_VALUE)
VALUES (:TID, “IDBANKDOC”, NEW.IDBANKDOC);
IF (NOT (NEW.IDBANKDOC IS NULL)) THEN
INSERT INTO IBE$LOG_FIELDS (LOG_TABLES_ID, FIELD_NAME, OLD_VALUE, NEW_VALUE)
VALUES (:TID, “IDBANKDOC”, NULL, NEW.IDBANKDOC);
IF (NOT (NEW.SCHETID IS NULL)) THEN
INSERT INTO IBE$LOG_FIELDS (LOG_TABLES_ID, FIELD_NAME, OLD_VALUE, NEW_VALUE)
VALUES (:TID, “SCHETID”, NULL, NEW.SCHETID);
IF (NOT (NEW.DOCID IS NULL)) THEN
INSERT INTO IBE$LOG_FIELDS (LOG_TABLES_ID, FIELD_NAME, OLD_VALUE, NEW_VALUE)
VALUES (:TID, “DOCID”, NULL, NEW.DOCID);
IF (NOT (NEW.SUMMASPISAN IS NULL)) THEN
INSERT INTO IBE$LOG_FIELDS (LOG_TABLES_ID, FIELD_NAME, OLD_VALUE, NEW_VALUE)
VALUES (:TID, “SUMMASPISAN”, NULL, NEW.SUMMASPISAN);
IF (NOT (NEW.SUMMAPRIHOD IS NULL)) THEN
INSERT INTO IBE$LOG_FIELDS (LOG_TABLES_ID, FIELD_NAME, OLD_VALUE, NEW_VALUE)
VALUES (:TID, “SUMMAPRIHOD”, NULL, NEW.SUMMAPRIHOD);
IF (NOT (NEW.SUMMASPISANNDS IS NULL)) THEN
INSERT INTO IBE$LOG_FIELDS (LOG_TABLES_ID, FIELD_NAME, OLD_VALUE, NEW_VALUE)
VALUES (:TID, “SUMMASPISANNDS”, NULL, NEW.SUMMASPISANNDS);
IF (NOT (NEW.SUMMAPRIHODNDS IS NULL)) THEN
INSERT INTO IBE$LOG_FIELDS (LOG_TABLES_ID, FIELD_NAME, OLD_VALUE, NEW_VALUE)
VALUES (:TID, “SUMMAPRIHODNDS”, NULL, NEW.SUMMAPRIHODNDS);
IF (NOT (NEW.KODF IS NULL)) THEN
INSERT INTO IBE$LOG_FIELDS (LOG_TABLES_ID, FIELD_NAME, OLD_VALUE, NEW_VALUE)
VALUES (:TID, “KODF”, NULL, NEW.KODF);
IF (NOT (NEW.NAZNACH IS NULL)) THEN
INSERT INTO IBE$LOG_BLOB_FIELDS (LOG_TABLES_ID, FIELD_NAME, OLD_BLOB_VALUE,
NEW_BLOB_VALUE)
VALUES (:TID,”NAZNACH”,NULL, NEW.NAZNACH);
END
^
CREATE TRIGGER IBE$BANKDOC_AU FOR BANKDOC
ACTIVE AFTER UPDATE POSITION 32767
AS
DECLARE VARIABLE TID INTEGER;
BEGIN
TID = GEN_ID(IBE$LOG_TABLES_GEN,1);
INSERT INTO IBE$LOG_TABLES (ID, TABLE_NAME, OPERATION, DATE_TIME, USER_NAME)
VALUES (:TID, “BANKDOC”, “U”, “NOW”, USER);
INSERT INTO IBE$LOG_KEYS (LOG_TABLES_ID, KEY_FIELD, KEY_VALUE)
VALUES (:TID, “IDBANKDOC”, OLD.IDBANKDOC);
IF ((OLD.IDBANKDOC IS NULL AND NEW.IDBANKDOC IS NOT NULL) OR
(NEW.IDBANKDOC IS NULL AND OLD.IDBANKDOC IS NOT NULL) OR
(NEW.IDBANKDOC IS NOT NULL AND OLD.IDBANKDOC IS NOT NULL
AND NEW.IDBANKDOC <> OLD.IDBANKDOC)) THEN
INSERT INTO IBE$LOG_FIELDS (LOG_TABLES_ID, FIELD_NAME, OLD_VALUE, NEW_VALUE)
VALUES (:TID,”IDBANKDOC”,OLD.IDBANKDOC, NEW.IDBANKDOC);
IF ((OLD.SCHETID IS NULL AND NEW.SCHETID IS NOT NULL) OR
(NEW.SCHETID IS NULL AND OLD.SCHETID IS NOT NULL) OR
(NEW.SCHETID IS NOT NULL AND OLD.SCHETID IS NOT NULL
AND NEW.SCHETID <> OLD.SCHETID)) THEN
INSERT INTO IBE$LOG_FIELDS (LOG_TABLES_ID, FIELD_NAME, OLD_VALUE, NEW_VALUE)
VALUES (:TID,”SCHETID”,OLD.SCHETID, NEW.SCHETID);
IF ((OLD.DOCID IS NULL AND NEW.DOCID IS NOT NULL) OR
(NEW.DOCID IS NULL AND OLD.DOCID IS NOT NULL) OR
(NEW.DOCID IS NOT NULL AND OLD.DOCID IS NOT NULL
AND NEW.DOCID <> OLD.DOCID)) THEN
INSERT INTO IBE$LOG_FIELDS (LOG_TABLES_ID, FIELD_NAME, OLD_VALUE, NEW_VALUE)
VALUES (:TID,”DOCID”,OLD.DOCID, NEW.DOCID);
IF ((OLD.SUMMASPISAN IS NULL AND NEW.SUMMASPISAN IS NOT NULL) OR
(NEW.SUMMASPISAN IS NULL AND OLD.SUMMASPISAN IS NOT NULL) OR
(NEW.SUMMASPISAN IS NOT NULL AND OLD.SUMMASPISAN IS NOT NULL
AND NEW.SUMMASPISAN <> OLD.SUMMASPISAN)) THEN
INSERT INTO IBE$LOG_FIELDS (LOG_TABLES_ID, FIELD_NAME, OLD_VALUE, NEW_VALUE)
VALUES (:TID,”SUMMASPISAN”,OLD.SUMMASPISAN, NEW.SUMMASPISAN);
IF ((OLD.SUMMAPRIHOD IS NULL AND NEW.SUMMAPRIHOD IS NOT NULL) OR
(NEW.SUMMAPRIHOD IS NULL AND OLD.SUMMAPRIHOD IS NOT NULL) OR
(NEW.SUMMAPRIHOD IS NOT NULL AND OLD.SUMMAPRIHOD IS NOT NULL
AND NEW.SUMMAPRIHOD <> OLD.SUMMAPRIHOD)) THEN
INSERT INTO IBE$LOG_FIELDS (LOG_TABLES_ID, FIELD_NAME, OLD_VALUE, NEW_VALUE)
VALUES (:TID,”SUMMAPRIHOD”,OLD.SUMMAPRIHOD, NEW.SUMMAPRIHOD);
IF ((OLD.SUMMASPISANNDS IS NULL AND NEW.SUMMASPISANNDS IS NOT NULL) OR
(NEW.SUMMASPISANNDS IS NULL AND OLD.SUMMASPISANNDS IS NOT NULL) OR
(NEW.SUMMASPISANNDS IS NOT NULL AND OLD.SUMMASPISANNDS IS NOT NULL
AND NEW.SUMMASPISANNDS <> OLD.SUMMASPISANNDS)) THEN
INSERT INTO IBE$LOG_FIELDS (LOG_TABLES_ID, FIELD_NAME, OLD_VALUE, NEW_VALUE)
VALUES (:TID,”SUMMASPISANNDS”,OLD.SUMMASPISANNDS, NEW.SUMMASPISANNDS);
IF ((OLD.SUMMAPRIHODNDS IS NULL AND NEW.SUMMAPRIHODNDS IS NOT NULL) OR
(NEW.SUMMAPRIHODNDS IS NULL AND OLD.SUMMAPRIHODNDS IS NOT NULL) OR
(NEW.SUMMAPRIHODNDS IS NOT NULL AND OLD.SUMMAPRIHODNDS IS NOT NULL
AND NEW.SUMMAPRIHODNDS <> OLD.SUMMAPRIHODNDS)) THEN
INSERT INTO IBE$LOG_FIELDS (LOG_TABLES_ID, FIELD_NAME, OLD_VALUE, NEW_VALUE)
VALUES (:TID,”SUMMAPRIHODNDS”,OLD.SUMMAPRIHODNDS, NEW.SUMMAPRIHODNDS);
IF ((OLD.KODF IS NULL AND NEW.KODF IS NOT NULL) OR
(NEW.KODF IS NULL AND OLD.KODF IS NOT NULL) OR
(NEW.KODF IS NOT NULL AND OLD.KODF IS NOT NULL
AND NEW.KODF <> OLD.KODF)) THEN
INSERT INTO IBE$LOG_FIELDS (LOG_TABLES_ID, FIELD_NAME, OLD_VALUE, NEW_VALUE)
VALUES (:TID,”KODF”,OLD.KODF, NEW.KODF);
INSERT INTO IBE$LOG_BLOB_FIELDS (LOG_TABLES_ID, FIELD_NAME, OLD_BLOB_VALUE,
NEW_BLOB_VALUE)
VALUES (:TID,”NAZNACH”,OLD.NAZNACH, NEW.NAZNACH);
END
^
CREATE TRIGGER IBE$BANKDOC_AD FOR BANKDOC
ACTIVE AFTER DELETE POSITION 32767
AS
DECLARE VARIABLE TID INTEGER;
BEGIN
TID = GEN_ID(IBE$LOG_TABLES_GEN,1);
INSERT INTO IBE$LOG_TABLES (ID, TABLE_NAME, OPERATION, DATE_TIME, USER_NAME)
VALUES (:TID, “BANKDOC”, “D”, “NOW”, USER);
INSERT INTO IBE$LOG_KEYS (LOG_TABLES_ID, KEY_FIELD, KEY_VALUE)
VALUES (:TID, “IDBANKDOC”, OLD.IDBANKDOC);
INSERT INTO IBE$LOG_FIELDS (LOG_TABLES_ID, FIELD_NAME, OLD_VALUE, NEW_VALUE)
VALUES (:TID,”IDBANKDOC”,OLD.IDBANKDOC, NULL);
INSERT INTO IBE$LOG_FIELDS (LOG_TABLES_ID, FIELD_NAME, OLD_VALUE, NEW_VALUE)
VALUES (:TID,”SCHETID”,OLD.SCHETID, NULL);
INSERT INTO IBE$LOG_FIELDS (LOG_TABLES_ID, FIELD_NAME, OLD_VALUE, NEW_VALUE)
VALUES (:TID,”DOCID”,OLD.DOCID, NULL);
INSERT INTO IBE$LOG_FIELDS (LOG_TABLES_ID, FIELD_NAME, OLD_VALUE, NEW_VALUE)
VALUES (:TID,”SUMMASPISAN”,OLD.SUMMASPISAN, NULL);
INSERT INTO IBE$LOG_FIELDS (LOG_TABLES_ID, FIELD_NAME, OLD_VALUE, NEW_VALUE)
VALUES (:TID,”SUMMAPRIHOD”,OLD.SUMMAPRIHOD, NULL);
INSERT INTO IBE$LOG_FIELDS (LOG_TABLES_ID, FIELD_NAME, OLD_VALUE, NEW_VALUE)
VALUES (:TID,”SUMMASPISANNDS”,OLD.SUMMASPISANNDS, NULL);
INSERT INTO IBE$LOG_FIELDS (LOG_TABLES_ID, FIELD_NAME, OLD_VALUE, NEW_VALUE)
VALUES (:TID,”SUMMAPRIHODNDS”,OLD.SUMMAPRIHODNDS, NULL);
INSERT INTO IBE$LOG_FIELDS (LOG_TABLES_ID, FIELD_NAME, OLD_VALUE, NEW_VALUE)
VALUES (:TID,”KODF”,OLD.KODF, NULL);
INSERT INTO IBE$LOG_BLOB_FIELDS (LOG_TABLES_ID, FIELD_NAME, OLD_BLOB_VALUE,
NEW_BLOB_VALUE)
VALUES (:TID,”NAZNACH”,OLD.NAZNACH, NULL);
END
^

Як бачите, вийшли досить громіздкі тригера. Це сталися тому, що ми враховуємо не тільки зміни цілої записи, а й кожного поля окремо.

Можна даний скрипти намагатися вводити вручну, але це займе дуже багато часу.

Для того, щоб полегшити Вашу працю є прекрасна програма під назвою IBExpert (до того ж безкоштовна для ex-USSR). Завантажити дану програму Ви можете за адресою: www.ibexpert.com. Ця програма не тільки дозволить згенерувати Вам тригера для ведення логів, описаних в даному розділі, а й переглянути інформацію в логах в потрібних розрізах.

Для генерації логів в IBExpert призначена команда Інструменти -> Менеджер протоколів даних.

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*