рдбреЗрдЯрд╛рдмреЗрд╕ рдХреЗ рд▓рд┐рдП рдЖрд╡реЗрджрди рдХрд╛ рдкрджрд╛рдиреБрдХреНрд░рдорд┐рдд рд▓реЙрдЧрд┐рдВрдЧ

рд╕рднреА рдХреЛ рдирдорд╕реНрдХрд╛рд░!


рд▓реЗрдЦ рдореЗрдВ, рдореИрдВ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рд▓реЙрдЧрд┐рдВрдЧ рдХреЗ рд▓рд┐рдП рдПрдХ рджреГрд╖реНрдЯрд┐рдХреЛрдг рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдмрд╛рдд рдХрд░рдирд╛ рдЪрд╛рд╣реВрдВрдЧрд╛, рдЬрд┐рд╕рдиреЗ рдореБрдЭреЗ рдФрд░ рдореЗрд░реЗ рд╕рд╣рдпреЛрдЧрд┐рдпреЛрдВ рдХреЛ рдбрд┐рдмрдЧрд┐рдВрдЧ, рд╕рдорд╕реНрдпрд╛ рдирд┐рд╡рд╛рд░рдг рдФрд░ рдкреНрд░рджрд░реНрд╢рди рд╕рдорд╕реНрдпрд╛рдУрдВ рдХрд╛ рд╡рд┐рд╢реНрд▓реЗрд╖рдг рдХрд░рдиреЗ рдореЗрдВ рдмрд╣реБрдд рдорджрдж рдХреАред Habr├й рдкрд░ рд▓реЙрдЧрд┐рдВрдЧ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдмрд╣реБрдд рд╕рд╛рд░реЗ рдЕрдЪреНрдЫреЗ рд▓реЗрдЦ рд▓рд┐рдЦреЗ рдЧрдП рдереЗ, рдЗрд╕рд▓рд┐рдП, рдпрд╣рд╛рдБ рджреЛрд╣рд░рд╛рдиреЗ рдХреА рдмрд╣реБрдд рдмрд╛рдд рдирд╣реАрдВ рд╣реИред рдореИрдВрдиреЗ рдУрд░реЗрдХрд▓ рдХреЗ рд╕рд╛рде рдЕрдкрдирд╛ рдЖрдИрдЯреА рдХрд░рд┐рдпрд░ рд╢реБрд░реВ рдХрд┐рдпрд╛ рдФрд░ рдореБрдЦреНрдп рд╡рд┐рд╢реЗрд╖рдЬреНрдЮ - рдЯреЙрдо рдХрд╛рдЗрдЯ рдХреА рдкреБрд╕реНрддрдХреЛрдВ рд╕реЗ рдЗрд╕ рдбреЗрдЯрд╛рдмреЗрд╕ рдХрд╛ рдЕрдзреНрдпрдпрди рдХрд┐рдпрд╛ред рдореБрдЭреЗ рдпрд╛рдж рд╣реИ рдХрд┐ "рдкреНрд░рднрд╛рд╡реА рдУрд░реЗрдХрд▓ рдмрд╛рдп рдбрд┐рдЬрд╛рдЗрди" рдкреБрд╕реНрддрдХ рд╕реЗ рд▓реЙрдЧрд┐рдВрдЧ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдЙрдирдХрд╛ рд╡рд╛рдХреНрдпрд╛рдВрд╢:

