Log objects carry more information with them than just simply message text. They also carry message type, positioning information, ID, a file target, and caller data. Some of these are able to be set through Legato, but some can only be read. To break down the basics, I have put together a fairly simple script that shows off how to use most of the properties available to the log object.
handle oldLog;
handle newLog;
int rc, count;
int w, a, i, n, e;
string props[];
handle myLog; /* Declare our log */
myLog = LogCreate("This is a log"); /* Create our log */
LogSetMessageType(LOG_WARNING); /* Set to Warning */
AddMessage(myLog, "Warning Message"); /* Output to log */
LogIndent(myLog, 5); /* Indent the log */
LogSetMessageType(LOG_INFO); /* Set to Info */
AddMessage(myLog, "Info Message"); /* Output to log */
LogIndent(myLog, 40); /* Indent the log */
LogSetMessageType(LOG_ERROR); /* Set to Error */
AddMessage(myLog, "Error Message"); /* Output to log */
LogOutdent(myLog, 10); /* Outdent the log */
LogClearProperties(); /* Clear Properties */
AddMessage(myLog, "Message 4"); /* Output to log */
LogAddErrorSummary(myLog); /* Add Error Summary */
LogDisplay(myLog); /* Display the Log */
LogExport(myLog, GetScriptFolder() + "logoutput.xml"); /* Export the log */
oldLog = LogCreate("This is a log");
rc = LogImport(oldLog, GetScriptFolder() + "logoutput.xml");
newLog = LogCreate("New Log");
count = LogGetMessageCount(oldLog, 0);
w = 0;
a = 0;
i = 0;
n = 0;
e = 0;
int x;
for(x = 0; x < count; x++) {
props = LogGetMessageProperties(oldLog, x);
switch(TextToInteger(props["Type"])) {
case LOG_INFO:
i++;
break;
case LOG_ADVISORY:
a++;
break;
case LOG_WARNING:
w++;
break;
case LOG_ERROR:
case LOG_FATAL:
e++;
break;
case LOG_NONE:
n++;
break;
default:
MessageBox("%d", props["type"]);
}
AddMessage(newLog, "x is currently: %d", x);
AddMessage(newLog, "Message: %s", LogGetMessage(oldLog, x));
AddMessage(newLog, "Flags: %s", props["Flags"]);
AddMessage(newLog, "Type: %s", props["Type"]);
AddMessage(newLog, "SX: %s", props["SX"]);
AddMessage(newLog, "SY: %s", props["SY"]);
AddMessage(newLog, "EX: %s", props["EX"]);
AddMessage(newLog, "EY: %s", props["EY"]);
AddMessage(newLog, "Page: %s", props["Page"]);
AddMessage(newLog, "ID: %s", props["ID"]);
AddMessage(newLog, "FieldName: %s", props["FieldName"]);
AddMessage(newLog, "OffendingValue: %s", props["OffendingValue"]);
AddMessage(newLog, "Target: %s", props["Target"]);
AddMessage(newLog, "CallerOffset: %s", props["CallerOffset"]);
AddMessage(newLog, "CallerString1: %s", props["CallerString1"]);
AddMessage(newLog, "CallerString2: %s", props["CallerString2"]);
AddMessage(newLog, "CallerString3: %s", props["CallerString3"]);
AddMessage(newLog, "CallerString4: %s", props["CallerString4"]);
AddMessage(newLog, "CallerData1: %s", props["CallerData1"]);
AddMessage(newLog, "CallerData2: %s", props["CallerData2"]);
AddMessage(newLog, "CallerData3: %s", props["CallerData3"]);
AddMessage(newLog, "CallerData4: %s", props["CallerData4"]);
}
string script;
script = GetScriptFilename();
LogSetTarget(script);
LogSetPosition(0,89);
AddMessage("Number of normal entries: %d", n);
LogSetPosition(0,91);
AddMessage("Number of info entries: %d", i);
LogSetPosition(0,93);
AddMessage("Number of advisory entries: %d", a);
LogSetPosition(0,95);
AddMessage("Number of warning entries: %d", w);
LogSetPosition(0,97);
AddMessage("Number of error entries: %d", e);
LogDisplay(newLog);
We can split this up into two sections: creation of a log object and reading properties from a log object. Throughout both we will be using functions to set properties as we go.
handle oldLog;
handle newLog;
int rc, count;
int w, a, i, n, e;
string props[];
handle myLog; /* Declare our log */
myLog = LogCreate("This is a log"); /* Create our log */
LogSetMessageType(LOG_WARNING); /* Set to Warning */
AddMessage(myLog, "Warning Message"); /* Output to log */
LogIndent(myLog, 5); /* Indent the log */
LogSetMessageType(LOG_INFO); /* Set to Info */
AddMessage(myLog, "Info Message"); /* Output to log */
LogIndent(myLog, 40); /* Indent the log */
LogSetMessageType(LOG_ERROR); /* Set to Error */
AddMessage(myLog, "Error Message"); /* Output to log */
LogOutdent(myLog, 10); /* Outdent the log */
LogClearProperties(); /* Clear Properties */
AddMessage(myLog, "Message 4"); /* Output to log */
LogAddErrorSummary(myLog); /* Add Error Summary */
LogDisplay(myLog); /* Display the Log */
LogExport(myLog, GetScriptFolder() + "logoutput.xml"); /* Export the log */
Here we have the exact code from the first blog post. We create a log object using the LogCreate() function. We then add messages to that object with different message types. Putting properties into logs is different from most other properties in Legato; we can’t add properties as parameters when adding the message itself. Instead we have to set the global property, which will then apply to any log function called afterwards until the property is changed or cleared.
Let’s take a look then at this in practice as seen above:
LogSetMessageType(LOG_ERROR); /* Set to Error */
AddMessage(myLog, "Error Message"); /* Output to log */
[...]
LogClearProperties(); /* Clear Properties */
We set the message type using the LogSetMessageType() function to an error message. Every function that adds messages to a log, any log, will now add messages as an error. That’s why the next AddMessage() will be an error message. A line or two later we use the LogClearProperties() function to clear away any set properties. This resets the global log properties to be default.
In our script above we have created our log and have added messages to it of varying levels of urgency. We then export the log to our script folder (this means that you have to save the script before executing it).
oldLog = LogCreate("This is a log");
rc = LogImport(oldLog, GetScriptFolder() + "logoutput.xml");
newLog = LogCreate("New Log");
count = LogGetMessageCount(oldLog, 0);
w = 0;
a = 0;
i = 0;
n = 0;
e = 0;
Moving on we create another log object that will be where we import the log that we just exported. We also create a whole bunch of small variables as we are going to track the number of message types in the log that we’re importing. This second half of the script can be used on any log object that you want, even though we are using on one we created in this script. It can process any log created by Legato or GoFiler.
int x;
for(x = 0; x < count; x++) {
props = LogGetMessageProperties(oldLog, x);
switch(TextToInteger(props["Type"])) {
case LOG_INFO:
i++;
break;
case LOG_ADVISORY:
a++;
break;
case LOG_WARNING:
w++;
break;
case LOG_ERROR:
case LOG_FATAL:
e++;
break;
case LOG_NONE:
n++;
break;
default:
MessageBox("%d", props["type"]);
}
Let’s look at this for loop in two halves. The first half we use the GetMessageProperties() to get all the properties from a log entry. We then use a switch statement to check what kind of entry it is (info, advisory, warning, error, or none) and increment the counters that we defined before the for loop.
AddMessage(newLog, "x is currently: %d", x);
AddMessage(newLog, "Message: %s", LogGetMessage(oldLog, x));
AddMessage(newLog, "Flags: %s", props["Flags"]);
AddMessage(newLog, "Type: %s", props["Type"]);
AddMessage(newLog, "SX: %s", props["SX"]);
AddMessage(newLog, "SY: %s", props["SY"]);
AddMessage(newLog, "EX: %s", props["EX"]);
AddMessage(newLog, "EY: %s", props["EY"]);
AddMessage(newLog, "Page: %s", props["Page"]);
AddMessage(newLog, "ID: %s", props["ID"]);
AddMessage(newLog, "FieldName: %s", props["FieldName"]);
AddMessage(newLog, "OffendingValue: %s", props["OffendingValue"]);
AddMessage(newLog, "Target: %s", props["Target"]);
AddMessage(newLog, "CallerOffset: %s", props["CallerOffset"]);
AddMessage(newLog, "CallerString1: %s", props["CallerString1"]);
AddMessage(newLog, "CallerString2: %s", props["CallerString2"]);
AddMessage(newLog, "CallerString3: %s", props["CallerString3"]);
AddMessage(newLog, "CallerString4: %s", props["CallerString4"]);
AddMessage(newLog, "CallerData1: %s", props["CallerData1"]);
AddMessage(newLog, "CallerData2: %s", props["CallerData2"]);
AddMessage(newLog, "CallerData3: %s", props["CallerData3"]);
AddMessage(newLog, "CallerData4: %s", props["CallerData4"]);
}
We then add to the log all of the properties that are retrieved earlier when we used GetMessageProperties(). Detailed information about what all of these properties are can be found in the Legato documentation.
string script;
script = GetScriptFilename();
LogSetTarget(script);
LogSetPosition(0,89);
AddMessage("Number of normal entries: %d", n);
LogSetPosition(0,91);
AddMessage("Number of info entries: %d", i);
LogSetPosition(0,93);
AddMessage("Number of advisory entries: %d", a);
LogSetPosition(0,95);
AddMessage("Number of warning entries: %d", w);
LogSetPosition(0,97);
AddMessage("Number of error entries: %d", e);
LogDisplay(newLog);
Finally we get the name of our saved script and we use LogSetTarget() to point to our file. We then use LogSetPosition() to set the position of the log to the next line of the file, which for us will end up being where each AddMessage() is called. We then print out the number of entries of each type.
When this script is run it will open three different logs: the default Legato log, “New Log”, and “This is a log”. “This is a log” will contain the information that we create in the first section of this script. “New Log” will show all of the message properties for each message in “This is a log”. Finally, the default log will show the number of each type of entry in “This is a log”.
This example is just a shell, but it shows how powerful the information stored in logs can be. I am only using properties for demonstration purposes, but there can be a number of uses for logs. For example, you can store the beginning and ending positions for different files, and then retrieve those to use as a framework for replacing portions of files. You can also create specialized validation processes for files specific to your organization (and then hook those validations into the ribbon to replace GoFiler’s default validation or to work alongside the default validation). The possibilities are endless thanks to the power provided within the logs.
Logs are usually seen as a mundane piece of technology, but I hope that today I have been able to show how logs can help developers by carrying information between scripts or for long term storage, adding another tool to our extensive toolbox to help make developers lives easier.
Joshua Kwiatkowski is a developer at Novaworks, primarily working on Novaworks’ cloud-based solution, GoFiler Online. He is a graduate of the Rochester Institute of Technology with a Bachelor of Science degree in Game Design and Development. He has been with the company since 2013. |
Additional Resources
Novaworks’ Legato Resources
Legato Script Developers LinkedIn Group
Primer: An Introduction to Legato