Monkey C In Application Logging
During the development of a Garmin IQ application in Monkey C we struggled to reconcile the applications behaviour in the watch emulator with the behaviour on an actual watch. What we needed was an on-watch logging mechanism essentially the same as System.println(). My co-author quickly knocked up this simple solution which we used extensively. It occurs to me that its not an obvious solution and others might benefit from seeing the possibility and the illustration..
using Toybox.Communications;
using Toybox.Lang;
(:glance)
class WebLog {
// Create a debug log over the Internet to keep track of the watch's runtime
// execution.
//
function print(str as Lang.String) {
Communications.makeWebRequest(
ClientId.webLogUrl,
{
"log" => str
},
{
:method => Communications.HTTP_REQUEST_METHOD_GET,
:headers => {
"Content-Type" => Communications.REQUEST_CONTENT_TYPE_URL_ENCODED
},
:responseType => Communications.HTTP_RESPONSE_CONTENT_TYPE_TEXT_PLAIN
},
method(:on)
);
}
function println(str as Lang.String) {
print(str + "\n");
}
function on(responseCode as Lang.Number, data as Null or Lang.Dictionary or Lang.String) as Void {
if (Globals.debug && (responseCode != 200)) {
System.println("WebLog on() Response Code: " + responseCode);
System.println("WebLog on() Response Data: " + data);
}
}
}
Here ClientId.webLogUrl is sourced from a class of static constants. You can source that any way you like. To use this class follow this example:
wl = new WebLog();
wl.println("Debug Message");
This Monkey C code extract will create a single line text log at a URL where this PHP is implemented:
<?php
// E.g. https://domain.name/path/test.php
$myfile = fopen("log", "a");
$queries = array();
parse_str($_SERVER['QUERY_STRING'], $queries);
fwrite($myfile, $queries['log']);
print "Success";
?>
The log can then be views by the URL "https://domain.name/path/log". To reset the logs to zero length issue a single GET request to a URL with this PHP script.
<?php
// E.g. https://domain.name/path/test_clear.php
$myfile = fopen("log", "w");
fwrite($myfile, "");
print "Success";
?>
The above example code shows a basic logging mechanism to see what is going on inside an application, but it can fail when called too frequently. The Communications.makeWebRequest() method takes a responseCallback parameter. When the logs are not sent, this can be because the responseCodeis not the desired 200 value for HTTP 200 OK. The value that I frequently saw was -101 for BLE_QUEUE_FULL indicating that too many requests had been queued. The solution to this is to group up the logs to be sent. The revised code to do that is given on GitHub.
The usage now changes as the same object must be used for all logs rather than creating a local logger and disposing. This will mean passing the same WebLog instance to all objects wanting to use it, and can get convoluted. The revised code will automatically flush the logs after a specified number of calls. That leaves any residual logs buffered and waiting for be 'flushed out' before quitting, e.g. in the Application.AppBase.onStop() method.
wl = new WebLog();
// Delete the previous log contents from the web
wl.clear();
// Only buffer two calls worth at a time, increase this value with more aggressive logging
wl.setCallsBuffer(2);
wl.println("Debug Message 1");
wl.println("Debug Message 2");
// Logs sent
wl.println("Debug Message 3");
wl.flush();
// Logs sent
In this example, the first two debug messages are buffered and sent on the second call. The third call is buffered and not sent until the flush() method is called.Set the number of calls to buffer with the setCallsBuffer() method.
Conclusions
There are inevitable cautions to be expressed here about using such a logging facility to extract and send personal information. The source code is provided to the purposes of debugging, typically on one's own watch. Leaving this logging code in the published application would be cause for suspicion by users. Suspicion that can be mitigated by publishing the code under an Open Source licence agreement. But that is not itself proof the published application is from the indicated source code. Make sure all WebLog class usage is removed from the final implementation.