有没有办法刷新 Oracle 中 PL/SQL 的输出?

发布于 2024-08-06 04:24:18 字数 157 浏览 6 评论 0原文

我有一个从 shell 脚本中调用的 SQL 脚本,需要很长时间才能运行。它当前在不同点包含 dbms_output.put_line 语句。这些打印语句的输出会出现在日志文件中,但仅在脚本完成后才会出现。

有什么方法可以确保脚本运行时输出出现在日志文件中?

I have an SQL script that is called from within a shell script and takes a long time to run. It currently contains dbms_output.put_line statements at various points. The output from these print statements appear in the log files, but only once the script has completed.

Is there any way to ensure that the output appears in the log file as the script is running?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

澜川若宁 2024-08-13 04:24:18

并不真地。 DBMS_OUTPUT 的工作方式是这样的:您的 PL/SQL 块在数据库服务器上执行,不与客户端交互。因此,当您调用 PUT_LINE 时,它只是将该文本放入服务器内存中的缓冲区中。当您的 PL/SQL 块完成时,控制权将返回给客户端(在本例中我假设是 SQLPlus);此时,客户端通过调用 GET_LINE 从缓冲区中获取文本并显示它。

因此,使输出更频繁地出现在日志文件中的唯一方法是将大型 PL/SQL 块分解为多个较小的块,以便更频繁地将控制权返回给客户端。这可能不切实际,具体取决于您的代码正在执行的操作。

其他替代方法是使用 UTL_FILE 写入文本文件(可以随时刷新该文件),或者使用自主事务过程将调试语句插入数据库表中并在每条语句后提交。

Not really. The way DBMS_OUTPUT works is this: Your PL/SQL block executes on the database server with no interaction with the client. So when you call PUT_LINE, it is just putting that text into a buffer in memory on the server. When your PL/SQL block completes, control is returned to the client (I'm assuming SQLPlus in this case); at that point the client gets the text out of the buffer by calling GET_LINE, and displays it.

So the only way you can make the output appear in the log file more frequently is to break up a large PL/SQL block into multiple smaller blocks, so control is returned to the client more often. This may not be practical depending on what your code is doing.

Other alternatives are to use UTL_FILE to write to a text file, which can be flushed whenever you like, or use an autonomous-transaction procedure to insert debug statements into a database table and commit after each one.

情绪少女 2024-08-13 04:24:18

如果可能的话,您应该用您自己的函数替换对 dbms_output.put_line 的调用。

以下是此函数的代码WRITE_LOG - 如果您希望能够在 2 个日志记录解决方案之间进行选择:

将日志写入表在自主事务

CREATE OR REPLACE PROCEDURE to_dbg_table(p_log varchar2)
  -- table mode: 
  -- requires
  -- CREATE TABLE dbg (u varchar2(200)   --- username
  --                 , d timestamp       --- date
  --                 , l varchar2(4000)  --- log 
  -- );
AS
   pragma autonomous_transaction;
BEGIN
  insert into dbg(u, d, l) values (user, sysdate, p_log);
  commit;
END to_dbg_table;
/

或直接写入托管数据库的数据库服务器

这使用Oracle 目录 TMP_DIR

CREATE OR REPLACE PROCEDURE to_dbg_file(p_fname varchar2, p_log varchar2)
  -- file mode: 
  -- requires
--- 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', p_fname, 'A');
  utl_file.put_line(l_file, p_log);
  utl_file.fflush(l_file);
  utl_file.fclose(l_file);
END to_dbg_file;
/

WRITE_LOG

然后WRITE_LOG< /code>程序可以在两种用途之间切换,或者被停用以避免性能损失(g_DEBUG:=FALSE)。

CREATE OR REPLACE PROCEDURE write_log(p_log varchar2) AS
  -- g_DEBUG can be set as a package variable defaulted to FALSE
  -- then change it when debugging is required
  g_DEBUG boolean := true;
  -- the log file name can be set with several methods...
  g_logfname varchar2(32767) := 'my_output.log';
  -- choose between 2 logging solutions:
  -- file mode: 
  g_TYPE varchar2(7):= 'file';
  -- table mode: 
  --g_TYPE varchar2(7):= 'table';
  -----------------------------------------------------------------
BEGIN
  if g_DEBUG then
    if g_TYPE='file' then
      to_dbg_file(g_logfname, p_log);
    elsif g_TYPE='table' then
      to_dbg_table(p_log);
    end if;
  end if;  
END write_log;
/

以下是如何测试上述内容:

1) 从 SQLPLUS 启动此(文件模式):

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) 在数据库服务器上,打开 shell 并

    tail -f -n500 /directory/where/oracle/can/write/on/DB_server/my_output.log

If it is possible to you, you should replace the calls to dbms_output.put_line by your own function.

Here is the code for this function WRITE_LOG -- if you want to have the ability to choose between 2 logging solutions:

write logs to a table in an autonomous transaction

