Setting up Logging

IceLink 3 has an internal logging API that its components use to provide you with information about what's happening. You will want to hook into this API at some point, because you will want to know when an error occurs. This guide describes how the logging API works and how you can integrate your application with it.

Understanding the Logging Model

The logging API outputs messages with one of five possible severity levels. The severity level indicates the importance of the message. The levels are, from most severe to least severe:

  • FATAL: The message indicates a fatal application error; the application will need to shut down and no recovery actions are possible.
  • ERROR: The message indicates a normal application error (ie: timeouts, bad user input); this is a normal error state and usually it is possible to proceed.
  • WARN: The message indicates that something abnormal occurred but that the application was not negatively affected; this is used for unexpected values, deprecations or abnormal application state.
  • INFO: This is a trace message; it describes what is going on in the SDK.
  • DEBUG: This is a diagnostic message; it is similar to the INFO message but is used for outputting detailed parameters or for "spammy" messages.

You can output a log message by invoking one of several methods on the static FM.IceLink.Log class. The methods are named after the severity level of the logs they output. They are Debug, Info, Warn, Error, Fatal. The following snippet shows how to output a DEBUG and an ERROR log message.

FM.IceLink.Log.Debug("This is a DEBUG message.");
FM.IceLink.Log.Fatal("This is a FATAL message.");
fm.icelink.Log.debug("This is a DEBUG message."); 
fm.icelink.Log.fatal("This is a FATAL message.");
[FMIceLinkLog debugWithMessage:@"This is a DEBUG message."]; 
[FMIceLinkLog fatalWithMessage:@"This is a FATAL message."];
FMIceLinkLog.debug(message: "This is a DEBUG message.")
FMIceLinkLog.fatal(message: "This is a FATAL message.")
fm.icelink.Log.debug("This is a DEBUG message."); 
fm.icelink.Log.fatal("This is a FATAL message.");

You can also set the level of logs you want displayed. By default, only INFO and more severe messages are displayed. In production, you may want to display no message, and in development, you may want to display all diagnostic messages. This is accomplished by setting the LogLevel property of the FM.IceLink.Log class. Note that there is one additional level here, None. You can set this if you want to suppress all log messages.


// for development
FM.IceLink.Log.LogLevel = FM.IceLink.LogLevel.Debug;


// for production
FM.IceLink.Log.LogLevel = FM.IceLink.LogLevel.None;
// or
FM.IceLink.Log.LogLevel = FM.IceLink.LogLevel.Error;
// for development
fm.icelink.Log.setLogLevel(fm.icelink.LogLevel.Debug);

// for production
fm.icelink.Log.setLogLevel(fm.icelink.LogLevel.None);
// or
fm.icelink.Log.setLogLevel(fm.icelink.LogLevel.Error);
// for development
[FMIceLinkLog setLoglevel: FMIceLinkLogLevelDebug];

// for production
[FMIceLinkLog setLogLevel: FMIceLinkLogLevelNone];
// or
[FMIceLinkLog setLogLevel: FMIceLinkLogLevelError];
// for development
FMIceLinkLog.setLogLevel(FMIceLinkLogLevelDebug)

// for production
FMIceLinkLog.setLogLevel(FMIceLinkLogLevelNone)
// or
FMIceLinkLog.setLogLevel(FMIceLinkLogLevelError)
// for development
fm.icelink.Log.setLogLevel(fm.icelink.LogLevel.Debug);

// for production
fm.icelink.Log.setLogLevel(fm.icelink.LogLevel.None);
// or
fm.icelink.Log.setLogLevel(fm.icelink.LogLevel.Error);

Registering a Provider

By default, nothing happens when a log message is generated. You must first specify what should happen when a log message is output. This is done by registering an FM.IceLink.LogProvider instance with the static FM.IceLink.Log class. Each provider has a specific method of outputting a log message. A common provider is one that that outputs messages to the log console but you can also create providers that output messages to files or to a cloud logging provider.

Each provider has an associated severity level. These severity levels are used in combination with the global severity level to determine whether or not a log message should be output. This allows you to specify that some log providers with an expensive logging mechanism (ie: a cloud log provider) should only log error messages, while log providers with a fast logging mechanism can output everything, including debug messages.

Adding a provider is accomplished by invoking the RegisterProvider method of the static FM.IceLink.Log class. This example uses the default IceLink log providers, all of which output to the console. Note how the provider's log level is specified when the provider is instantiated.


