Как перенаправить вывод DBMS_OUTPUT.PUT_LINE в файл?

Мне нужно отладить в pl/sql, чтобы вычислить время процедур, которые я хочу использовать:

SELECT systimestamp FROM dual INTO time_db;
DBMS_OUTPUT.PUT_LINE('time before procedure ' || time_db);

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


person Community    schedule 21.09.2009    source источник


Ответы (10)


DBMS_OUTPUT — не лучший инструмент для отладки, поскольку в большинстве сред он изначально не используется. Однако, если вы хотите получить вывод DBMS_OUTPUT, вы просто используете процедуру DBMS_OUTPUT.get_line.

Вот небольшой пример:

SQL> create directory tmp as '/tmp/';

Directory created

SQL> CREATE OR REPLACE PROCEDURE write_log AS
  2     l_line VARCHAR2(255);
  3     l_done NUMBER;
  4     l_file utl_file.file_type;
  5  BEGIN
  6     l_file := utl_file.fopen('TMP', 'foo.log', 'A');
  7     LOOP
  8        EXIT WHEN l_done = 1;
  9        dbms_output.get_line(l_line, l_done);
 10        utl_file.put_line(l_file, l_line);
 11     END LOOP;
 12     utl_file.fflush(l_file);
 13     utl_file.fclose(l_file);
 14  END write_log;
 15  /

Procedure created

SQL> BEGIN
  2     dbms_output.enable(100000);
  3     -- write something to DBMS_OUTPUT
  4     dbms_output.put_line('this is a test');
  5     -- write the content of the buffer to a file
  6     write_log;
  7  END;
  8  /

PL/SQL procedure successfully completed

SQL> host cat /tmp/foo.log

this is a test
person Vincent Malgrat    schedule 21.09.2009
comment
Как вы определяете переменные l_line VARCHAR2(255); l_done NUMBER; l_file utl_file.file_type; ? - person David Kakauridze; 27.11.2012
comment
VARCHAR2, NUMBER — это стандартные типы данных SQL. UTL_FILE — это стандартный пакет. - person Vincent Malgrat; 27.11.2012

В качестве альтернативы записи в файл, как насчет записи в таблицу? Вместо вызова DBMS_OUTPUT.PUT_LINE вы можете вызвать свою собственную процедуру DEBUG.OUTPUT примерно так:

procedure output (p_text varchar2) is
   pragma autonomous_transaction;
begin
   if g_debugging then
      insert into debug_messages (username, datetime, text)
      values (user, sysdate, p_text);
      commit;
   end if;
end;

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

Логическая переменная g_debugging — это переменная пакета, для которой по умолчанию можно установить значение false и установить значение true, когда требуется отладочный вывод.

Конечно, вам нужно управлять этой таблицей, чтобы она не росла вечно! Одним из способов может быть задание, которое выполняется каждую ночь/еженедельно и удаляет все «старые» отладочные сообщения.

person Tony Andrews    schedule 21.09.2009
comment
+1 за ответ на реальный основной вопрос (как войти в Oracle?) вместо навязчивого вопроса. - person Andrew Spencer; 12.02.2014

Если вы просто тестируете свой PL/SQL в SQL Plus, вы можете направить его в такой файл:

spool output.txt
set serveroutput on

begin
  SELECT systimestamp FROM dual INTO time_db;
  DBMS_OUTPUT.PUT_LINE('time before procedure ' || time_db);
end;
/

spool off

Такие IDE, как Toad и SQL Developer, могут захватывать выходные данные другими способами, но я не знаю, как это сделать.

person Tony Andrews    schedule 21.09.2009
comment
спасибо, но я тестирую другое приложение, использующее базу данных pl/sql, и мне нужно, чтобы информация сохранялась в файл, пока сценарий .sql не запускается из sqldeveloper, есть идеи? - person ; 21.09.2009
comment
Что ж, вы можете получить выходные данные, созданные DBMS_OUTPUT в вызывающем приложении, вызвав DBMS_OUTPUT.ENABLE перед записью каких-либо сообщений, а затем вызвав DBMS_OUTPUT.GET_LINE или GET_LINES. Но чтобы затем поместить эту информацию в файл, потребуется открыть/записать/закрыть собственный файл, например. используя UTL_FILE - в этом случае вы также можете использовать UTL_FILE в первую очередь! - person Tony Andrews; 21.09.2009