CREATE OR REPLACE PROCEDURE to_dbg_table(p_log varchar2)
  -- table mode: 
  -- requires
  -- CREATE TABLE dbg (u varchar2(200)   --- username
  --                 , d timestamp       --- date
  --                 , l varchar2(4000)  --- log 
  -- );
AS
   pragma autonomous_transaction;
BEGIN
  insert into dbg(u, d, l) values (user, sysdate, p_log);
  commit;
END to_dbg_table;
/

or write directly to the DB server that hosts your database

This uses the Oracle directory TMP_DIR

CREATE OR REPLACE PROCEDURE to_dbg_file(p_fname varchar2, p_log varchar2)
  -- file mode: 
  -- requires
--- 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', p_fname, 'A');
  utl_file.put_line(l_file, p_log);
  utl_file.fflush(l_file);
  utl_file.fclose(l_file);
END to_dbg_file;
/

WRITE_LOG

Then the WRITE_LOG procedure which can switch between the 2 uses, or be deactivated to avoid performances loss (g_DEBUG:=FALSE).

CREATE OR REPLACE PROCEDURE write_log(p_log varchar2) AS
  -- g_DEBUG can be set as a package variable defaulted to FALSE
  -- then change it when debugging is required
  g_DEBUG boolean := true;
  -- the log file name can be set with several methods...
  g_logfname varchar2(32767) := 'my_output.log';
  -- choose between 2 logging solutions:
  -- file mode: 
  g_TYPE varchar2(7):= 'file';
  -- table mode: 
  --g_TYPE varchar2(7):= 'table';
  -----------------------------------------------------------------
BEGIN
  if g_DEBUG then
    if g_TYPE='file' then
      to_dbg_file(g_logfname, p_log);
    elsif g_TYPE='table' then
      to_dbg_table(p_log);
    end if;
  end if;  
END write_log;
/

And here is how to test the above:

1) Launch this (file mode) from your SQLPLUS:

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) on the database server, open a shell and

    tail -f -n500 /directory/where/oracle/can/write/on/DB_server/my_output.log
递刀给你 2024-08-13 04:24:18

两种替代方案:

  1. 您可以使用自治事务将日志记录详细信息插入日志记录表中。您可以在另一个 SQLPLUS/Toad/sql Developer 等会话中查询此日志记录表。您必须使用自治事务才能提交日志记录,而不会干扰主 sql 脚本中的事务处理。

  2. 另一种选择是使用返回日志信息的管道函数。请参阅此处的示例: http://berxblog.blogspot.com /2009/01/pipelined-function-vs-dbmsoutput.html 当您使用管道函数时,您不必使用另一个 SQLPLUS/Toad/sql 开发人员等...会话。

Two alternatives:

  1. You can insert your logging details in a logging table by using an autonomous transaction. You can query this logging table in another SQLPLUS/Toad/sql developer etc... session. You have to use an autonomous transaction to make it possible to commit your logging without interfering the transaction handling in your main sql script.

  2. Another alternative is to use a pipelined function that returns your logging information. See here for an example: http://berxblog.blogspot.com/2009/01/pipelined-function-vs-dbmsoutput.html When you use a pipelined function you don't have to use another SQLPLUS/Toad/sql developer etc... session.

只等公子 2024-08-13 04:24:18

当调用过程DBMS_OUTPUT.get_line时,读取DBMS_OUTPUT的缓冲区。如果您的客户端应用程序是 SQL*Plus,则意味着只有在过程完成后才会刷新它。

您可以应用 此 SODBMS_OUTPUT 缓冲区写入文件。

the buffer of DBMS_OUTPUT is read when the procedure DBMS_OUTPUT.get_line is called. If your client application is SQL*Plus, it means it will only get flushed once the procedure finishes.

You can apply the method described in this SO to write the DBMS_OUTPUT buffer to a file.

谁把谁当真 2024-08-13 04:24:18

使用dbms_application_info()设置会话元数据MODULE和/或ACTION。
通过 OEM 进行监控,例如:

Module: ArchiveData
Action: xxx of xxxx

Set session metadata MODULE and/or ACTION using dbms_application_info().
Monitor with OEM, for example:

Module: ArchiveData
Action: xxx of xxxx
半世蒼涼 2024-08-13 04:24:18

如果您可以从 PL/SQL 环境访问系统 shell,您可以调用 netcat:

 BEGIN RUN_SHELL('echo "'||p_msg||'" | nc '||p_host||' '||p_port||' -w 5'); END;

p_msg - 是一条日志消息
v_host 是运行 python 脚本的主机,它从端口 v_port 上的套接字读取数据。

当我为实时 shell 编写 aplogr 时,我使用了这种设计pl/sql 日志监控。

If you have access to system shell from PL/SQL environment you can call netcat:

 BEGIN RUN_SHELL('echo "'||p_msg||'" | nc '||p_host||' '||p_port||' -w 5'); END;

p_msg - is a log message
v_host is a host running python script that reads data from socket on port v_port.

I used this design when I wrote aplogr for real-time shell and pl/sql logs monitoring.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文