рдЗрдВрд╕реНрдЯреНрд░реВрдореЗрдВрдЯреЗрд╢рди рдУрд╡рд░рд╣реЗрдб рдирд╣реАрдВ рд╣реИред рдУрд╡рд░рд╣реЗрдб рдПрдХ рдРрд╕реА рдЪреАрдЬ рд╣реИ рдЬрд┐рд╕реЗ рдЖрдк рдмрд┐рдирд╛ рдЬреНрдпрд╛рджрд╛ рдлрд╛рдпрджрд╛ рдЦреЛрдП рдирд┐рдХрд╛рд▓ рд╕рдХрддреЗ рд╣реИрдВред рд╣рдЯрд╛рдиреЗ (рдпрд╛ рдирд╣реАрдВ) рдЗрдВрд╕реНрдЯреНрд░реВрдореЗрдВрдЯреЗрд╢рди рдХрд╛рдлреА рдХрд╛рд░реНрдпрдХреНрд╖рдорддрд╛ рдХреЛ рджреВрд░ рд▓реЗ рдЬрд╛рддрд╛ рд╣реИред рдЖрдкрдХреЛ рдпрд╣ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдирд╣реАрдВ рд╣реЛрдЧреА рдпрджрд┐ рдЖрдкрдХреЗ рд╕рд┐рд╕реНрдЯрдо рдХрднреА рдирд╣реАрдВ рдЯреВрдЯреЗрдВрдЧреЗ, рдХрднреА рдирд┐рджрд╛рди рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдирд╣реАрдВ рд╣реЛрдЧреА, рдФрд░ рдкреНрд░рджрд░реНрд╢рди рдХреЗ рдореБрджреНрджреЛрдВ рд╕реЗ рдХрднреА рднреА рдкреАрдбрд╝рд┐рдд рдирд╣реАрдВ рд╣реЛрдВрдЧреЗред рдпрджрд┐ рдпрд╣ рд╕рдЪ рд╣реИ, рддреЛ рдЖрдкрдХреЛ рдЕрдкрдиреЗ рд╕рд┐рд╕реНрдЯрдо рдХреЛ (рдФрд░ рдореБрдЭреЗ рдЕрдкрдирд╛ рдИрдореЗрд▓ рдкрддрд╛ рднреЗрдЬрдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдирд╣реАрдВ рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рдореЗрд░реЗ рдкрд╛рд╕ рдЖрдкрдХреЗ рд▓рд┐рдП рдиреМрдХрд░реА рдХрд╛ рдкреНрд░рд╕реНрддрд╛рд╡ рд╣реИ)ред


рдУрд░реЗрдХрд▓ рдкрд░рд┐рдпреЛрдЬрдирд╛рдУрдВ рдкрд░ рдХрд╛рдо рдХреЗ рд╕рд╛рде, рдпрд╣ рд╕рдм рд╢реБрд░реВ рд╣реБрдЖред


рдУрд░реЗрдХрд▓ рдореЗрдВ рдкрджрд╛рдиреБрдХреНрд░рдо рд▓реЙрдЧрд┐рдВрдЧ


