Blog programisty w Oracle PL/SQL

Jest to blog eksperymentatora programisty w PL/SQL dla Oracle. Wszystkie kody tutaj zamieszczone mogą być dowolnie wykorzystywane i zmieniane. A jeśli Ktoś z Gości znajdzie błąd, będę niezwykle wdzięczny...
Zapisz

Szukaj na tym blogu

wtorek, 30 listopada 2010

Prosty logger wzorowany na Log4j

Napiszmy sobie prosty logger do obsługi błędów, zapisywania informacji pomocniczych, śledzenia wartości zmiennych..

Założenia:

  1. Poniższy pakiet to prosta wariacja na temat Log4j  stworzona jako pakiet PL/SQL..
  2. Do instalacji wymagane są następujące uprawnienia: CREATE TABLE, CREATE PROCEDURE, CREATE SEQUENCE
  3. Tabele, sekwencja i pakiet  powinny znajdować się w jednym schemacie...
  4. Dostęp do tabeli  logów powinien być read only dla osób trzecich
  5. Wszystkie rekordy zapisywane są w transakcjach autonomicznych - jest to rozwiązanie niewrażliwe na użyty przez użytkownika mechanizm do obsługi transakcji. Każdy zapis to osobna transakcja autonomiczna
  6. Pakiet ma dwie procedury – każda w pięciu wariantach. Procedury PDB_SetLevelLoging służą do ustawienia poziomu logowania (Level  przyjmuje wartości od  Debug po Fatal), analogicznie procedury PDB_Level… służą do zapisu informacji i ewentualnie komentarza jako parametru opcjonalnego…
  7. W przypadku umieszczenia procedur w sekcji obsługi wyjątków do tabeli logu zapisywany jest kod i opis błędu oraz stos wywołania łącznie z linią kodu, w której wystąpił błąd (to powinna być informacja szczególnie przydatna dla programistów).
  8. Kod był testowany dla Oracle w wersji 11g R2  
  9. Logger dobrze radzi sobie z zapisywaniem logów z triggerów- instrukcje DCL w triggerach powodują błędy kompilacji
  10. Kod pakietu jest tak łopatologiczny, że pominę wszelki komentarz…
  11. Autor nie ponosi odpowiedzialności za działanie kodu, można ów kod dowolnie zmieniać i wykorzystywać do celów amatorskich i komercyjnych. Będę wdzięczny za wszelkie uwagi odnośnie poprawności, wydajności itp. ... Użytkownik za pomocą instrukcji SUBSTR może dopasować wszelkie zmienne tekstowe do wielkości pól w tabeli logów..


CREATE TABLE TBL_DICT_LOGGING_LEVEL
(
  ID           NUMBER(1)                        NOT NULL,
  DESCRIPTION  VARCHAR2(50 CHAR)                NOT NULL
);


CREATE UNIQUE INDEX PK_DICT_LOGGING_ID ON TBL_DICT_LOGGING_LEVEL
(ID) LOGGING NOPARALLEL;

ALTER TABLE TBL_DICT_LOGGING_LEVEL ADD (
  CONSTRAINT PK_DICT_LOGGING_ID
PRIMARY KEY (ID)USING INDEX );

CREATE SEQUENCE SEQ_ERROR_LOG
  START WITH 1
  MAXVALUE 999999999999999999999999  MINVALUE 1 NOCYCLE
  CACHE 20  NOORDER;

Insert into TBL_DICT_LOGGING_LEVEL  (ID, DESCRIPTION)
Values   (1, 'Debug logging');
Insert into TBL_DICT_LOGGING_LEVEL(ID, DESCRIPTION)
Values (2, 'Info logging');
Insert into TBL_DICT_LOGGING_LEVEL(ID, DESCRIPTION)
Values(3, 'Warning logging');
Insert into TBL_DICT_LOGGING_LEVEL(ID, DESCRIPTION)
Values(4, 'Error logging');
Insert into TBL_DICT_LOGGING_LEVEL(ID, DESCRIPTION)
Values(5, 'Fatal logging');
COMMIT;

CREATE TABLE TBL_LOGGING
(
  ID                NUMBER(12)    NOT NULL,
  MESSAGE           VARCHAR2(200 CHAR) NOT NULL,
  COMMENTS          VARCHAR2(200 CHAR),
  OSUSER            VARCHAR2(30 CHAR),
  MACHINE           VARCHAR2(64 CHAR),
  PROGRAM           VARCHAR2(64 CHAR),
  ID_LOGGING_LEVEL  NUMBER(1)    NOT NULL,
  SID               NUMBER(6)    NOT NULL,
  ERROR_STACK       VARCHAR2(4000 CHAR),
  ERROR_MESSAGE     VARCHAR2(1000 CHAR),
  USER_NAME         VARCHAR2(30 CHAR)      DEFAULT USER         NOT NULL,
  INSERTION_DATE    DATE                   DEFAULT SYSDATE      NOT NULL
);


--
ALTER TABLE TBL_LOGGING ADD (
  CONSTRAINT FK_LOOGING_ID_LOG
FOREIGN KEY (ID_LOGGING_LEVEL)
REFERENCES TBL_DICT_LOGGING_LEVEL (ID));


