Scripts/Logger

From J Wiki
Jump to navigation Jump to search

This script provides routines for logging messages and (optionally) displaying them in a small window.

Brief Usage Example

logMsg_logger_ 'Show this text in a small gray window and'
logMsg_logger_ 'write these lines to the default log file.'
logMsg_logger_ 'Lines that are too long to be entirely displayed in the window will be logged'
logMsg_logger_ 'in their entirety.'

To close the window created by the above lines:

closeLog_logger_ ''

For further examples, see "exampleUses" near the beginning of the script.

Download script: File:Logger.ijs.

NB.* logger.ijs: display & log info.

NB.* logMsg: log message to file and show in window.  Title window by x .
NB.* exampleUses: examples of different ways to log messages.
NB.* initStatusWin: initialize status window.
NB.* setBuffLen: set buffer length=number of lines to buffer in var CUMOUT
NB.* initLogFl: set up log file; pick new name for today's file if none spec'd.
NB.* initWin: initialize message window.
NB.* initLog: initialize log file if necessary.
NB.* flushLog: write all text to log file and empty the cumulative output.
NB.* closeLog: write remaining text to log file, clean up vars, close window.
NB.* todayfile: Create unique file name based on today's date.
NB.* lastlines: return only last x lines from message y (text vec w/EOL
NB.* txtEOL2Lns: text delimited by end-of-lines chars -> vec of text vecs.
NB.* deepDisclose: disclose enclose arrays, to simplest level->simple text vec
NB.* pickFontSz: pick 10 or 12 point font size depending on ratio of
NB.* getTempDir: get name of temporary directory.
NB.* showdate: show given, or current if arg '', date & time in standard form.

NB. Use "logMsg_logger_" to show messages in a standard window and log
NB. them to a file.  Probably want to avoid "coinsert 'logger'" so this can
NB. be used with anything without danger of name-shadowing.

coclass 'logger'
coinsert 'base'
require 'winapi winlib'  NB.  dt'

NB. Global parameters below.  Other globals are CUMOUT (cumulative text
NB. output), BUFLEN (number of lines to accumulate in CUMOUT before writing
NB. to file), and FNM (name of log file).

DISPLAY=: 1              NB. Display messages on screen; 0: just log them.
LINELEN=: 77             NB. Maximum length of line to display in window.

logMsg=: 3 : 0
NB.* logMsg: log message to file and show in window.  Title window by x .
   '' logMsg y
:
   txt=. y                             NB. Text to output; may be matrix.
   x initWin '' [ initLog ''
   setBuffLen ''
   if. (0=L. txt) do.                   NB. Change any simple array
       if. (#$txt)e. 0 1 do.            NB.  from EOL-delimited vector to
           txt=. txtEOL2Lns txt         NB.  encl. vec.
       elseif. 2=#$txt do.              NB. Simple mat to
           txt=. dtsp&.><"1 txt         NB.  encl. text vec.
       end.
   else.                                NB. If txt is already enclosed.

   end.                                 NB. Assume encl. already OK.
   CUMOUT=: CUMOUT,,txt                 NB. Accumulate text.
   msg=. lastlines CUMOUT               NB. Show only last bit that fits
   if. DISPLAY do. wdstatus msg  end.   NB.  into window.
   if. BUFLEN<:#CUMOUT do.              NB. Write out accumulated text?
       flushLog ''
   end.
)

NB.* exampleUses: examples of different ways to log messages.
exampleUses=: 0 : 0
NB. Basic:
   logMsg_logger_ 'This line should print in a small gray window in the'
   logMsg_logger_ 'lower-right corner of the screen; also, this message'
   logMsg_logger_ 'gets logged to a file, the name of which is displayed'
   logMsg_logger_ 'on the title-bar of the message window.'
   wait 5                          NB. Give chance to read messages.

   logMsg_logger_ 'Lines that are too long to fit completely into the output window will be elided'
   logMsg_logger_ 'in the window (after BUFLEN chars) but not in the log file.'
   flushLog_logger_ ''             NB. Write CUMOUT to file.
   wait 5                          NB. Give chance to read messages.

   logMsg_logger_ 'Deeply nested arrays';<' are shown very ',:' simply by'
   logMsg_logger_ 'flattening them and turning everything into text';<i. 2 2 3
   wait 5                          NB. Give chance to see message.
   flushLog_logger_ ''             NB. Flush to file to clear out CUMOUT for
                                   NB.  next demo about turning off DISPLAY.
NB. Turn off display: write to logfile without displaying message.
   DISPLAY_logger_=: 0
   logMsg_logger_ 'This message will not appear in window but will get logged.'
   wait 5                          NB. Give chance to see lack of message.

NB. If changing the logfile, be sure to close and re-init the window (or the
NB. title on it will misleadingly refer to the old logfile.)  Alternately,
NB. globally assign the file name "FNM_logger_" before using any log functions.
   DISPLAY_logger_=: 1 [ closeLog_logger_ ''
   FNM_logger_=: 'C:\annoyingRootLevel.log'
   initWin_logger_ ''
   logMsg_logger_ 'This gets logged to a non-default file: ',FNM_logger_,'.'
   wait 5                          NB. Give chance to read message
   closeLog_logger_ ''
)

NB.* initStatusWin: initialize status window.
initStatusWin=: 4 : 0
   x wdstatus y
   wd 'pmove 10 10 250 100'
   wd 'setxywh s0 1 1 327 96'
   fontsz=. ":pickFontSz ''
   wd 'setfont s0 "Courier New" ',fontsz
NB. The specific values below were determined by trial-and-error.
   scrsz=. <;._1 ' ',wd 'qm'       NB. Find screen size so we can
   scrsz=. ;".&.>2{.scrsz          NB.  shrink window to about 8x60 chars
   loc=. ":scrsz-654 190           NB.  and put in LR corner; 25=200-175
   wd 'pmovex ',loc,' 654 170'     NB.  to avoid covering task bar at bottom.
)

NB. wd 'qformx'  NB. To find form's location and size.

NB.* setBuffLen: set buffer length=number of lines to buffer in var CUMOUT
NB. before writing to file FNM.
setBuffLen=: 3 : 0
   if. 0={.0$y do. BUFLEN=: y end.
   if. 0>4!:0 <'BUFLEN' do. BUFLEN=: 100 end.
)

initLogFl=: 3 : 0
NB.* initLogFl: set up log file; pick new name for today's file if none spec'd.
   if. 0>4!:0 <'FNM' do.
       FNM=: todayfile y
   end.
)

