Table of Contents Previous Next
Logo
The Ice Run Time in Detail : 32.19 The Ice::Logger Interface
Copyright © 2003-2010 ZeroC, Inc.

32.19 The Ice::Logger Interface

Depending on the setting of various properties (see Chapter 30), the Ice run time produces trace, warning, or error messages. These messages are written via the Ice::Logger interface:
module Ice {
    local interface Logger {
        void print(string message);
        void trace(string category, string message);
        void warning(string message);
        void error(string message);
        Logger cloneWithPrefix(string prefix);
    };
};
The cloneWithPrefix operation returns a new logger that logs to the same desti­nation but with a different prefix. (The prefix is used to, for example, provide the name of the process writing the log messages.)

32.19.1 The Default Logger

A default logger is instantiated when you create a communicator. The default logger logs to the standard error output. The trace operation accepts a category parameter in addition to the error message; this allows you to separate trace output from different subsystems by sending the output through a filter.
You can obtain the logger that is attached to a communicator:
module Ice {
    local interface Communicator {
        Logger getLogger();
    };
};

32.19.2 Custom Loggers

You have several options if you wish to install a logger other than the default one:
• Select one of the other built-in loggers (see Section 32.19.3), which allow you to log to a file, to the syslog on Unix, and to the Windows event log
• Supply your own logger implementation in an InitializationData parameter (see Section 32.3) when you create a communicator
• Load a logger implementation dynamically via the Ice plug-in facility (see Section 32.19.4)
Changing the Logger object that is attached to a communicator allows you to inte­grate Ice messages into your own message handling system. For example, for a complex application, you might have an existing logging framework. To integrate Ice messages into that framework, you can create your own Logger implementa­tion that logs messages to the existing framework.
When you destroy a communicator, its logger is not destroyed. This means that you can safely use a logger even beyond the lifetime of its communicator.

32.19.3 Built‑In Loggers

A file-based logger, enabled via the Ice.LogFile property, is available for all supported languages and platforms. Ice also provides Unix- and Windows-specific logger implementations. Finally, for .NET, the default Ice logger uses a TraceListener and so can be customized at run time via configuration.
Refer to Appendix D for more information on the properties mentioned in this section.

Unix

You can activate a logger that logs via the Unix syslog implementation by setting the Ice.UseSyslog property. This logger is available for C++, Java, and C# applications.

Windows

On Windows, subclasses of Ice::Service use the Windows application event log by default (see Section 8.3.2). The event log implementation is available for C++ applications.

.NET

The default logger in Ice for .NET writes its messages using the System.Diagnostics.Trace facility. By default, the Ice run time registers a ConsoleTraceListener that writes to stderr. You can disable the logging of messages via this trace listener by setting the property Ice.ConsoleListener to zero.
You can change the trace listener for your application via the application’s configuration file. For example:
 
<configuration>
  <system.diagnostics>
    <trace autoflush="true" indentsize="4">
      <listeners>
        <add name="Console"
             type="System.Diagnostics.EventLogTraceListener"
             initializeData="true"/>
      </listeners>
    </trace>
  </system.diagnostics>
</configuration>
This configures a trace listener that logs to the Windows event log.

32.19.4 Logger Plug‑Ins

Installing a custom logger using the Ice plug‑in facility has several advantages. Because the logger plug‑in is specified by a configuration property and loaded dynamically by the Ice run time, an application requires no code changes in order to utilize a custom logger implementation. Furthermore, a logger plug‑in takes precedence over the per-process logger (see Section 32.19.5) and the logger supplied in the InitializationData argument during communicator initial­ization, meaning you can use a logger plug‑in to override the logger that an appli­cation installs by default.

Installing a C++ Logger Plug‑In

To install a logger plug‑in in C++, you must first define a subclass of Ice::Logger:
class MyLoggerI : public Ice::Logger {
public:

    virtual void print(const std::string& message);
    virtual void trace(const std::string& category,
                       const std::string& message);
    virtual void warning(const std::string& message);
    virtual void error(const std::string& message);
    virtual LoggerPtr void cloneWithPrefix(const string& prefix);

