Using ILogger

It's strange how many BREW specialists you can talk to who have never used ILogger. It's not hard to see why, though: the API reference only sometimes tells you the details of how to use it, and while the ILogger overview notionally tells you what it does, you have to read it very carefully to figure out how to use it.

And when you find out what it does, it doesn't seem like the most appropriate thing to use.

Because you don't want to be bored with the details (otherwise you'd be reading the API reference, hoping to find out what you're missing), I'll give you a code example:

#include "AEELogger.h"
void log_something(AEEApplet* pMe)
{
    int bucket = 0;

    ILogger *pILogger;

    // Error checking has been omitted in this example for clarity
    ISHELL_CreateInstance(pMe->m_pIShell, 
                                    AEECLSID_LOGGER_WIN, 
                                    (void**)&pILogger);
    ILOGGER_SetParam(pILogger, 
                              AEE_LOG_PARAM_FILTER_ONE, 
                              bucket, 
                              (void*)TRUE);

    // Do the actual logging
    LOG_TEXT(pILogger, bucket, "Hello world");

    // Cleanup
    ILOGGER_Release( pILogger );
    pILogger = NULL;
}

As mentioned in the comments, error handling has been omitted: if ISHELL_CreateInstance() fails, your app will crash (ISHELL_CreateInstance(), ILOGGER_SetParam(), and LOG_TEXT() return error codes; ILOGGER_Release() returns the ramining reference count). In English, what it does is pretty simple:

  1. Create an ILogger instance, which logs to the BREW output window.

  2. Enable log bucket 0.

  3. Log a test message.

  4. Release the logger, and set the pointer to NULL (generally good practice).

The log output appears a bit cryptic at first: the log message is prefixed with the log bucket, the record type, your app's class ID, and the ILogger's instance ID.

There are three classes implementing ILogger mentioned in the API references:

  • AEECLSID_LOGGER_FILE logs to a file

  • AEECLSID_LOGGER_SERIAL logs to the serial port, and

  • AEECLSID_LOGGER_WIN logs to the BREW Simulator output window.

Already, you've probably realised something: It doesn't log to the serial port or BREW output window as appropriate, unlike DBGPRINTF(). Of course, you can probably do something with an #ifdef. [1]

You set parameters with a call to ILOGGER_SetParam() (parameters are defined in AEELogParamType). While param is usually a uint32, pParam is sometimes a uint32pretending to be a void*, and the documentation isn't that clear on this.

There are 256 log buckets, which can be filtered by the app (call ILOGGER_SetParam and set AEE_LOG_PARAM_FILTER_ONE with the bucket in param and (void*)TRUE or (void*)FALSE in pParam), and by the log parser, so it's good for tagging different sections of code so you can switch logging for, say, your network code on at runtime. There are also 256 instance IDs, and the default is 0; you set it by setting AEE_LOG_PARAM_INSTANCE_ID. If you do it at runtime with some sort of thread ID, you can distinguish between threads.

There are 65536 record types, defined in AEELogItemType:

  • 0 is text,

  • 1 is a "binary message",

  • 2 is a "binary block" (a BLOB), and

  • anything 0x100 or over is in a "user-defined format".

A binary message is typically produced from ILOGGER_PutMsg(). The format is defined in AEELogBinMsgType, but to summarise, they're fixed at 112 bytes (supposedly this makes logging faster), and store four uint32s and two strings totalling 80 bytes (including null terminators); the first uint32 is meant to be __LINE__ and the first string is meant to be __FILE__ (and standard log parsers probably assume this). The current implementation (3.1.5 simulator) also outputs some contents of your stack if you don't fill 80 bytes with the two strings, this is a security risk (it ought to zero them anyway to make log entries cleaner).

A binary block is just a byte array, and you'll have to find a way of distinguishing between them (like user-defined formats, or a prefix string, or XML).

If you're logging to a file, you'll have to call ILOGGER_SetParam() with AEE_LOG_PARAM_FILE_NEW and a file name (drive letters don't work; for testing, just use something like "LogFile"). You need to #include "AEEFile.h" for the file modes.

You'll notice that logs are actually in a binary format. It's defined in the API (AEELogRecord, AEELogRcdHdrType), but I haven't found something that will parse the log files, let alone let you filter for specific buckets and class IDs, let you have pluggable pretty printing for your user-defined log format, or save a BLOB to a file. ILogger could prove to be a useful general logging tool (all apps spam the serial port, and the receiver filters), but for most purposes, it's a bit overkill.

[1] The BREW Tools Suite's BREW Logger is meant to be able to connect to the BREW Simulator, but I haven't managed this. Anyone?

Teanlorg Chan
in BREW

Airsource design and develop apps for ourselves and for our clients. We help people like you to turn concepts into reality, taking ideas from initial design, development and ongoing maintenance and support.

Contact us today to find out how we can help you build and maintain your app.