initWin=: 3 : 0
NB.* initWin: initialize message window.
   '' initWin y
:
   initLogFl ''
   if. DISPLAY do.
       if. -.('status',LF)-:wd 'qp' do.      NB. Need to initialize window?
           if. 0 e. $x do. titleStr=. 'Log@',FNM
           else. titleStr=. x end.
           titleStr initStatusWin ' '        NB. Avoid moving or resizing
       end.                                  NB.  programmatically after this.
   end.
)

initLog=: 3 : 0
NB.* initLog: initialize log file if necessary.
   initLogFl ''
   if. 0>4!:0 <'CUMOUT' do.             NB. Initialize cumulative log var if
       CUMOUT=: '[Logfile=',FNM,']'     NB.  necessary.
       CUMOUT=: ,<CUMOUT,' at ',(showdate ''),']'
       (;CUMOUT,&.><CRLF) 1!:3 <FNM
       CUMOUT=: ''
   end.                                 NB. Accumulate output, last lines
   if. 0=L. CUMOUT do. CUMOUT=: ,<CUMOUT end.
)

flushLog=: 3 : 0
NB.* flushLog: write all text to log file and empty the cumulative output.
   initWin '' [ initLog ''
   (deepDisclose CUMOUT) 1!:3 <FNM
   (CRLF,~'Log flushed: ',showdate '') 1!:3 <FNM
   [CUMOUT=: ''
)

closeLog=: 3 : 0
NB.* closeLog: write remaining text to log file, clean up vars, close window.
   if. 0=4!:0 <'CUMOUT' do. if. 0~:#CUMOUT do. flushLog '' end. end.
   4!:55 'CUMOUT';<'FNM'
   wd 'reset'
)