    // ...
};
Next, supply a factory function that installs your custom logger by returning an instance of Ice::LoggerPlugin:
extern "C"
{

ICE_DECLSPEC_EXPORT Ice::Plugin*
createLogger(const Ice::CommunicatorPtr& communicator,
             const std::string& name,
             const Ice::StringSeq& args)
{
    Ice::LoggerPtr logger = new MyLoggerI;
    return new Ice::LoggerPlugin(communicator, logger);
}

}
The factory function can have any name you wish; we used createLogger in this example. See Section 32.25.1 for more information on plug‑in factory func­tions.
The definition of LoggerPlugin is shown below:
namespace Ice {
class LoggerPlugin {
public:
    LoggerPlugin(const CommunicatorPtr&, const LoggerPtr&);

    virtual void initialize();
    virtual void destroy();
};
}
The constructor installs your logger into the given communicator. The initialize and destroy methods are empty, but you can subclass LoggerPlugin and override these methods if necessary.
Finally, define a configuration property that loads your plug‑in into an applica­tion:
Ice.Plugin.MyLogger=mylogger:createLogger
The plug‑in’s name in this example is MyLogger; again, you can use any name you wish. The value of the property represents the plug‑in’s entry point, in which mylogger is the abbreviated form of its shared library or DLL, and createL­ogger is the name of the factory function.
If the configuration file containing this property is shared by programs in multiple implementation languages, you can use an alternate syntax that is loaded only by the Ice for C++ run time:
Ice.Plugin.MyLogger.cpp=mylogger:createLogger
Refer to Appendix D for more information on the Ice.Plugin properties.

Installing a Java Logger Plug‑In

To install a logger plug‑in in Java, you must first define a subclass of Ice.Logger:
public class MyLoggerI implements Ice.Logger {

    public void print(String message) { ... }
    public void trace(String category, String message) { ... }
    public void warning(String message) { ... }
    public void error(String message) { ... }
    public Logger cloneWithPrefix(String prefix) { ... }

    // ...
}
Next, define a factory class that installs your custom logger by returning an instance of Ice.LoggerPlugin:
public class MyLoggerPluginFactoryI implements Ice.PluginFactory {
    public Ice.Plugin create(Ice.Communicator communicator,
                             String name, String[] args)
    {
        Ice.Logger logger = new MyLoggerI();
        return new Ice.LoggerPlugin(communicator, logger);
    }
}
The factory class can have any name you wish; in this example, we used MyLoggerPluginFactoryI. See Section 32.25.1 for more information on plug‑in factories.
The definition of LoggerPlugin is shown below:
package Ice;

public class LoggerPlugin implements Plugin {
    public LoggerPlugin(Communicator communicator, Logger logger)
    {
        // ...
    }

    public void initialize() { }

    public void destroy() { }
}
The constructor installs your logger into the given communicator. The initialize and destroy methods are empty, but you can subclass LoggerPlugin and override these methods if necessary.
Finally, define a configuration property that loads your plug‑in into an applica­tion:
Ice.Plugin.MyLogger=MyLoggerPluginFactoryI
The plug‑in’s name in this example is MyLogger; again, you can use any name you wish. The value of the property is the name of the factory class.
If the configuration file containing this property is shared by programs in multiple implementation languages, you can use an alternate syntax that is loaded only by the Ice for Java run time:
Ice.Plugin.MyLogger.java=MyLoggerPluginFactoryI
Refer to Appendix D for more information on the Ice.Plugin properties.

Installing a .NET Logger Plug‑In

To install a logger plug‑in in .NET, you must first define a subclass of Ice.Logger:
// C#
public class MyLoggerI : Ice.Logger {

    public void print(string message) { ... }
    public void trace(string category, string message) { ... }
    public void warning(string message) { ... }
    public void error(string message) { ... }
    public Logger cloneWithPrefix(string prefix) { ... }