рдХреБрдЫ рд╕рдордп рдкрд╣рд▓реЗ рдореИрдВ рдУрд░реЗрдХрд▓ рдкреНрд▓реЗрдЯрдлрд╝реЙрд░реНрдо рдкрд░ рдПрдХ рдмрдбрд╝рд╛ рдбреЗрдЯрд╛ рд╡реЗрдпрд░рд╣рд╛рдЙрд╕ рд╡рд┐рдХрд╕рд┐рдд рдХрд░рдиреЗ рдХреА рдкрд░рд┐рдпреЛрдЬрдирд╛ рдкрд░ рдХрд╛рдо рдХрд░ рд░рд╣рд╛ рдерд╛ред рдбреЗрдЯрд╛рдмреЗрд╕ рдореЗрдВ рдкреАрдПрд▓ / рдПрд╕рдХреНрдпреВрдПрд▓ рдореЗрдВ рдбреЗрдЯрд╛ рд▓реЛрдб рдХрд░рдиреЗ, рдЬрд╛рдВрдЪрдиреЗ, рд╕рдореГрджреНрдз рдХрд░рдиреЗ рдХрд╛ рддрд░реНрдХ рд▓рд╛рдЧреВ рдХрд┐рдпрд╛ рдЧрдпрд╛ рдерд╛ред рд▓реЙрдЧрд┐рдВрдЧ рдХреЗ рд╕рдВрдмрдВрдз рдореЗрдВ, рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рд╡рд┐рдЪрд╛рд░ рдкреНрд░рд╕реНрддрд╛рд╡рд┐рдд рдХрд┐рдпрд╛ рдЧрдпрд╛ рдерд╛ред рдпрджрд┐ рдЖрдк рдХрд┐рд╕реА рднреА рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдХреЛ рд╡рд┐рд╢реНрд╡ рд╕реНрддрд░ рдкрд░ рджреЗрдЦрддреЗ рд╣реИрдВ, рддреЛ рдпрд╣ рдлрд╝рдВрдХреНрд╢рди / рдкреНрд░рдХреНрд░рд┐рдпрд╛рдУрдВ (рд╡рд┐рдзрд┐рдпреЛрдВ) рдХреЗ рд▓рд┐рдП рдХреЙрд▓ рдХрд╛ рдПрдХ рдкреЗрдбрд╝ рд╣реИред рдЙрд╕реА рд╕рдордп, рдЕрдкрдиреЗ рдЖрдк рдореЗрдВ рдПрдХ рд╡рд┐рдзрд┐ рдХрдИ рдмрд╛рд▓ рд╡рд┐рдзрд┐рдпреЛрдВ рдХреЛ рдХреЙрд▓ рдХрд░ рд╕рдХрддреА рд╣реИ, рд▓реЗрдХрд┐рди рдЗрд╕рдХреА рдХреЙрд▓ рдПрдХ рдореВрд▓ рд╡рд┐рдзрд┐ рд╕реЗ рд╣реЛрддреА рд╣реИред рдЗрд╕ рдкреНрд░рдХрд╛рд░, рд╣рдо рддрд░реАрдХреЗ (рдХрд╣рд╛ рдЬрд╛рддрд╛ рд╣реИ /) рд╣реИ, рдЬреЛ рд╕реНрд╡рд╛рднрд╛рд╡рд┐рдХ рд░реВрдк рд╕реЗ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рдбреЗрдЯрд╛рдмреЗрд╕ рдореЗрдВ рдореЙрдбрд▓рд┐рдВрдЧ рдХреА рд╣реИ рдХреЗ рдмреАрдЪ рдорд╛рддрд╛ рдкрд┐рддрд╛ / рдмрдЪреНрдЪреЗ рдкрджрд╛рдиреБрдХреНрд░рдо рдорд┐рд▓ рдЖрдИрдбреА , PARENT_ID рдХреНрд╖реЗрддреНрд░реЛрдВ , рдЬрд╣рд╛рдВ PARENT_ID рдкрд░ рд╡рд┐рджреЗрд╢реА рдХреБрдВрдЬреА рд╣реИ рдЖрдИрдбреА рдХреНрд╖реЗрддреНрд░ред рдпрд╣ рдареАрдХ рд╡реИрд╕рд╛ рд╣реА рд╕рдВрдмрдВрдз рд╣реИ рдЬреИрд╕рд╛ рдХрд┐ рдХрд░реНрдордЪрд╛рд░рд┐рдпреЛрдВ рдФрд░ рдЙрдирдХреЗ рдкреНрд░рдмрдВрдзрдХреЛрдВ рдХреЗ рд╕рд╛рде рдХреНрд▓рд╛рд╕рд┐рдХ рдЙрджрд╛рд╣рд░рдг рдореЗрдВ (рдПрдХ рдХрд░реНрдордЪрд╛рд░реА рдореЗрдВ рдХреЗрд╡рд▓ рдПрдХ рдкреНрд░рдмрдВрдзрдХ рд╣реЛ рд╕рдХрддрд╛ рд╣реИ, рдПрдХ рдкреНрд░рдмрдВрдзрдХ рдХреЗ рдкрд╛рд╕ рдХрдИ рдХрд░реНрдордЪрд╛рд░реА рд╣реЛ рд╕рдХрддреЗ рд╣реИрдВ)ред рд▓реЙрдЧрд┐рдВрдЧ рдпреЛрдЬрдирд╛ рдХреЛ рдирд┐рдореНрдирд╛рдиреБрд╕рд╛рд░ рд╡рд░реНрдгрд┐рдд рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ:


create table log_instances (
  start_log_id number(16) not null
  , log_instance_name varchar2(100) not null
  , start_ts timestamp(6) not null
  , end_ts timestamp(6)
  , status varchar2(1) not null
  , log_date date not null,
  , constraint log_instances_pk primary key(start_log_id))
/

create index log_instances_name_idx on log_instances(log_instance_name)
/