todayfile=: 3 : 0
NB.* todayfile: Create unique file name based on today's date.
   if. 0=#y do. y=. 'OUT' end.
   type=. 4{.(y-.' '),4$'_'       NB. 4-letter filename prefix
   tmpdir=. getTempDir ''
   letts=. '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_$'
   dtix=. ((($letts),10)#:{.TS),1 2{TS=. qts ''
   flnm=. tmpdir,type,(dtix{letts),'.LOG'
)

lastlines=: 3 : 0
NB.* lastlines: return only last x lines from message y (text vec w/EOL
NB. chars).
   8 lastlines y   NB. 8 lines should fit in 12-pt, 175 pixels high window.
:
   numlns=. -x
   if. 0=L. msg=. y do.
       msg=. txtEOL2Lns y
   end.
   msg=. ;txtEOL2Lns&.>deepDisclose&.>msg
   msg=. (numlns>.-#msg){.msg
   lens=. ;#&.>msg
   msg=. ((LINELEN<.lens){.&.>msg),&.>(LINELEN<lens)#&.><'...'
   msg=. (+./\0~:;#&.>msg-.&.>' ')#msg  NB. Remove leading blank lines.
   msg=. (-#CRLF)}.;msg,&.><CRLF        NB. Convert back to EOL-delimited chars.
)

NB.* txtEOL2Lns: text delimited by end-of-lines chars -> vec of text vecs.
txtEOL2Lns=: 3 : '<;._1 ((LF~:{.y)#LF),y=. (|.+./\|.y~:LF)#y=. y-.CR'

deepDisclose=: 3 : 0
NB.* deepDisclose: disclose enclose arrays, to simplest level->simple text vec
NB. with CRLF end-of-line char delimiters.
   if. 0=L. y do. ,((0~:#y)#CRLF),"1~":y
   else.
       ;deepDisclose&.>y
   end.
)

NB.* pickFontSz: pick 10 or 12 point font size depending on ratio of
NB. screen units to pixels.  The values below were picked by trial-and-error.
pickFontSz=: 3 : 0
   scrnum=. 0.1 <.0.5+({:".wd 'qchildxywhx s0')%{:".wd 'qchildxywh s0'
   closest=. 0 i.~ /:|2 2.5-scrnum NB. See which ratio is closer to.
   closest{12 10                   NB. Closer to 2 for 12-pt, 2.5 for 10-pt.
)

NB. Six general utilities that should reside in a separate script but are
NB. here for completeness.
NB.* getTempPath: MS Windows API returns designated temporary file dir.
getTempPath=: 'GetTempPath' win32api

NB.* getTempDir: get name of temporary directory.
getTempDir=: 3 : 0
   td=. >2{getTempPath 256;256$' '
   td=. td{.~td i. 0{a.
)
NB.* getTempDir: get name of temporary directory assuming in USERFOLDER_j_.
getTempDir=: 3 : 'PATHSEP_j_,~>1{(USERFOLDERS_j_,a:;PATHSEP_j_,''Temp''){~({."1 USERFOLDERS_j_)i.<''Temp'''

NB.* showdate: show given, or current if arg '', date & time in standard form.
NB. Bug: can show 60 in seconds position because rounded to nearest second and
NB. carrying because of rounding gets too complicated.
showdate=: 3 : 0
   y=. ,y                        NB. Must be vector.
   if. 0=#y do. y=. 6!:0 '' end. NB. Arg is timestamp: YYYY MM DD hh mm ss
   dt=. }:;(2 lead0s&.>":&.>3{.y),&.>'/'
   if. 3=#y do. tm=. '' else.
       tm=. }:;(2 lead0s&.>":&.>3}.y),&.>':' NB. Leading 0s
   end.
   dt,((0~:#tm)#' '),tm
NB.EG showdate 1959 5 24 8 9 0
NB.EG showdate ''                  NB. Current date and time
NB.EG >}.<;._1 ' ',showdate ''     NB. Time only
NB.EG showdate 1992 12 16          NB. Date only
)

qts=: 3 : 0
NB.* qts: like {quad}TS: return current timestamp.
   6!:0 ''
)

NB.* lead0s: precede integer y with up to x leading zeros.
lead0s=: 4 : '(-x>.$,":y){.(x$''0''),(]`":@.(0={.0$y))y'

wait=: wait_base_=: 3 : '6!:3 y'