J'ai un script SQL qui est appelé à partir d'un script Shell et prend beaucoup de temps à exécuter. Il contient actuellement dbms_output.put_line
déclarations en divers points. La sortie de ces instructions d'impression apparaît dans les fichiers journaux, mais uniquement une fois le script terminé.
Existe-t-il un moyen de garantir que la sortie apparaît dans le fichier journal pendant l'exécution du script?
Pas vraiment. Le fonctionnement de DBMS_OUTPUT est le suivant: votre bloc PL/SQL s'exécute sur le serveur de base de données sans interaction avec le client. Ainsi, lorsque vous appelez PUT_LINE, il s'agit simplement de mettre ce texte dans un tampon en mémoire sur le serveur. Lorsque votre bloc PL/SQL se termine, le contrôle est retourné au client (je suppose SQLPlus dans ce cas); à ce stade, le client extrait le texte du tampon en appelant GET_LINE et l'affiche.
Par conséquent, la seule façon de faire apparaître la sortie dans le fichier journal plus fréquemment est de diviser un grand bloc PL/SQL en plusieurs blocs plus petits, de sorte que le contrôle est retourné plus souvent au client. Cela peut ne pas être pratique selon ce que fait votre code.
D'autres alternatives consistent à utiliser UTL_FILE pour écrire dans un fichier texte, qui peut être vidé à tout moment, ou à utiliser une procédure de transaction autonome pour insérer des instructions de débogage dans une table de base de données et valider après chacune.
Si cela vous est possible, vous devez remplacer les appels à dbms_output.put_line par votre propre fonction.
Voici le code de cette fonction WRITE_LOG
- si vous voulez avoir la possibilité de choisir entre 2 solutions de journalisation:
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;
/
Cela utilise le répertoire OracleTMP_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
Ensuite, le WRITE_LOG
procédure qui peut basculer entre les 2 utilisations, ou être désactivée pour éviter la perte de performances (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) Lancez ceci ( mode fichier) depuis votre 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) sur le serveur de base de données, ouvrez un Shell et
tail -f -n500 /directory/where/Oracle/can/write/on/DB_server/my_output.log
Deux alternatives:
Vous pouvez insérer vos détails de journalisation dans une table de journalisation à l'aide d'une transaction autonome. Vous pouvez interroger cette table de journalisation dans une autre session SQLPLUS/Toad/sql developer etc .... Vous devez utiliser une transaction autonome pour permettre de valider votre journalisation sans interférer avec la gestion des transactions dans votre script sql principal.
Une autre alternative consiste à utiliser une fonction en pipeline qui renvoie vos informations de journalisation. Voir ici pour un exemple: http://berxblog.blogspot.com/2009/01/pipelined-function-vs-dbmsoutput.html Lorsque vous utilisez une fonction en pipeline, vous n'avez pas besoin d'en utiliser une autre Session SQLPLUS/Toad/sql developer etc ...
le tampon de DBMS_OUTPUT
est lu lorsque la procédure DBMS_OUTPUT.get_line
est appelé. Si votre application client est SQL * Plus, cela signifie qu'elle ne sera vidée qu'une fois la procédure terminée.
Vous pouvez appliquer la méthode décrite dans ce SO pour écrire le DBMS_OUTPUT
tampon dans un fichier.
Si vous avez accès au shell système depuis l'environnement PL/SQL, vous pouvez appeler netcat:
BEGIN RUN_Shell('echo "'||p_msg||'" | nc '||p_Host||' '||p_port||' -w 5'); END;
p_msg
- est un message de journal v_Host
est un hôte exécutant python qui lit les données du socket sur le port v_port
.
J'ai utilisé cette conception lorsque j'ai écrit aplogr pour la surveillance en temps réel des journaux Shell et pl/sql.