CREATE OR REPLACE PACKAGE PCKG_Logging IS
PROCEDURE PDB_TEST;
PROCEDURE PDB_SetDebugLogging;
PROCEDURE PDB_SetInfoLogging;
PROCEDURE PDB_SetWarningLogging;
PROCEDURE PDB_SetErrorLogging;
PROCEDURE PDB_SetFatalLogging;
PROCEDURE PDB_Debug( pc_Message IN VARCHAR2, pc_Comments   IN VARCHAR2 DEFAULT NULL );
PROCEDURE PDB_Info( pc_Message IN VARCHAR2, pc_Comments  IN VARCHAR2 DEFAULT NULL);
PROCEDURE  PDB_Warning( pc_Message IN VARCHAR2, pc_Comments  IN VARCHAR2 DEFAULT NULL);
PROCEDURE PDB_Error( pc_Message IN VARCHAR2, pc_Comments  IN VARCHAR2 DEFAULT NULL);
PROCEDURE PDB_Fatal( pc_Message IN VARCHAR2, pc_Comments  IN VARCHAR2 DEFAULT NULL);
---------------------------
END ;
/
CREATE OR REPLACE PACKAGE BODY PCKG_Logging IS
cn_Debug CONSTANT NUMBER(1) :=1;
cn_Info CONSTANT NUMBER(1) :=2;
cn_Warning CONSTANT NUMBER(1) :=3;
cn_Error CONSTANT NUMBER(1) :=4;
cn_Fatal CONSTANT NUMBER(1) :=5;
vn_CurrentLevel   NUMBER(1);
vc_SID NUMBER;
vc_UserName TBL_LOGGING.USER_NAME%TYPE;
vc_OSUser TBL_LOGGING.osuser%TYPE;
vc_Program TBL_LOGGING.program%TYPE;
vc_Machine TBL_LOGGING.machine%TYPE;
---------------------------------------------
PROCEDURE PDB_TEST
IS
vn_A NUMBER(8);
BEGIN
    vn_A := 1/0;
   
EXCEPTION WHEN OTHERS THEN
   FOr lp IN 1..10000
   LOOP
    PDB_Fatal('błąd');
    PDB_Error('błąd');
   END LOOP;
END;
---------------------------
PROCEDURE PDB_WriteToLog( pc_Message IN VARCHAR2, pc_Comments  IN VARCHAR2, pn_LoggingLevel IN NUMBER)
AS PRAGMA AUTONOMOUS_TRANSACTION;
vc_Sqlerrm tbl_Logging.ERROR_MESSAGE%TYPE;
BEGIN   
 
INSERT INTO TBL_LOGGING (
      ID, MESSAGE, COMMENTS,
OSUSER, MACHINE, PROGRAM,
ID_LOGGING_LEVEL,SID,ERROR_MESSAGE,
ERROR_STACK, USER_NAME, 
      INSERTION_DATE)
VALUES
( SEQ_ERROR_LOG.nextval,SUBSTR(pc_Message, 1,200) , SUBSTR(pc_Comments, 1,200) ,
vc_OSUser,vc_Machine, vc_Program,
pn_LoggingLevel, vc_Sid,SUBSTR(vc_Sqlerrm,1,1000), SUBSTR(DBMS_UTILITY.format_error_backtrace, 1, 4000),
      USER, SYSDATE );
COMMIT;  
EXCEPTION WHEN OTHERS then
    ROLLBACK;
END;
--------------------------------------------------
PROCEDURE PDB_Debug( pc_Message IN VARCHAR2, pc_Comments  IN VARCHAR2) IS
BEGIN
    IF vn_CurrentLevel <= cn_Debug THEN
        PDB_WriteToLog( pc_Message, pc_Comments, cn_Debug);
    END IF;
END;
--------------------------------------------------
PROCEDURE PDB_Info( pc_Message IN VARCHAR2, pc_Comments  IN VARCHAR2) IS
BEGIN
    IF vn_CurrentLevel <= cn_Info THEN
        PDB_WriteToLog( pc_Message, pc_Comments, cn_Info);
    END IF;
END;
--------------------------------------------------
PROCEDURE PDB_Warning( pc_Message IN VARCHAR2, pc_Comments  IN VARCHAR2) IS
BEGIN
    IF vn_CurrentLevel <= cn_Info THEN
        PDB_WriteToLog( pc_Message, pc_Comments, cn_Warning);
    END IF;
END;
--------------------------------------------------
PROCEDURE PDB_Error( pc_Message IN VARCHAR2, pc_Comments  IN VARCHAR2) IS
BEGIN
    IF vn_CurrentLevel <= cn_Error THEN
        PDB_WriteToLog( pc_Message, pc_Comments, cn_Error);
    END IF;
END;
--------------------------------------------------
PROCEDURE PDB_Fatal( pc_Message IN VARCHAR2, pc_Comments  IN VARCHAR2) IS
BEGIN
    IF vn_CurrentLevel <= cn_Fatal THEN
        PDB_WriteToLog( pc_Message, pc_Comments, cn_Fatal);
    END IF;
END;
---------------------------
PROCEDURE PDB_SetDebugLogging IS
BEGIN
    vn_CurrentLevel := cn_Debug;
END;
-----------------------------
PROCEDURE PDB_SetInfoLogging IS
BEGIN
    vn_CurrentLevel := cn_Info;
END;
---------------------------
PROCEDURE PDB_SetWarningLogging IS
BEGIN
    vn_CurrentLevel := cn_Warning;
END;
-----------------------------
PROCEDURE PDB_SetErrorLogging IS
BEGIN
    vn_CurrentLevel := cn_Error;
END;
---------------------------
PROCEDURE PDB_SetFatalLogging IS
BEGIN
    vn_CurrentLevel := cn_Fatal;
END;
---------------------------
BEGIN
    vn_CurrentLevel := cn_Debug;
    SELECT USERENV('sid') INTO vc_SID FROM DUAL;
    SELECT USER,  sys_context('USERENV','OS_USER'),                             sys_context('USERENV','MODULE'), sys_context('USERENV','TERMINAL')
     INTO vc_UserName, vc_OSUser, vc_Program, vc_Machine
        FROM DUAL ;

END ;
/