использовать set serveroutput on;

Например:

set serveroutput on;

DECLARE
x NUMBER;
BEGIN
x := 72600;
dbms_output.put_line('The variable X = '); dbms_output.put_line(x);
END;
person Mahmoud Hanafy    schedule 16.04.2013
comment
Это не ответило на вопрос. - person luis.espinal; 03.04.2018

В дополнение к ответу Тони, если вы хотите узнать, на что ваша программа PL/SQL тратит свое время, также стоит проверить эта часть документации Oracle PL/SQL.

person Ian Carpenter    schedule 21.09.2009
comment
+1 для DBMS_PROFILER. Всегда лучше использовать встроенный Oracle, когда это возможно. - person APC; 21.09.2009
comment
Да, это очень полезный совет. - person J. Chomel; 25.01.2017

Использование UTL_FILE вместо DBMS_OUTPUT перенаправит вывод в файл:

http://oreilly.com/catalog/oraclebip/chapter/ch06.html

person Rafa de Castro    schedule 21.09.2009
comment
спасибо за быстрый ответ :) Я попытаюсь попробовать (но звучит очень сложно заставить это работать), у вас есть идеи, как распечатать DBMS_OUTPUT в файл (это может быть проще) - person ; 21.09.2009
comment
Ссылка мертва сейчас, - person Fering; 10.02.2020

В качестве примечания помните, что весь этот вывод генерируется на стороне сервера.

Используя DBMS_OUTPUT, текст генерируется на сервере во время выполнения вашего запроса и сохраняется в буфере. Затем он перенаправляется в ваше клиентское приложение, когда сервер завершает получение данных запроса. То есть вы получите эту информацию только тогда, когда запрос завершится.

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

Надеюсь это поможет.

person Juan Calero    schedule 21.09.2009

Можно записать файл непосредственно на сервер БД, на котором размещена ваша база данных, и это изменится вместе с выполнением вашей программы PL/SQL.

При этом используется каталог Oracle TMP_DIR; вы должны объявить его и создать следующую процедуру:

CREATE OR REPLACE PROCEDURE write_log(p_log varchar2)
  -- file mode; thisrequires
--- CREATE OR REPLACE DIRECTORY TMP_DIR as '/directory/where/oracle/can/write/on/DB_server/';
AS
  l_file utl_file.file_type;
BEGIN
  l_file := utl_file.fopen('TMP_DIR', 'my_output.log', 'A');
  utl_file.put_line(l_file, p_log);
  utl_file.fflush(l_file);
  utl_file.fclose(l_file);
END write_log;
/

Вот как это использовать:

1) Запустите это из вашего клиента SQL * PLUS:

BEGIN
  write_log('this is a test');
  for i in 1..100 loop
    DBMS_LOCK.sleep(1);
    write_log('iter=' || i);
  end loop;
  write_log('test complete');
END;
/

2) на сервере базы данных откройте оболочку и

    tail -f -n500 /directory/where/oracle/can/write/on/DB_server/my_output.log
person J. Chomel    schedule 23.09.2016

Старая тема, но есть и другая альтернатива.

Начиная с 9i вы можете использовать конвейерную табличную функцию.

Сначала создайте тип в виде таблицы varchar:

CREATE TYPE t_string_max IS TABLE OF VARCHAR2(32767);

Во-вторых, оберните свой код в объявление конвейерной функции:

CREATE FUNCTION fn_foo (bar VARCHAR2) -- your params
  RETURN t_string_max PIPELINED IS 
  -- your vars
BEGIN
  -- your code
END;
/

Замените все DBMS_OUTPUT.PUT_LINE на PIPE ROW.

Наконец, назовите это так:

SELECT * FROM TABLE(fn_foo('param'));

Надеюсь, поможет.

person Felypp Oliveira    schedule 20.07.2016

Попробуй это:

SELECT systimestamp INTO time_db FROM dual ;

DBMS_OUTPUT.PUT_LINE('time before procedure ' || time_db);
person Mark    schedule 16.05.2013
comment
Почему мы должны попробовать это в любом случае? - person J. Chomel; 25.01.2017