    // ...
}
Next, define a factory class that installs your custom logger by returning an instance of Ice.LoggerPlugin:
// C#
public class MyLoggerPluginFactoryI : Ice.PluginFactory {
    public Ice.Plugin create(Ice.Communicator communicator,
                             string name, string[] args)
    {
        Ice.Logger logger = new MyLoggerI();
        return new Ice.LoggerPlugin(communicator, logger);
    }
}
The factory class can have any name you wish; in this example, we used MyLoggerPluginFactoryI. See Section 32.25.1 for more information on plug‑in factories. Typically the logger implementation and the factory are compiled into a single assembly.
The definition of LoggerPlugin is shown below:
// C#
namespace Ice {
public partial class LoggerPlugin : Plugin {
    public LoggerPlugin(Communicator communicator, Logger logger)
    {
        // ...
    }

    public void initialize() { }

    public void destroy() { }
}
}
The constructor installs your logger into the given communicator. The initialize and destroy methods are empty, but you can subclass LoggerPlugin and override these methods if necessary.
Finally, define a configuration property that loads your plug‑in into an applica­tion:
Ice.Plugin.MyLogger=mylogger.dll:MyLoggerPluginFactoryI
The plug‑in’s name in this example is MyLogger; again, you can use any name you wish. The value of the property is the entry point for the factory, consisting of an assembly name followed by the name of the factory class.
If the configuration file containing this property is shared by programs in multiple implementation languages, you can use an alternate syntax that is loaded only by the Ice for .NET run time:
Ice.Plugin.MyLogger.clr=mylogger.dll:MyLoggerPluginFactoryI
Refer to Appendix D for more information on the Ice.Plugin properties.

32.19.5 The Per-Process Logger

Ice allows you to install a per-process custom logger. This logger is used by all communicators that do not have their own specific logger established at the time a communicator is created.
You can set a per-process logger in C++ by calling Ice::setProcessLogger, and you can retrieve the per-process logger by calling Ice::getProcessLogger:
LoggerPtr getProcessLogger();
void setProcessLogger(const LoggerPtr&);
If you call getProcessLogger without having called setProcessLogger first, the Ice run time installs a default per-process logger. Note that if you call setProcessLogger, only communicators created after that point will use this per-process logger; communicators created earlier use the logger that was in effect at the time they were created. (This also means that you can call setProcessLogger multiple times; communicators created after that point will use whatever logger was established by the last call to setProcessLogger.)
getProcessLogger and setProcessLogger are language-specific APIs that are not defined in Slice. Therefore, for Java and C#, these methods appear in the Ice.Util class.
For applications that use the Application or Service convenience classes and do not explicitly configure a logger, these classes set a default per-process logger that uses the Ice.ProgramName property as a prefix for log messages. The Application class is described in the server-side language mapping chapters; more information on the Service class can be found in Section 8.3.2.

32.19.6 C++ Utility Classes

The Ice run time supplies a collection of utility classes that make use of the logger facility simpler and more convenient. Each of the logger’s four operations has a corresponding helper class:
namespace Ice {
class Print {
public:
    Print(const LoggerPtr&);
    void flush();
    ...
};

class Trace {
public:
    Trace(const LoggerPtr&, const std::string&);
    void flush();
    ...
};

class Warning {
public:
    Warning(const LoggerPtr&);
    void flush();
    ...
};

class Error {
public:
    Error(const LoggerPtr&);
    void flush();
    ...
};
}
The only notable difference among these classes is the extra argument to the Trace constructor; this argument represents the trace category.
To use one of the helper classes in your application, you simply instantiate it and compose your message:
if (errorCondition) {
    Error err(communicator>getLogger());
    err << "encountered error condition: " << errorCondition;
}
The Ice run time defines the necessary stream insertion operators so that you can treat an instance of a helper class as if it were a standard C++ output stream. When the helper object is destroyed, its destructor logs the message you have composed. If you want to log more than one message using the same helper object, invoke the flush method on the object to log what you have composed so far and reset the object for a new message.
The helper classes also supply insertion operators to simplify the task of logging an exception. The operators accept instances of std::exception (from which all Ice exceptions derive) and log the string returned by the what method. If you are using GCC, you can also enable the configuration property Ice.PrintStackTraces (see Appendix D), which causes the helper classes to log the stack trace of the exception in addition to the value of what.

Table of Contents Previous Next
Logo