Nome File: U_BIF463.DLL
I messaggi applicativi sono sempre stati la "croce e delizia" di DModa: da una parte perchè in certe situazioni si rendono utili per capire che giri fa il programma
in altre vengono utilizzati per dei "debug" a volo.
La cosa da capire è che bisogna trovare un bilanciamento tra l'effettiva utilità dei messaggi e l'appesantimento delle procedure che li devono scrivere in quanto
anche se è vero che da un lato sono necessari, è anche vero che non è concepibile avere giga e giga di informazioni scritte in 5 posti differenti.
Questa BIF è stata concepita con lo scopo di avere un'unico strumento ed un unico repository per i messaggi.
E' stata scritta per girare nella stessa maniera in tutti gli ambienti a nostra disposizione e con tutti i database che possiamo utilizzare:
WINDOWS
AS400
SYBASE
SQL SERVER
DB2
RDML - RDML/X
RDML - RDML/X
Centralizzando il tutto è possibile gestire l'attivazione e la disattivazione dei log in modo razionale così come gestirne la riorganizzazione.
Inoltre l'uso delle stored-procedure a livello database la rendono molto più veloce delle tecnice fino a d'ora implementate in LANSA.
Questo test è stato eseguito scrivendo 5000 righe di log:
Per le informazione tecniche e dettagliate è possibile fare riferimento a questo documento: DMODA APPLICATION LOG MANAGEMENT
Descrizione della BIF:
L'"Application Reference" deve essere un raggruppamento logico della o delle procedure (se vengono coinvolti più programmi) per unità elaborativa (quando possibile),
non deve essere utilizzato nella maniera più assoluta il nome del programma in quanto ci pensa la BIF ad indentificarlo: piuttosto deve essere una sorta nome della procedura da tracciare.
Per facilitare l'utilizzo è stato creato il campo a repository £R_LOGAREF: quando occorre implementare la scrittura dei log in un programma, basta assegnare il valore al campo
all'inizio del programma e poi passarlo alla BIF ogni volta.
Per quanto riguarda il testo del messaggio è possibile specificare un testo libero o eventualmente inserire all'interno del testo i tag &1 fino a &9 che verranno poi sostituiti con i valori
passati nei rispettivi parametri. Esempio:
£R_LOGAREF := "WIKI"
UD_LOGMESSAGE (£R_LOGAREF "Errore durante l'elaborazione dell'articolo &1" "I" *BLANK *BLANK £PAKAR) TO_GET(£R_LOGSTS)
[Errore durante l'elaborazione dell'articolo KU898723DA200]
UD_LOGMESSAGE (£R_LOGAREF "Riferimento &2 per l'articolo &1 è errato" "I" *BLANK *BLANK £PAKAR £WRKARREF) TO_GET(£R_LOGSTS)
[Riferimento 028P9 per l'articolo KU898723DA200 è errato]
Da notare nel secondo esmpio che i tag non sono posizionati in ordine: la bif sostituisce i parametri in modo posizionale (arg6 con &1, arg7 con &2 fino a arg14 con &9) ma i tag all'interno del testo
possono trovarsi in qualsiasi ordine.
se la chiamata fosse stata così (per seguire la posizione dei tag):
UD_LOGMESSAGE (£R_LOGAREF "Riferimento &2 per l'articolo &1 è errato" "I" *BLANK *BLANK £WRKARREF £PAKAR) TO_GET(£R_LOGSTS)
Il messaggio sarebbe stato così:
[Riferimento KU898723DA200 per l'articolo 028P9 è errato]
I SINGOLI TAG POSSONO ESSERE SPECIFICATI UNA SOLA VOLTA (OVVERO "&1" NON PUO' TROVARSI 2 VOLTE ALL'INTERNO DEL TESTO)
il testo "#CHECK" serve solo per fare un controllo se il log è attualmente attivo per APPLICATION_REFERENCE/PROGRAMMA.
il testo "#FROMMSGF:<MSGF>/<MSGID>/<LANG>" indica alla BIF di prendere il testo dal file messaggi <MSGF> con id <MSGID>, la lingua è opzionale: se passata viene decodificato
il messaggio per quella lingua, altrimenti viene utilizzata la lingua con cui sta girando il programma.
Dato che i messaggi possono contenere le variabili di sostituzione nella forma &n, la BIF prima decodifica il messaggio e poi applica le sostituzioni come sopra.
Quindi riscrivendo l'esempio di prima considerando che il messaggio fosse codificato:
Codice Messaggio ED§M01/ED06767 => Errore durante l'elaborazione dell'articolo &1
UD_LOGMESSAGE (£R_LOGAREF "#FROMMSGF:ED§M01/ED06767" "I" *BLANK *BLANK £PAKAR) TO_GET(£R_LOGSTS)
[Errore durante l'elaborazione dell'articolo KU898723DA200]
Se il messaggio non viene trovato, la BIF scrive il messaggio con testo "Messaggio non trovato", nel file del log i riferimenti del file messaggi e dell'id messaggio sono sempre memorizzati.
il testo "#FROMMLTVAR:<MLTVARID>/<LANG>" indica alla BIF di prendere il testo dalla variabile multilingua con id <MLTVARID>, la lingua è opzionale: se passata viene decodificata
la variabile multilingua per quella lingua, altrimenti viene utilizzata la lingua con cui sta girando il programma.
L'id della variabile deve essere passato nella forma estesa "*MTXTnnnnnnnnnnnnn"
UD_LOGMESSAGE (£R_LOGAREF "#FROMMLTVAR:*MTXTEDORDINE") TO_GET(£R_LOGSTS)
[Ordine]
Il tipo messaggio è importante perchè deve dare un senso al messaggio, quindi occorre specificare bene quando il log è solo Informativo piùttosto che Warning o Errore.
Il tipo D = Debug potrebbe essere usato al solo scopo di creare un livello più dettagliato da attivare solo in casi particolari.
Il Codice di Errore ha codifica libera.
L'"ID Record" è il numero dell'identity appena inserita. E' stato implementato per implementazioni future.
ATTIVAZIONE/DISATTIVAZIONE DEI LOG
Per controllare la reale scrittura di un log, la BIF utilizza un altro file: DMAPPLFLT (file LANSA).
Prima di tutto vede se esiste un record per APPLICATION_REFERENCE o NOME PROGRAMMA
poi vede se è stato impostato un filtro di utenti e se l'utente è tra questi
infine vede se il flag di attivazione è ON: questo si basa sul tempo che può essere "SEMPRE" - "RANGE DI DATETIME" - "MAI"
Se il log risulta attivo, l'ultimo filtro è sul tipo messaggio.
Se tutto questo risulta ok il log viene inserito, altrimenti no.
LETTURA DEI LOG
E' possibile leggere i log scritti tramite una query eseguita da UD_EXECSQL.
Importante considerare che la struttura è composta da 2 files: DMAPPLSYSLOG e DMAPPLSYSLOGEXT correlati tramite LOGSEQ <-> LOGSEQEXT
La query necessaria alla selezione dei record deve essere così formattata:
SELECT
LOGAPPLREF,
LOGTYPE,
LOGERRCODE,
(LOGTEXT + COALESCE(LOGTEXTEXT, '')) AS LOGTEXT,
(LOGEXTRA + COALESCE(LOGEXTRAEXT, '')) AS LOGEXTRA,
LOGMSGF,
LOGMSGID,
LOGPGMSRC,
LOGJOBNBR,
LOGUSER,
LOGTIME
FROM LOGDTALIB.DMAPPLSYSLOG
LEFT OUTER JOIN LOGDTALIB.DMAPPLSYSLOGEXT ON LOGSEQEXT = LOGSEQ
WHERE ...
Questo è l'esempio di come il gestore lavori in coda legge i log di un determinato lavoro
SELECT LOGAPPLREF, LOGTYPE, LOGERRCODE,
(LOGTEXT + COALESCE(LOGTEXTEXT, '')) LOGTEXT, (LOGEXTRA + COALESCE(LOGEXTRAEXT, '')) AS LOGEXTRA,
LOGMSGF, LOGMSGID, LOGPGMSRC, LOGJOBNBR, LOGUSER, LOGTIME
FROM LOGDTALIB.DMAPPLSYSLOG
LEFT OUTER JOIN LOGDTALIB.DMAPPLSYSLOGEXT ON LOGSEQ = LOGSEQEXT
WHERE (LOGJOBNBR = 'JOB001245') AND (LOGTIME BETWEEN DATETIME('2016-01-22 16:54:25') AND DATETIME('2016-01-22 16:58:32.999999'))
ORDER BY LOGAPPLREF, LOGJOBNBR, LOGTIME, LOGSEQ
* Nota: su AS/400 non va specifiato l'owner "LOGDTALIB"
* Nota: su AS/400 utilizzare la funzione TIMESTAMP al posto di DATETIME
* Nota: per SQLSERVER utilizzare la funzione CONVERT(datetime2, 'stringa datetime') al posto di DATETIME
Una nota sul filtro della data/ora: il campo database è di tipo DATETIME nativo e il modo migliore per gestirlo è utilizzando la data e l'ora in formato ISO "yyyy-mm-dd HH:MM:SS".
Passando questo valore come stringa alla funzione SQL DATETIME() il valore viene convertito e confrontato correttamente.
Date che il timestamp è espresso in millisecondi, in caso di selezione per range è sempre bene aggiungere ".999999" al limite superiore per essere sicuri includere tutto.
CANCELLAZIONE DEI LOG
Quando è necessario pulire i log basta eliminare solo i record da LOGDTALIB.DMAPPLSYSLOG in quanto è attivo un trigger after delete che cancellerà i relativi record in LOGDTALIB.DMAPPLSYSLOGEXT