Oracle Database 강좌

오라클에서 디버깅 로그를 테이블에 저장하기: 프로시저명과 라인 번호 추가

_Blue_Sky_ 2025. 3. 17. 00:30
728x90
 
오라클에서 DBMS_OUTPUT.PUT_LINE의 출력을 테이블에 저장하면서, 단순히 메시지뿐만 아니라 호출된 프로시저명라인 번호를 함께 기록하고 싶다면, PL/SQL의 내장 기능을 활용해 호출 스택을 분석해야 합니다. 이를 위해 DBMS_UTILITY.FORMAT_CALL_STACK을 사용하면 호출 경로와 라인 번호를 추출할 수 있습니다. 아래에서 이를 구현하는 방법을 단계별로 정리하겠습니다.

목표
  • DBMS_OUTPUT.PUT_LINE 대신 커스텀 프로시저를 사용해 디버깅 메시지를 테이블에 저장.
  • 메시지와 함께 호출된 프로시저명, 라인 번호를 기록.
  • 실시간 디버깅과 영구 로그를 모두 지원.

728x90
1. 로그 테이블 생성
먼저, 메시지뿐만 아니라 프로시저명과 라인 번호를 저장할 수 있도록 테이블을 설계합니다.
CREATE TABLE debug_log (
    log_id NUMBER GENERATED ALWAYS AS IDENTITY,
    log_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    procedure_name VARCHAR2(100),
    line_number NUMBER,
    message VARCHAR2(4000)
);

2. 커스텀 디버깅 프로시저 작성
DBMS_UTILITY.FORMAT_CALL_STACK을 활용해 호출 스택에서 프로시저명과 라인 번호를 추출하고, 이를 테이블에 삽입하는 프로시저를 작성합니다.
CREATE OR REPLACE PROCEDURE custom_put_line(p_message IN VARCHAR2)
AS
    l_call_stack VARCHAR2(4000);
    l_lines DBMS_UTILITY.NAME_ARRAY;
    l_line_count NUMBER;
    l_procedure_name VARCHAR2(100);
    l_line_number NUMBER;
BEGIN
    -- 호출 스택 가져오기
    l_call_stack := DBMS_UTILITY.FORMAT_CALL_STACK;
    
    -- 호출 스택을 줄 단위로 분리
    l_line_count := REGEXP_COUNT(l_call_stack, CHR(10));
    FOR i IN 1..l_line_count LOOP
        l_lines(i) := REGEXP_SUBSTR(l_call_stack, '[^\n]+', 1, i);
    END LOOP;

    -- 호출한 프로시저명과 라인 번호 추출 (최근 호출자 정보는 상위 몇 줄에 있음)
    FOR i IN 1..l_line_count LOOP
        IF REGEXP_LIKE(l_lines(i), 'procedure|function|anonymous') THEN
            l_procedure_name := REGEXP_SUBSTR(l_lines(i), '[^. ]+$'); -- 마지막 단어 (프로시저명)
            l_line_number := TO_NUMBER(REGEXP_SUBSTR(l_lines(i-1), '\d+')); -- 이전 줄의 라인 번호
            EXIT;
        END IF;
    END LOOP;

    -- 테이블에 삽입
    INSERT INTO debug_log (procedure_name, line_number, message)
    VALUES (l_procedure_name, l_line_number, p_message);
    COMMIT; -- 필요에 따라 제거 가능

    -- 콘솔에도 출력
    DBMS_OUTPUT.PUT_LINE(p_message || ' (from ' || l_procedure_name || ' at line ' || l_line_number || ')');
EXCEPTION
    WHEN OTHERS THEN
        INSERT INTO debug_log (procedure_name, line_number, message)
        VALUES ('ERROR_HANDLER', 0, 'Error in custom_put_line: ' || SQLERRM);
        COMMIT;
        RAISE;
END custom_put_line;
/

3. 사용 예제
작성한 custom_put_line을 호출해 테스트합니다.
 
CREATE OR REPLACE PROCEDURE test_debug
AS
BEGIN
    custom_put_line('This is a test message');
END test_debug;
/

BEGIN
    test_debug;
END;
/

-- 결과 확인
SELECT * FROM debug_log;
예상 출력 (debug_log 테이블):
 
LOG_ID  LOG_TIME            PROCEDURE_NAME  LINE_NUMBER  MESSAGE
1       2025-03-17 10:00:00 test_debug      4            This is a test message
콘솔 출력:
 
This is a test message (from test_debug at line 4)

4. 코드 설명
  • DBMS_UTILITY.FORMAT_CALL_STACK: 호출 스택을 문자열로 반환합니다. 예:
     
    ----- PL/SQL Call Stack -----
      object      line  object
    handle    number name
    0x12345678     3  anonymous block
    0xabcdef12     4  procedure TEST_DEBUG
  • 정규 표현식: REGEXP_SUBSTRREGEXP_LIKE를 사용해 스택에서 프로시저명과 라인 번호를 추출.
  • 테이블 삽입: INSERT INTO debug_log로 모든 정보를 기록.
  • 예외 처리: 오류 발생 시에도 로그를 남김.

 
 
5. 주의사항
  • 성능: 호출 스택 파싱은 약간의 오버헤드가 있으므로, 대량 호출 시 성능 테스트 필요.
  • 정확성: 스택의 구조는 환경에 따라 다를 수 있으니, 추출 로직을 조정해야 할 수도 있음.
  • 대체 방법: 더 정확한 라인 번호를 위해 $$PLSQL_LINE (현재 라인 번호)과 $$PLSQL_UNIT (현재 유닛 이름)을 사용할 수도 있음:
    INSERT INTO debug_log (procedure_name, line_number, message)
    VALUES ($$PLSQL_UNIT, $$PLSQL_LINE, p_message);
    하지만 이는 호출자의 정보가 아닌 현재 프로시저의 정보만 제공합니다.

6. 확장 가능성
  • 추가 정보: 세션 ID(SYS_CONTEXT('USERENV', 'SESSIONID'))나 사용자명(USER)을 추가.
  • 로그 레벨: 메시지에 레벨(예: INFO, ERROR)을 추가해 필터링 가능.
 

 

728x90