create table log_table (
  action_name varchar2(64) not null,
  log_id  NUMBER(16) not null,
  parent_log_id NUMBER(16),
  start_ts timestamp(6) not null,
  end_ts timestamp(6),
  status varchar2(1) not null,
  comments varchar2(4000),
  exception_message varchar2(4000),
  large_text CLOB,
  log_date date not null,
  constraint log_table_status_chck check (status in ('C'/*completed*/, 'R'/*running*/, 'F'/*failed*/))
)
partition by range (log_date)
interval(NUMTODSINTERVAL(7,'day'))
(partition p_fst_day_of_week values less than (date '...'))
/

create index log_table_log_id_idx on log_table(log_id) local
/

create index log_table_parent_id_idx on log_table(parent_log_id) local
/

create index log_table_action_name_idx on log_table(action_name) local
/

рдкрд╣рд▓реЗ log_instances рддрд╛рд▓рд┐рдХрд╛ рдореЗрдВ рдкреНрд░рддреНрдпреЗрдХ рдЕрдиреБрдкреНрд░рдпреЛрдЧ рдкреНрд░рд╛рд░рдВрдн рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдЬрд╛рдирдХрд╛рд░реА рд╣реЛрддреА рд╣реИ:


  • log_instance_name - рдирд╛рдо, рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, "sample_app_20200501"
  • start_ts / end_ts - рд▓реЙрдЧрд┐рдВрдЧ рдХрд╛ рдкреНрд░рд╛рд░рдВрдн рдФрд░ рд╕рдорд╛рдкреНрддрд┐ рд╕рдордп ( рдпрджрд┐ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рд╡рд░реНрддрдорд╛рди рдореЗрдВ рдЪрд▓ рд░рд╣рд╛ рд╣реИ рддреЛ end_ts рдЦрд╛рд▓реА рд╣реИ)
  • рд╕реНрдерд┐рддрд┐ - 'рд╕реА' (рдкреВрд░реНрдг) - рддреНрд░реБрдЯрд┐рдпреЛрдВ рдХреЗ рдмрд┐рдирд╛ рд╕рдорд╛рдкреНрдд рдХрд┐рдпрд╛ рдЧрдпрд╛ рдЖрд╡реЗрджрди, 'рдЖрд░' (рдЪрд▓ рд░рд╣рд╛ рд╣реИ) - рдЖрд╡реЗрджрди рдЪрд▓ рд░рд╣рд╛ рд╣реИ, 'рдПрдл' (рд╡рд┐рдлрд▓) - рдЖрд╡реЗрджрди рдПрдХ рддреНрд░реБрдЯрд┐ рдХреЗ рд╕рд╛рде рд╕рдорд╛рдкреНрдд рд╣реБрдЖред
  • start_log_id рдХреЙрд▓ рдкрджрд╛рдиреБрдХреНрд░рдо рдореЗрдВ рд░реВрдЯ рдЖрдИрдбреА рд╣реИред

log_table .


  • action_name тАФ
  • start_ts/end_ts тАФ /
  • status тАФ 'C' (completed) тАФ , 'R'(running) тАФ , 'F'(failed) тАФ .
  • exception_message тАФ , error stack trace ,

API ( Oracle API / ). :


  • PROCEDURE start_logging(instance_name) тАФ , , log_instances
  • PROCEDURE open_next_level(action_name, comments, clob_text) тАФ . log_table
  • PROCEDURE close_level_success тАФ 'C'
  • PROCEDURE close_level_fail тАФ 'F'
  • PROCEDURE stop_log_success тАФ 'C'
  • PROCEDURE stop_log_fail тАФ 'F'


( Oracle ):


  • id
  • id, INSERT/UPDATE log_table
  • id log_table ,

pk_util_log . API :
-info тАФ open_level close_level. - logger.info Java.
-resume_logging (parent_log_id) тАФ . id log_table.