FM.IceLink.Log.Level = FM.IceLink.LogLevel.Debug;
FM.IceLink.Log.RegisterProvider(new FM.IceLink.ConsoleLogProvider(FM.IceLink.LogLevel.Debug));
// for android
fm.icelink.Log.setLevel(fm.icelink.LogLevel.Debug);
fm.icelink.Log.registerProvider(new fm.icelink.android.LogProvider(fm.icelink.LogLevel.Debug));
[FMIceLinkLog setLevel:FMIceLinkLogLevelDebug];
[FMIceLinkLog registerProvider:[FMIceLinkNSLogProvider nsLogProviderWithLogLevel:FMIceLinkLogLevelDebug]];
FMIceLinkLog.setLevel(FMIceLinkLogLevelDebug)
FMIceLinkLog.registerProvider(provider: FMIceLinkNSLogProvider(logLevel: FMIceLinkLogLevelDebug))
fm.icelink.Log.setLevel(fm.icelink.LogLevel.Debug);
fm.icelink.Log.registerProvider(fm.icelink.ConsoleLogProvider(fm.icelink.LogLevel.Debug));


Removing a Provider

Generally, you will not want to remove a provider. Log providers are usually set up on application start up and there are not many use cases for changing them during execution. However, IceLink does provide this functionality. You will need to retain a reference to the specific provider you wish to remove. Pass this reference into the RemoveProvider method of the static FM.IceLink.Log instance.


FM.IceLink.Log.RemoveProvider(consoleLogProvider);
fm.icelink.Log.removeProvider(consoleLogProvider);
[FMIceLinkLog removeProviderWithProvider:nsLogProvider];
FMIceLinkLog.removeProvider(provider:nsLogProvider)
fm.icelink.Log.removeProvider(consoleLogProvider);



Implementing Your Own Log Provider

IceLink provides some basic FM.IceLink.LogProvider implementations but you may want to create some application-specific implementations. To do so, extend the LogProvider class and implement the DoLog method. The method has five parameters:

  • timestamp: The date and time when the message was logged.
  • level: The severity level of the message.
  • message: The actual text of the log message.
  • tag: A value based on which component generated the log message.
  • ex: If the log message is associated with an exception, then this will contain the exception instance; otherwise it will be null.

If you do not need to perform any custom formatting on the message, it's recommended to use the inherited GenerateLogLine method. This returns a string in the same format as the other IceLink log providers. You can then output the message in whatever way you want. The following code shows a re-implementation of the console log provider.

Note: The LogProvider base class defaults to the INFO log level. If you want to support a different logging level in your custom log provider be sure to pass the logging level through in the constructor.


public class ConsoleLogProvider : FM.IceLink.LogProvider
{
    protected override void DoLog(DateTime timestamp, FM.IceLink.LogLevel level, string tag, string message, Exception ex)
    {
        Console.WriteLine(GenerateLogLine(timestamp, level, tag, message, ex);
    }
}
public class ConsoleLogProvider extends fm.icelink.LogProvider {
    @Override
    protected void doLog(java.util.Date timestamp, fm.icelink.LogLevel level, String tag, String message, Exception ex) {
        System.out.println(generateLogLine(timestamp, level, tag, message, ex)); 
    }
}
@interface ConsoleLogProvider : FMIceLinkLogProvider
@end

@implementation ConsoleLogProvider
- (void)doLogWithTimestamp:(NSDate*)timestamp level:(FMIceLinkLogLevel)level tag:(NSString*)tag message:(NSString*)message ex:(NSException*)ex {
    NSLog("%@\n", [self generateLogLineWithTimestamp:timestamp level:level tag:tag message:message ex:ex]);
}
@end
public class ConsoleLogProvider : FMIceLinkLogProvider {
    override func doLog(timestamp:NSDate, level:FMIceLinkLogLevel, tag:NSString, message:NSString, ex:NSException) {
        print(generateLogLine(timestamp: timestamp, level: level, tag: tag, message: message, ex: ex))
    }
}
function ConsoleLogProvider() {
    fm.icelink.LogProvider.call(this);
}

ConsoleLogProvider.prototype.doLog = function(timestamp, level, tag, message, ex) {
    console.log(this.generateLogLine(timestamp, level, tag, message, ex));
}



Wrapping Up

You now have all the knowledge that you need to incorporate the IceLink logging API into your application. If you prefer to use another logging framework, that's perfectly fine. In this case, you can write a custom log provider to send IceLink log messages to the logging framework and let the framework handle the output of messages.