<html>
<head>
<base href="https://wiki.asterisk.org/wiki">
<link rel="stylesheet" href="/wiki/s/en/2171/18/9/_/styles/combined.css?spaceKey=TOP&forWysiwyg=true" type="text/css">
</head>
<body style="background: white;" bgcolor="white" class="email-body">
<div id="pageContent">
<div id="notificationFormat">
<div class="wiki-content">
<div class="email">
<h2><a href="https://wiki.asterisk.org/wiki/display/TOP/Logging+Service+Design">Logging Service Design</a></h2>
<h4>Page <b>edited</b> by <a href="https://wiki.asterisk.org/wiki/display/~khunt">Ken Hunt</a>
</h4>
<br/>
<h4>Changes (0)</h4>
<div id="page-diffs">
<table class="diff" cellpadding="0" cellspacing="0">
<tr><td class="diff-snipped" >...<br></td></tr>
</table>
</div> <h4>Full Content</h4>
<div class="notificationGreySide">
<p>The log4scf component provides a client/server logging facility for Asterisk SCF. The requirements are fairly straightforward:</p>
<ul>
        <li>Overall
        <ul>
                <li>Message filtering based on source component and level</li>
                <li>Centralized configuration at the server</li>
                <li>Thread safe</li>
        </ul>
        </li>
        <li>Client
        <ul>
                <li>Minimize cost when logging is disabled</li>
                <li>Filter messages at client to reduce network traffic</li>
                <li>Super-simple setup and usage
                <ul>
                        <li>Simplified API that hides ICE details</li>
                </ul>
                </li>
        </ul>
        </li>
        <li>Server
        <ul>
                <li>Configurable logging destination
                <ul>
                        <li>Files</li>
                        <li>Syslog</li>
                </ul>
                </li>
        </ul>
        </li>
</ul>
<div class='panelMacro'><table class='noteMacro'><colgroup><col width='24'><col></colgroup><tr><td valign='top'><img src="/wiki/images/icons/emoticons/warning.gif" width="16" height="16" align="absmiddle" alt="" border="0"></td><td>UML models are a bit out of date</td></tr></table></div>
<h2><a name="LoggingServiceDesign-Futurework"></a>Future work</h2>
<ul>
        <li>Clients should also use ChaingedLogOut, so they can log locally as well as remotely.
        <ul>
                <li>Local logging should, ideally, have different levels than remote. This will allow fine grain local logging and coarse grain remote logging.</li>
        </ul>
        </li>
        <li>File rotation</li>
</ul>
<h2><a name="LoggingServiceDesign-ClientAPI"></a>Client API</h2>
<p>The client API is pretty straightforward. There is a default <tt>LoggerFactory</tt>, which is used to build <tt>Logger</tt>'s. These <tt>Logger</tt>'s can be built per-instance, per-class or per-component. Each <tt>Logger</tt> is initialized with a source identifying the message source. You can (and probably will) have multiple <tt>Logger</tt>'s with the same source string.</p>
<p><tt>Logger</tt>'s have a logical inheritance relationship, established using <tt>Logger</tt>'s name. For example, the <tt>Logger</tt> "AsteriskSCF.System" is a parent of "AsteriskSCF.System.Logging". <tt>Logger</tt>'s will inherit settings from their parent, unless they've been explicitly set.</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="theme: Confluence; brush: cpp; gutter: false">Logger &root = uut.getLogger("");
Logger &asteriskScf = uut.getLogger("AsteriskSCF");
Logger &core = uut.getLogger("AsteriskSCF.Core");
Logger &routing = uut.getLogger("AsteriskSCF.Core.Routing");
root.setLevel(Off);
assert(Off == routing.getEffectiveLevel()); // inherits from root
core.setLevel(Debug);
assert(Debug == routing.getEffectiveLevel()); // inherits from core
root.setLevel(Critical);
assert(Critical == asteriskScf.getEffectiveLevel()); // inherits from root
assert(Debug == routing.getEffectiveLevel()); // inherits from core
core.unsetLevel();
assert(Critical == routing.getEffectiveLevel()); // inherits from root</pre>
</div></div>
<p><tt>Logger</tt>'s are thread safe, and easily shared. Usage will be something like this:</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="theme: Confluence; brush: cpp; gutter: false">// demonstrating logging for the core Widget library
class Widget
{
public:
/* ... */
private:
static const Logger &logger;
};
Logger const &Widget::logger = getLoggerFactory()::getLogger("AsteriskSCF.Core.Widget");
void Widget::foo(Bar b)
{
// ostream style
logger(Debug) << "Widget::foo(" << b << ")";
try
{
// ...
}
catch (WidgetException const &e)
{
// printf style
logger.logf(Error, "Error foo'ing a widget: %s", e.what());
rethrow;
}
// puts style
logger.logs(Debug, "Widget::foo() completed successfully");
}</pre>
</div></div>
<h3><a name="LoggingServiceDesign-Level"></a>Level</h3>
<p>Log levels are <del>copied</del> inspired by syslog.</p>
<ul>
        <li>Emergency: system is unusable</li>
        <li>Alert: action must be taken immediately</li>
        <li>Critical: critical conditions</li>
        <li>Error: error conditions</li>
        <li>Warning: warning conditions</li>
        <li>Notice: normal but significant condition</li>
        <li>Informational: informational messages</li>
        <li>Debug: debug-level messages</li>