DECLARE
    PROCEDURE b(p_name_in IN VARCHAR2) IS
        v_dummy_cnt PLS_INTEGER;
    BEGIN
        pk_util_log.open_next_level(p_action_name_in => 'In procedure b()',
                        p_comments_in => 'procedure B(), line: ' || $$PLSQL_LINE || chr(13) || chr(10) ||
                                                     'p_name_in: ' || p_name_in);
        dbms_lock.sleep(3);
        pk_util_log.close_level_success;
    EXCEPTION
        WHEN OTHERS THEN
            pk_util_log.close_level_fail;
            RAISE;
    END;

    PROCEDURE a(p_name_in IN VARCHAR2) IS
    BEGIN
        pk_util_log.open_next_level(p_action_name_in => 'In procedure a()',
                            p_comments_in => 'procedure A(), line: ' || $$PLSQL_LINE || chr(13) || chr(10) ||
                                    'p_name_in: ' || p_name_in);
        b('dummy_b');
        dbms_lock.sleep(2);
        pk_util_log.close_level_success;
    EXCEPTION
        WHEN OTHERS THEN
            pk_util_log.close_level_fail;
            RAISE;
    END;

BEGIN
    pk_util_log.start_logging('sample_app_20200501');
    dbms_output.put_line(pk_util_log.get_start_log_id);
    a('dummy_a');
    pk_util_log.stop_log_success;
exception
    when others then
        pk_util_log.stop_log_fail;
        raise;
END;
/

start_log_id:


select start_log_id from log_instances where log_instance_name = 'sample_app_20200501';

start_log_id :


SELECT
    LPAD (' ', 2* (LEVEL- 1)) || l.action_name as action_name,
    l.status,
    l.start_ts,
    l.end_ts,
    l.comments
FROM
    tech_log_table l
START WITH l.log_id = 204 /*start_log_id*/
CONNECT BY
    l.parent_log_id = PRIOR l.log_id
ORDER SIBLINGS BY
    l.log_id ASC;

ACTION_NAMESTATUSSTART_TSEND_TSCOMMENTS
sample_app_20200501C2020-05-01 16:37:46.7531682020-05-01 16:37:51.755380NULL
In procedure a()C2020-05-01 16:37:46.7535542020-05-01 16:37:51.754649procedure A(), line: 19
p_name_in: dummy_a
In procedure b()C2020-05-01 16:37:46.7538692020-05-01 16:37:49.753746procedure B(), line: 6
p_name_in: dummy_b

, a(p_name_in) b(p_name_in).



  1. , , , .
  2. , , . : , .
  3. , , . SQL . email id .

:


  1. DML log_table (log_id). log_id sequence Oracle, . sequence .
  2. log_table , . Oracle , . , .

Oracle , Java. Java API.


Java


, , . PostgreSql Oracle Open Source.


, Connection , Connection Pool ( HikariCP).


. Java thread'. ThreadLocal, InheritableThreadLocal, thread .


API


:


  • public static void startLog(final String logInstanceName)
  • public static void openNextLevel(final String actionName, final String comments)
  • public static void stopLogSuccess()
  • public static void stopLogFail(final Exception exception)
  • public static void closeLevelSuccess()
  • public static void closeLevelFail(final Exception exception)

:


public class A {
    public static void main(String[] args) {
        try {
            LogUtils.startLog("sample_app_20200501");
            A a = new A();
            a.method1("some string");
            LogUtils.stopLogSuccess();
        } catch (Exception e) {
            LogUtils.stopLogFail(e);
            throw new RuntimeException(e);
        }
    }

    private String method1(String s) {
        try {
            LogUtils.openNextLevel("method1", "Arguments: " + s);
            method2(1, 2);
            LogUtils.closeLevelSuccess();
            return s;
        } catch (Exception e) {
            LogUtils.closeLevelFail(e);
            throw new RuntimeException(e);
        }
    }

    private long method2(int i1, int i2) {
        try {
            LogUtils.openNextLevel("method2", "Arguments: " + i1 + ", " + i2);
            LogUtils.closeLevelSuccess();
            return i1 + i2;
        } catch (Exception e) {
            LogUtils.closeLevelFail(e);
            throw new RuntimeException(e);
        }
    }

}

, log_instances id (start_log_id) . PostgreSql RECURSIVE WITH:


