jonasespelita
jonasespelita

Reputation: 1650

Have PL/SQL Outputs in Real Time

Is it possible to have Outputs from PL/SQL in real time? I have a pretty huge package that runs for more than an hour and I'd like to see where the package is at a particular time.

Anyways, I currently do this with a log table, which gets filled up with hundreds of log descriptions per run, I'm just curious if this is possible.

Thanks!

Upvotes: 6

Views: 4853

Answers (6)

olekb
olekb

Reputation: 648

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

BEGIN RUN_SHELL('echo "'||v_msg||'" | nc '||v_host||' '||v_port||' -w 5'); END;

/

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 shell and pl/sql logs monitoring.

Upvotes: 0

Jeffrey Kemp
Jeffrey Kemp

Reputation: 60262

If your long-running job is processing a large number of fairly evenly sized tasks, you may find session longops a good way of monitoring the job progress, as well as allowing you to estimate how long the job will take to finish.

DBMS_APPLICATION_INFO.set_session_longops

Upvotes: 1

Rob van Laarhoven
Rob van Laarhoven

Reputation: 8905

I don't know if this is exactly what you want but I use dbms_application_info.set_module to see where my package is.

dbms_application_info.set_module(module_name => 'Conversion job',
                                 action_name => 'updating table_x'); 

A query on v$session will show you which part of the procedure is running.

Upvotes: 8

Rene
Rene

Reputation: 10541

Use DBMS_PIPE to write a message to a named pipe. In another session you can read the messages from the pipe. Very simple, works like a charm !

procedure sendmessage(p_pipename varchar2
                        ,p_message  varchar2) is
      s number(15);
   begin
      begin
         sys.dbms_pipe.pack_message(p_message);
      exception
         when others then
            sys.dbms_pipe.reset_buffer;
      end;

      s := sys.dbms_pipe.send_message(p_pipename, 0);

      if s = 1
      then
         sys.dbms_pipe.purge(p_pipename);
      end if;
   end;




function receivemessage(p_pipename varchar2
                          ,p_timeout  integer) return varchar2 is
      n   number(15);
      chr varchar2(200);
   begin
      n := sys.dbms_pipe.receive_message(p_pipename, p_timeout);

      if n = 1
      then
         return null;
      end if;

      sys.dbms_pipe.unpack_message(chr);
      return(chr);
   end;

Upvotes: 1

PaulJ
PaulJ

Reputation: 1510

This is the kind of thing I use (output can be seen in v$session and v$session_longops)...

DECLARE
   lv_module_name   VARCHAR2(48);
   lv_action_name   VARCHAR2(32);

   gc_MODULE   CONSTANT   VARCHAR2(48) := 'MY_PROC';

   -- For LONGOPS
   lv_rindex BINARY_INTEGER;
   lv_slno   BINARY_INTEGER;

   lc_OP_NAME   CONSTANT   VARCHAR2(64)   :=   '['||gc_MODULE||']';
   lv_sofar   NUMBER;

   -- This is a guess as to the amount of work we will do
   lv_totalwork   NUMBER;
   lc_TARGET_DESC   CONSTANT VARCHAR2(64) := 'Tables';
   lc_UNITS   CONSTANT VARCHAR2(64) := 'Rows';

   CURSOR tab_cur
   IS
      SELECT owner, table_name
        FROM all_tables;

BEGIN
   <<initialisation>>
   BEGIN
      -- To preserve the calling stack, read the current module and action
      DBMS_APPLICATION_INFO.READ_MODULE( module_name => lv_module_name
                                       , action_name => lv_action_name );

      -- Set our current module and action
      DBMS_APPLICATION_INFO.SET_MODULE( module_name => gc_MODULE
                                      , action_name => NULL );
   END initialisation;

   <<main>>
   BEGIN
      DBMS_APPLICATION_INFO.SET_ACTION( action_name => 'Part 01' );
      NULL;

      DBMS_APPLICATION_INFO.SET_ACTION( action_name => 'Part 02' );
      FOR tab_rec IN tab_cur
      LOOP
         DBMS_APPLICATION_INFO.SET_CLIENT_INFO( client_info => 'Rows = ['||TO_CHAR( tab_cur%ROWCOUNT, '999,999,999' )||']' );
         NULL;
      END LOOP;

      DBMS_APPLICATION_INFO.SET_ACTION( action_name => 'Part 03' );

      --Initialising longops
      lv_rindex := DBMS_APPLICATION_INFO.SET_SESSION_LONGOPS_NOHINT;
      lv_sofar := 0;
      lv_totalwork := 5000; -- This is a guess, but could be actual if the query is quick

      FOR tab_rec IN tab_cur
      LOOP
         DBMS_APPLICATION_INFO.SET_CLIENT_INFO( client_info => 'Rows = ['||TO_CHAR( tab_cur%ROWCOUNT, '999,999,999' )||']' );

         lv_sofar := lv_sofar + 1;

         -- Update our totalwork guess
         IF lv_sofar > lv_totalwork
         THEN
            lv_totalwork := lv_totalwork + 500;
         END IF;

         DBMS_APPLICATION_INFO.SET_SESSION_LONGOPS( rindex      => lv_rindex
                                                  , slno        => lv_slno
                                                  , op_name     => lc_OP_NAME
                                                  , sofar       => lv_sofar
                                                  , totalwork   => lv_totalwork
                                                  , target_desc => lc_TARGET_DESC
                                                  , units       => lc_UNITS
                                                  );
      END LOOP;

      -- Clean up longops
      DBMS_APPLICATION_INFO.SET_SESSION_LONGOPS( rindex      => lv_rindex
                                               , slno        => lv_slno
                                               , op_name     => lc_OP_NAME
                                               , sofar       => lv_sofar
                                               , totalwork   => lv_sofar
                                               , target_desc => lc_TARGET_DESC
                                               , units       => lc_UNITS
                                               );
   END main;

   <<finalisation>>
   BEGIN
      -- Reset the module and action to the values that may have called us
      DBMS_APPLICATION_INFO.SET_MODULE( module_name => lv_module_name
                                      , action_name => lv_action_name );

      -- Clear the client info, preventing any inter process confusion for anyone looking at it
      DBMS_APPLICATION_INFO.SET_CLIENT_INFO( client_info => NULL );
   END finalisation;
END;
/

Upvotes: 9

Vincent Malgrat
Vincent Malgrat

Reputation: 67722

you could use autonomous transactions (as suggested in this SO for example).

This would allow you to write and commit in a log table without commiting the main transaction. You would then be able to follow what happens in your main script while it is running (incidentally, it will also allow you to time/tune your batch).

Upvotes: 4

Related Questions