Files
2025-09-29 00:52:08 +02:00

893 lines
22 KiB
Plaintext
Executable File

filein "RsUniversalLogViewer.ms"
global memUsage = 0
global gLogMemUsage = false
fn lastMemStep =
(
r=sysinfo.getMAXMemoryInfo()
for i=2 to 7 do r[i] /= (1024*1024.)
local lastMemUsage = memUsage
memUsage = r[3] --(r[2]-r[3])
local msg = stringstream ""
format " - used physical memory:\t% MB increase by:\t% MB" memUsage (memusage - lastMemUsage) to:msg
--::gRsUlog.LogMessage msg logMem:false quiet:false
msg
)
--ReUniLogSize = [600, 600]
--buttonSize = [70, 30]
--checkboxOffset = [160, 27]
--local oldData = grsUlog.uniLogFileObj
struct sRsULog
(
b_goAhead = false,
quietMode = false,
universalLogObj = undefined,
logFactory = undefined,
validatedLogFiles = #(),
private
fn InitLogFactory =
(
if (logFactory == undefined) then
(
logFactory = dotNetClass "RSG.Base.Logging.LogFactory"
logFactory.Initialize "" true
)
),
public
fn ShowDialog modal:true newErrors:false=
(
if true == GetQuietMode() then
(
return true
)
-- Need to init this because when just choosing Universal Log from the RS Export menu the factory will not have been setup.
InitLogFactory()
local logDirectory = logFactory.ParentProcessLogDirectory
format "Showing log files from: %\n" logDirectory
return (::openULogViewer modal:modal allowContinue:((RsAutomatedExport) or (not newErrors)) logDirectory:logDirectory)
),
fn GetProcessLogDirectory =
(
InitLogFactory()
return logFactory.ParentProcessLogDirectory
),
fn Shutdown =
(
InitLogFactory()
print "Shutting down log factory and closing log files..."
shutdownMode = dotNetClass "RSG.Base.Logging.LogFactory+ShutdownMode"
logFactory.ApplicationShutdown shutdownMode.NoLogDisplay
),
fn ClearProcessLogDirectory =
(
InitLogFactory()
logFactory.ClearLogDirectory true
validatedLogFiles = #()
),
fn CloseUniversalLogFile =
(
if (universalLogObj != undefined) then
(
universalLogObj.CloseLog()
)
universalLogObj = undefined
),
fn ClearLogDirectory =
(
InitLogFactory()
CloseUniversalLogFile()
-- Reset the log directory. This will clear out the log directory but rebuild the main process log file again so we start clean.
logFactory.ResetLogDirectory true
validatedLogFiles = #()
),
fn CreateUniversalLogFile =
(
universalLogObj = RsULogObject()
universalLogObj.Init "unilog"
),
fn ValidateGlobalLog =
(
if (universalLogObj == undefined) then CreateUniversalLogFile()
),
fn Init context appendToFile:true forceLogFile:undefined skipValidation:false =
(
InitLogFactory()
ValidateGlobalLog()
universalLogObj.ClearErrorsAndWarnings()
if not appendToFile then
ClearProcessLogDirectory()
),
fn Flush =
(
InitLogFactory()
ValidateGlobalLog()
universalLogObj.Flush()
logFactory.FlushApplicationLog()
),
fn Filename =
(
InitLogFactory()
-- Returns the filename of the main application filename.
-- This is to keep backwards compatibility but in the future will be nice to use per-process log files for this scenario.
-- Biggest culprit is RexExport calls. It takes a filename and writes directly to the log file, doesn't use any of the logobjects, etc.
logFactory.ApplicationLogFilename
),
-- Returns list of message-types/objects stored by log:
fn getObjLists =
(
local msgList = uniLogFileObj.getmessages 0 -1
local msgTypes = #()
local outArray = #()
for msg in msgList do
(
local msgObjString = msg.objectContext
local msgObj
if (msgObjString != "") do
(
msgObj = getNodeByName msgObjString
)
if (msgObj != undefined) do
(
local msgType = msg.MsgType
local msgNum = findItem msgTypes msgType
if (msgNum == 0) do
(
append msgTypes msgType
append outArray (dataPair type:msgType objs:#())
msgNum = msgTypes.count
)
append outArray[msgNum].objs msgObj
)
)
fn sortByType v1 v2 = striCmp v1.type v2.type
qsort outArray sortByType
return outArray
),
-- Creates selection-sets for log's message-objects:
fn createSelSets =
(
local selSetPrefix = "R* Log: "
local objArrays = getObjLists()
local selSetNames = for item in objArrays collect
(
-- Capitalise the message-type:
item.type[1] = toUpper item.type[1]
selSetPrefix + item.type
)
-- Create/update selection sets:
for n = 1 to objArrays.count do
(
selectionsets[selSetNames[n]] = objArrays[n].objs
)
-- Remove unused "R* Log" selection sets:
for n = selectionSets.count to 1 by -1 where
(
local setName = getNamedSelSetName n
((findString setName selSetPrefix) == 1) and ((findItem selSetNames setName) == 0)
) do
(
deleteItem selectionSets n
)
OK
),
fn GetLogFileEntry logName =
(
for i = 1 to validatedLogFiles.count do
(
if validatedLogFiles[i].v1 == logName then return validatedLogFiles[i]
)
return undefined
),
fn CheckNewErrorsWarnings &newErrors &newWarnings justErrors:false =
(
InitLogFactory()
Flush()
local logDirectory = logFactory.ParentProcessLogDirectory
local logFiles = getFiles (logDirectory + "\\*.ulog")
universalLogFileClass = dotNetClass "RSG.Base.Logging.Universal.UniversalLogFile"
currentValidatedLogFiles = #()
newErrors = false
newWarnings = false
-- Check all the current logs in the log directory for any errors or warnings.
for logFile in logFiles do
(
previousLogEntry = GetLogFileEntry logFile
previousLogErrorsWarnings = undefined
if (previousLogEntry != undefined) then
previousLogErrorsWarnings = previousLogEntry.v2
numErrors = universalLogFileClass.GetNumErrors logFile
numWarnings = universalLogFileClass.GetNumWarnings logFile
errorWarningEntry = DataPair numErrors numWarnings
logFileEntry = DataPair logFile errorWarningEntry
if (previousLogErrorsWarnings != undefined) then
(
previousLogNumErrors = previousLogErrorsWarnings.v1
previousLogNumWarnings = previousLogErrorsWarnings.v2
--format "Log File % - Current Errors: % -> Previous Errors: %\n" logFile numErrors previousLogNumErrors
--format "Log File % - Current Warnings: % -> Previous Warnings: %\n" logFile numWarnings previousLogNumWarnings
-- Compare the new and old values to see if we have legitimate errors or warnings
if (numErrors > previousLogNumErrors) then newErrors = true
if (numWarnings > previousLogNumWarnings) then newWarnings = true
)
else
(
-- If we don't have a previous entry for the file but we have errors or warnings
if (numErrors > 0) then newErrors = true
if (numWarnings > 0) then newWarnings = true
)
append currentValidatedLogFiles logFileEntry
)
validatedLogFiles = currentValidatedLogFiles
if justErrors then
return newErrors
else
return newWarnings or newErrors
),
fn ValidateLogDirectory noProgressControls:false createSelSets:true justErrors:false =
(
hasErrors = false
hasWarnings = false
CheckNewErrorsWarnings &hasErrors &hasWarnings justErrors:justErrors
if (quietMode == true) then
(
return true
)
else
(
if noProgressControls or hasErrors or (hasWarnings and not justErrors) then
(
return (gRsULog.ShowDialog modal:(not RsAutomatedExport and not noProgressControls) newErrors:hasErrors)
)
else
(
return true
)
)
),
fn LogDirectoryHasErrors =
(
InitLogFactory()
local logDirectory = logFactory.ParentProcessLogDirectory
local logFiles = getFiles (logDirectory + "\\*.ulog")
universalLogFileClass = dotNetClass "RSG.Base.Logging.Universal.UniversalLogFile"
local hasErrors = false
for logFile in logFiles do
(
hasErrors = universalLogFileClass.ContainsErrors logFile
if hasErrors then break
)
return hasErrors
),
fn LogDirectoryHasWarnings =
(
InitLogFactory()
local logDirectory = logFactory.ParentProcessLogDirectory
local logFiles = getFiles (logDirectory + "\\*.ulog")
universalLogFileClass = dotNetClass "RSG.Base.Logging.Universal.UniversalLogFile"
local hasWarnings = false
for logFile in logFiles do
(
hasWarnings = universalLogFileClass.ContainsWarnings logFile
if hasWarnings then break
)
return hasWarnings
),
fn Validate noProgressControls:false createSelSets:true =
(
-- For now we need to validate all the files in the directory.
-- In the future it will be nice to validate just a given log file.
--ValidateGlobalLog()
--universalLogObj.Validate noProgressControls:noProgressControls createSelSets:createSelSets
return ValidateLogDirectory noProgressControls:noProgressControls createSelSets:createSelSets justErrors:false
),
fn ValidateJustErrors noProgressControls:false createSelSets:true =
(
-- For now we need to validate all the files in the directory.
-- In the future it will be nice to validate just a given log file.
--ValidateGlobalLog()
--universalLogObj.ValidateJustErrors noProgressControls:noProgressControls createSelSets:createSelSets
return ValidateLogDirectory noProgressControls:noProgressControls createSelSets:createSelSets justErrors:true
),
fn ValidateLogFileErrors filename =
(
universalLogFileClass = dotNetClass "RSG.Base.Logging.Universal.UniversalLogFile"
local hasErrors = false
if not RsFileExists filename then
print "COULD NOT FIND LOG FILE: " + filename
if universalLogFileClass != undefined then
hasErrors = universalLogFileClass.ContainsErrors filename
if hasErrors then
(
local result = (gRsULog.ShowDialog modal:(not RsAutomatedExport) newErrors:hasErrors)
return result
)
return true
),
fn ValidateProfileContext =
(
ValidateGlobalLog()
return universalLogObj.ValidateProfileContext()
),
fn ProfileContext =
(
ValidateGlobalLog()
return universalLogObj.ProfileContext()
),
fn HasErrors refresh:false =
(
-- Would eventually like to do this on a per-log basis
--ValidateGlobalLog()
--return universalLogObj.HasErrors()
return LogDirectoryHasErrors()
),
fn HasWarnings refresh:false =
(
-- Would eventually like to do this on a per-log basis
--ValidateGlobalLog()
--return universalLogObj.HasWarnings()
return LogDirectoryHasWarnings()
),
fn HasNewErrorsWarnings =
(
HasNewErrors
HasNewWarnings
this.CheckNewErrorsWarnings &HasNewErrors &HasNewWarnings
if( HasNewErrors or HasNewWarnings ) then
(
return true
)
else
(
return false
)
),
fn LogError msg context:"" files: quiet:false =
(
ValidateGlobalLog()
universalLogObj.LogError msg context:context files:files quiet:quiet
),
fn LogToolError msg context:"" files: quiet:true =
(
ValidateGlobalLog()
universalLogObj.LogToolError msg context:context files:files quiet:quiet
),
fn LogWarning msg context:"" files: quiet:true =
(
ValidateGlobalLog()
universalLogObj.LogWarning msg context:context files:files quiet:quiet
),
fn LogMessage msg context:"" files: quiet:true =
(
ValidateGlobalLog()
universalLogObj.LogMessage msg context:context files:files quiet:quiet
),
fn LogDebug msg context:"" files: quiet:true =
(
ValidateGlobalLog()
universalLogObj.LogDebug msg context:context files:files quiet:quiet
),
fn ProfileStart context quiet:true =
(
ValidateGlobalLog()
universalLogObj.ProfileStart context quiet:quiet
),
fn ProfileEnd context: quiet:true =
(
ValidateGlobalLog()
universalLogObj.ProfileEnd context:context quiet:quiet
),
fn ShowULogFile =
(
print "This is a message in ShowULogFile"
return (::openULogViewer modal:false allowContinue:true)
)
)
savePreviousValidationInfo = false
previousValidationInfo = #()
-- Close our global log file if it's already defined and save the previous validation info
if (gRsULog != undefined) then
(
gRsULog.CloseUniversalLogFile()
savePreviousValidationInfo = true
previousValidationInfo = gRsULog.validatedLogFiles
)
global gRsULog = sRsULog()
global gRsMiscULog = sRsULog()
-- This saves the state of the previous validation info so "NewWarnings" and "NewErrors" functionality doesn't get blown away
-- when this file gets "filein'd" in other scripts
if (savePreviousValidationInfo) then gRsULog.validatedLogFiles = previousValidationInfo
struct RsULogObject
(
logName = "",
currContext = "DirtyDucks",
currProfileContext = #(),
quietMode = false,
logFile = undefined,
logObj = undefined,
logFactory = undefined,
msgPrefix = (if RsUserIsSuperUser() then "SU: " else ""),
currentErrors = #(),
currentWarnings = #(),
private
fn InitLogFactory =
(
if (logFactory == undefined) then
(
logFactory = dotNetClass "RSG.Base.Logging.LogFactory"
logFactory.Initialize "" true
)
),
fn HasNewErrorsWarnings =
(
if undefined == logObj then
(
return false
)
if quietMode == true then
(
return false
)
local requestStop = (currentErrors.count > 0 or currentWarnings.count > 0)
if (RsDontStopForErrors and requestStop) then
::gRsUlog.logWarning "\"RsDontStopForErrors\" is set! Validation not stopping althout errors occured!"
return (not RsDontStopForErrors) and requestStop
),
fn HasNewErrors =
(
if undefined == logObj then
(
return false
)
if quietMode == true then
(
return false
)
local requestStop = (currentErrors.count > 0)
if (RsDontStopForErrors and requestStop) then
::gRsUlog.logWarning "\"RsDontStopForErrors\" is set! Validation not stopping althout errors occured!"
return (not RsDontStopForErrors) and requestStop
),
fn HasNewWarnings =
(
if undefined == logObj then
(
return false
)
if quietMode == true then
(
return false
)
local requestStop = (currentWarnings.count > 0)
if (RsDontStopForErrors and requestStop) then
::gRsUlog.logWarning "\"RsDontStopForErrors\" is set! Validation not stopping althout errors occured!"
return (not RsDontStopForErrors) and requestStop
),
public
fn Init name =
(
InitLogFactory()
logName = name
if (logObj != undefined or logFile != undefined) then
(
format "Log file % already initialized..." logName
)
logObj = logFactory.CreateUniversalLog name
logFile = logFactory.CreateUniversalLogFile logObj
),
fn CloseLog =
(
if (logObj != undefined and logFile != undefined) then
(
logFactory.CloseUniversalLogFile logFile
logFactory.CloseUniversalLog logObj
logObj = undefined
logFile = undefined
)
),
fn Flush =
(
if (logFile != undefined) then
(
logFile.Flush()
)
),
fn Filename =
(
if (logFile != undefined) then
(
return logFile.Filename
)
return undefined
),
fn ClearErrorsAndWarnings =
(
currentErrors = #()
currentWarnings = #()
),
fn Validate noProgressControls:false createSelSets:true =
(
-- Need to figure out a different way to handle selection sets because they could come from multiple log files and also the GetMessages function is now deprecated.
--this.createSelSets()
Flush()
if (quietMode == true) then
(
return true
)
else
(
local newErrors = HasNewErrors()
local newWarnings = HasNewWarnings()
if noProgressControls or newErrors or newWarnings then
(
local result = (gRsULog.ShowDialog modal:(not RsAutomatedExport and not noProgressControls) newErrors:newErrors)
-- Clear the errors because we don't want to keep spamming that there are new errors
if newErrors then currentErrors = #()
if newWarnings then currentWarnings = #()
return result
)
else
(
return true
)
)
),
fn ValidateJustErrors noProgressControls:false createSelSets:true =
(
-- Create object selection-sets from log:
--this.createSelSets()
Flush()
if quietMode then
(
return true
)
else
(
local newErrors = HasNewErrors()
if noProgressControls or newErrors then
(
local result = (gRsULog.ShowDialog modal:(not RsAutomatedExport and not noProgressControls) newErrors:newErrors)
-- Clear the errors because we don't want to keep spamming that there are new errors
if newErrors then currentErrors = #()
return result
)
else
(
return true
)
)
),
fn ValidateProfileContext =
(
return (currProfileContext.count > 0)
),
fn ProfileContext =
(
return currProfileContext
),
fn MakeSafeMessage m prefix:true =
(
m = m as string
if (matchpattern m pattern:"*{*") or (matchpattern m pattern:"*}*") then
(
m = substituteString m "{" "{{"
m = substituteString m "}" "}}"
)
if prefix then
m = msgPrefix + m
return m
),
fn getContextString context =
(
local retVal = context
case of
(
((isKindOf context array) or (isKindOf context ObjectSet)):
(
-- Generate comma-separated string-list for arrays:
local stringList = for item in context collect (getContextString item)
local formatString = stringStream ""
for n = 1 to stringList.count do
(
format "%" stringList[n] to:formatString
if (n != stringList.count) do
(
format "," to:formatString
)
)
retVal = formatString as string
)
(isProperty context #name):(retVal = context.name)
default:(context as string)
)
return (MakeSafeMessage retVal prefix:false)
),
fn LogError msg context:"" files: quiet:false =
(
local ctx = (getContextString context) as String
if gLogMemUsage then
append msg (lastMemStep())
local m = MakeSafeMessage msg
if unsupplied!=files then
append m (" attached files:"+files as string)
if not quiet do (format "ERROR: %\n" (m as string))
append currentErrors m
logObj.ErrorCtx ctx m #()
),
fn LogToolError msg context:"" files: quiet:false =
(
local ctx = (getContextString context) as String
if gLogMemUsage then
append msg (lastMemStep())
local m = MakeSafeMessage msg
if unsupplied!=files then
append m (" attached files:"+files as string)
if not quiet do (format "TOOL ERROR: %\n" (m as string))
append currentErrors m
logObj.ToolErrorCtx ctx m #()
),
fn LogWarning msg context:"" files: quiet:true =
(
local ctx = (getContextString context) as String
if gLogMemUsage then
append msg (lastMemStep())
local m = MakeSafeMessage msg
if unsupplied!=files then
append m (" attached files:"+files as string)
if not quiet do (format "WARINING: %\n" (m as string))
append currentWarnings m
logObj.WarningCtx ctx m #()
),
fn LogMessage msg context:"" files: quiet:true =
(
local ctx = (getContextString context) as String
if gLogMemUsage then
append msg (lastMemStep())
local m = MakeSafeMessage msg
if unsupplied!=files then
append m (" attached files:"+files as string)
if not quiet do (format "MESSAGE: %\n" (m as string))
logObj.MessageCtx ctx m #()
),
fn LogDebug msg context:"" files: quiet:true =
(
local ctx = (getContextString context) as String
if gLogMemUsage then
append msg (lastMemStep())
local m = MakeSafeMessage msg
if unsupplied!=files then
append m (" attached files:"+files as string)
if not quiet do (format "DEBUG: %\n" (m as string))
logObj.DebugCtx ctx m #()
),
fn ProfileStart context quiet:true =
(
local safeContext = context
-- resuming context functionality
if lastProfileContext == safeContext then
(
if currProfileContext.count>0 and currProfileContext[currProfileContext.count]==safeContext then
ProfileEnd()
local formerNumberIndex = findString safeContext "_#"
local newCtxt = (safeContext+"_#0")
if 0!=formerNumberIndex then
(
local formerNumber = substring safeContext formerNumberIndex (safeContext.count) as number
newCtxt = (safeContext+"_#"+(formerNumber+1) as string)
)
safeContext = newCtxt
)
append currProfileContext safeContext
if not quiet do (format "PROFILE START: %\n" (safeContext as string))
logObj.Profile safeContext #()
lastProfileContext = safeContext
Flush()
),
fn ProfileEnd context: quiet:true =
(
local currProfileContextindex = currProfileContext.count
if currProfileContextindex<1 then
(
LogWarning ("More Profile end calls than start calls!")
)
else
(
if unsupplied!=context and currProfileContext[currProfileContextindex]!=context then
LogError ("Context \""+currProfileContext[currProfileContextindex]+"\" got not closed before ending \""+context+"\"")
if not quiet do (format "PROFILE END %s\n" currProfileContext[currProfileContextindex])
deleteItem currProfileContext currProfileContextindex
logObj.ProfileEnd()
Flush()
)
),
fn HasErrors refresh:false =
(
if logObj == undefined then
(
return false
)
else
(
return logObj.HasErrors
)
),
fn HasWarnings refresh:false =
(
if logObj == undefined then
(
return false
)
else
(
return logObj.HasWarnings
)
)
)
rexSetULogFile (gRsUlog.filename())
fn ShowRsUlogDialog =
(
return gRsULog.Validate noProgressControls:true
)
fn ShowRsUlogDialogFromMenu =
(
return gRsULog.ShowULogFile
)