WITH RECURSIVE log AS (
    SELECT 1                as level,
           ARRAY [l.log_id] AS path,
           l.log_id,
           l.action_name,
           l.parent_log_id,
           l.start_ts,
           l.end_ts,
           l.status,
           l.comments,
           l.exception_message
    FROM log_table l
    WHERE l.log_id = ...
    UNION ALL
    SELECT l.level + 1 as level,
           path || l1.log_id,
           l1.log_id,
           l1.action_name,
           l1.parent_log_id,
           l1.start_ts,
           l1.end_ts,
           l1.status,
           l1.comments,
           l1.exception_message
    FROM log_table l1
             INNER JOIN log l ON l.log_id = l1.parent_log_id
)
SELECT
       lpad(' ', (l.level - 1) * 2) || l.log_id as log_id,
       l.action_name,
       l.start_ts,
       l.end_ts,
       l.end_ts - l.start_ts as duration,
       l.status,
       l.comments,
       l.exception_message
FROM log l
order by l.path, l.start_ts;

Boilerplate Aspect Oriented Programming


, , , exception :


try {
    LogUtils.openNextLevel(...);
    ...
    LogUtils.closeLevelSuccess();
} catch (Exception e) {
    LogUtils.closeLevelFail(e);
    throw new RuntimeException(e);
}

"" , . code templates, IDE . : Aspect Oriented Programming (AOP). , , AOP тАФ , (cross-cutting concerns). , , . AOP (advice) , (pointcut). pointcut advice . , . (weave) : compile-time, post-compile, load-time. compile-time weaving, AOP AspectJ.


, @LogToDb, , . :


@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LogToDb {
    boolean suppressLogArgs() default false;
    boolean suppressLogResult() default false;
}

:


@Aspect
public class LogToDbAspect {
    @Pointcut("@annotation(logToDb) && execution(* *.*(..))")
    public void logToDbPointcut(LogToDb logToDb) {
    }

    @Around(value = "logToDbPointcut(logToDb)")
    public Object around(ProceedingJoinPoint pjp, LogToDb logToDb) throws Throwable {
        try {
            LogUtils.openNextLevel(pjp.getSignature().toShortString(),
                    logToDb.suppressLogArgs() ? null : AspectUtils.getArgsString(pjp.getArgs()));
            Object result = pjp.proceed();
            if (!logToDb.suppressLogResult()) {
                LogUtils.addComments("\nResult: " + result.toString());
            }
            LogUtils.closeLevelSuccess();
            return result;
        } catch (Exception e) {
            LogUtils.closeLevelFail(e);
            throw new RuntimeException(e);
        }
    }
}

boilerplate :


private String method1(String s) {
    try {
        LogUtils.openNextLevel("method1", "Arguments: " + s);
        LogUtils.addComments("\nResult: " + s);
        LogUtils.closeLevelSuccess();
        return s;
    } catch (Exception e) {
        LogUtils.closeLevelFail(e);
        throw new RuntimeException(e);
    }
}

:


@LogToDb
private String method1(String s) {
    return s;
}

:


  1. compile-time weaving aspectjc.
  2. aspectjc , . aspectjc Lombok. :

[WARNING] You aren't using a compiler supported by lombok, so lombok will not work and has been disabled.
Your processor is: org.aspectj.org.eclipse.jdt.internal.compiler.apt.dispatch.BatchProcessingEnvImpl
Lombok supports: sun/apple javac 1.6, ECJ

lombok-maven-plugin, aspectjc lombok.



:


  1. :
    • exception
    • parent/child (/)
  2. :
    • SQL ,
    • ,
  3. :
    • boilerplate
  4. Java :
    • . DevOps .
    • boilerplate AOP, ( , runtime).
    • , . Connection pool, , , тАФ . . DML .


GitHub:


рдУрд░реЗрдХрд▓ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди:


  1. https://github.com/nikita-mospan/plsql-liquibase-utplsql/blob/master/src/main/resources/oracle/tech_user/packages/pk_util_log.pks
  2. https://github.com/nikita-mospan/plsql-liquibase-utplsql/blob/master/src/main/resources/oracle/tech_user/packages/pk_util_log.pkb

рдЬрд╛рд╡рд╛ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди:


  1. https://github.com/nikita-mospan/log-to-db

All Articles