</ul>
<p>The phony level <em>Off</em> is a part of the Level enum, and should only be used to disable <tt>Logger</tt>'s (<tt>setLevel(Off)</tt>).</p>
<h2><a name="LoggingServiceDesign-ClientDesign"></a>Client Design</h2>
<p>The central object in the client library is the <tt>Logger</tt>. It's built by a <tt>LoggerFactory</tt>, of which a default instance is provided by the <tt>Logging</tt> client library.</p>
<p>The <tt>Logger</tt> itself is responsible for client side filtering of log messages. It does so by comparing its log level with the log level of the message. As inspired by <a href="http://logging.apache.org/log4j/index.html" class="external-link" rel="nofollow">log4j</a>, by default a <tt>Logger</tt> inherits its log level from its parent, unless it's been explicitly set.</p>
<p>The actual logging is handled by the <tt>LogOut</tt> interface. An <tt>OstreamLogger</tt>, which logs all messages to an <tt>ostream</tt>, is provided for easier testing. The <tt>IceLogger</tt> is the interesting implementation, which sends log messages to the <tt>LoggingServer</tt>.</p>
<h3><a name="LoggingServiceDesign-IceLogger"></a>IceLogger</h3>
<p>There are two objects at play when you use an <tt>IceLogger</tt>. There's the <tt>IceLogger</tt> itself, which is stupid-simple and just sends are received logs to the server proxy that it has. Then there's the <tt>ConfiguredIceLogger</tt>, which listens on the <tt>ServiceLocator</tt>'s IceStorm topic for service updates. As <tt>LoggingServer</tt>'s are registered, unregistered, suspended and unsuspended, it will update the proxy for the corresponding <tt>IceLogger</tt>.</p>
<h3><a name="LoggingServiceDesign-IceConfigurator"></a>IceConfigurator</h3>
<p>There's a final client-side class of importance: the <tt>IceConfigurator</tt>. This class subscribes to the <tt>LoggingServer</tt>'s IceStorm topic for configuration updates. When configuration updates are received, it will update the configuration of the local <tt>Logger</tt>'s appropriately.</p>
<p><span class="image-wrap" style=""><img src="/wiki/download/attachments/5243609/client.png?version=5&modificationDate=1285180537503" style="border: 1px solid black" /></span></p>
<h2><a name="LoggingServiceDesign-ServerDesign"></a>Server Design</h2>
<p>The server is relatively simple. The <em>logs</em> function just needs to:</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="theme: Confluence; brush: cpp; gutter: false">if (isEnabledFor(source, level))
{
mOut->logs(source, level, message);
}</pre>
</div></div>
<p>The interesting part is in determining whether that logLevel is enabled for the given source. Performance is critical, since we don't want to spend unnecessary cycles on logging. And we need to maintain the inheritance of logging settings, as described <a href="#LoggingServiceDesign-ClientAPI">above</a>.</p>
<p>The <tt>LoggingServer</tt> does this by keeping a reverse sorted map of source strings to their configuration. <em>isEnabledFor()</em> will find the first configured source which is a subpath of the source being logged. This will either exactly match our configuration, or be the configuration the source would inherit from. Given the nature of logging configuration, this has an average case complexity of <em>O(log(n))</em>, with a worst case of <em>O(n)</em>, where <em>n</em> is the number of configured sources. Typically, <em>n</em> is a very low number (< 10).</p>
<p>The server is also responsible for publishing its configuration to all clients, to allow them to do filtering at their end. This merely requires the server to send its updated configuration to the <tt>ServerConfigurationListener</tt> interface, which will publish the configuration to all interested listeners via IceStorm.</p>
<h3><a name="LoggingServiceDesign-ChainedLogOut"></a>ChainedLogOut</h3>
<p>The class responsible for actually writing the logs is <tt>ChainedLogOut</tt>. This is a singly linked list, which will lot the message according to the specified implementation, and then pass the message off to the next <tt>ChainedLogOut</tt>.</p>
<p><span class="image-wrap" style=""><img src="/wiki/download/attachments/5243609/server.png?version=4&modificationDate=1285180547521" style="border: 1px solid black" /></span></p>
<h3><a name="LoggingServiceDesign-Alternativesconsidered"></a>Alternatives considered</h3>
<h4><a name="LoggingServiceDesign-Reuse"></a>Reuse</h4>
<p>The first alternative considered was to reuse the <tt>Logger</tt> class from the client implementation. This had two big problems.</p>
<p>The first is that it would create a <tt>Logger</tt> for every new source seen. These would be long lived objects, so the logic to decide when to free them would be non-trivial. If someone erroneously (or maliciously) wrote some code that logged messages with unique values for <em>source</em>, the LoggerServer memory consumption would grow without bound. This could also degrade performance, as the <em>isEnabledFor()</em> function would be <em>O(m * log(n))</em>, where <em>m</em> is the number of levels in the source string, and <em>n</em> is the number of sources seen by the <tt>LoggerServer</tt>.</p>
<h2><a name="LoggingServiceDesign-Slice"></a>Slice</h2>
<p><span class="image-wrap" style=""><img src="/wiki/download/attachments/5243609/slice.png?version=4&modificationDate=1285180547554" style="border: 1px solid black" /></span></p>
</div>
<div id="commentsSection" class="wiki-content pageSection">
<div style="float: right;" class="grey">
<a href="https://wiki.asterisk.org/wiki/users/removespacenotification.action?spaceKey=TOP">Stop watching space</a>
<span style="padding: 0px 5px;">|</span>
<a href="https://wiki.asterisk.org/wiki/users/editmyemailsettings.action">Change email notification preferences</a>
</div>
<a href="https://wiki.asterisk.org/wiki/display/TOP/Logging+Service+Design">View Online</a>
|
<a href="https://wiki.asterisk.org/wiki/pages/diffpagesbyversion.action?pageId=5243609&revisedVersion=13&originalVersion=12">View Changes</a>
|
<a href="https://wiki.asterisk.org/wiki/display/TOP/Logging+Service+Design?showComments=true&showCommentArea=true#addcomment">Add Comment</a>
</div>
</div>
</div>
</div>
</div>
</body>
</html>