doc/manual-html/chapter-10.html in copland-0.8.0 vs doc/manual-html/chapter-10.html in copland-1.0.0

- old
+ new

@@ -1,8 +1,8 @@ <html> <head> - <title>Copland Manual :: Chapter 10: Service Factories</title> + <title>Copland Manual :: Chapter 10: Logging</title> <link type="text/css" rel="stylesheet" href="manual.css" /> </head> <body> <div id="banner"> @@ -12,12 +12,12 @@ <span class="product">Copland&mdash;</span><br /> <span class="tagline">compose yourself...</span> </div> </td><td valign='middle' align='right'> <div class="info"> - Copland Version: <strong>0.8.0</strong><br /> - Manual Last Updated: <strong>2004-09-27 03:37 GMT</strong> + Copland Version: <strong>1.0.0</strong><br /> + Manual Last Updated: <strong>2004-10-12 02:22 GMT</strong> </div> </td></tr> </table> </div> @@ -37,18 +37,26 @@ <ol type="1"> <li><a href="chapter-1.html#s1">What is Copland?</a></li> - <li><a href="chapter-1.html#s2">Features</a></li> + <li><a href="chapter-1.html#s2">What Can Copland Do For Me?</a></li> - <li><a href="chapter-1.html#s3">Getting Copland</a></li> + <li><a href="chapter-1.html#s3">Copland <em>sans</em> Buzzwords</a></li> - <li><a href="chapter-1.html#s4">License Information</a></li> + <li><a href="chapter-1.html#s4">Copland <em>avec</em> Buzzwords</a></li> - <li><a href="chapter-1.html#s5">Support</a></li> + <li><a href="chapter-1.html#s5">The Buzzwords Themselves</a></li> + <li><a href="chapter-1.html#s6">Features</a></li> + + <li><a href="chapter-1.html#s7">Getting Copland</a></li> + + <li><a href="chapter-1.html#s8">License Information</a></li> + + <li><a href="chapter-1.html#s9">Support</a></li> + </ol> </li> <li> <a href="chapter-2.html"> @@ -147,12 +155,14 @@ <ol type="1"> <li><a href="chapter-8.html#s1">Descriptor Syntax</a></li> - <li><a href="chapter-8.html#s2">DefaultSymbolSource</a></li> + <li><a href="chapter-8.html#s2">FactoryDefaults</a></li> + <li><a href="chapter-8.html#s3">ApplicationDefaults</a></li> + </ol> </li> <li> <a href="chapter-9.html"> @@ -164,58 +174,72 @@ </ol> </li> <li><strong> <a href="chapter-10.html"> - Service Factories + Logging </a> </strong> <big>&larr;</big> <ol type="1"> - <li><a href="chapter-10.html#s1">Schemas</a></li> + <li><a href="chapter-10.html#s1">Log Factory</a></li> - <li><a href="chapter-10.html#s2">How do they work?</a></li> + <li><a href="chapter-10.html#s2">Configuration</a></li> - <li><a href="chapter-10.html#s3">BuilderFactory</a></li> - </ol> </li> <li> <a href="chapter-11.html"> + Service Factories + </a> + + <ol type="1"> + + <li><a href="chapter-11.html#s1">Schemas</a></li> + + <li><a href="chapter-11.html#s2">How do they work?</a></li> + + <li><a href="chapter-11.html#s3">BuilderFactory</a></li> + + </ol> + </li> + + <li> + <a href="chapter-12.html"> Schemas </a> <ol type="1"> - <li><a href="chapter-11.html#s1">Basic Format</a></li> + <li><a href="chapter-12.html#s1">Basic Format</a></li> - <li><a href="chapter-11.html#s2">Subschemas</a></li> + <li><a href="chapter-12.html#s2">Subschemas</a></li> - <li><a href="chapter-11.html#s3">Arrays</a></li> + <li><a href="chapter-12.html#s3">Arrays</a></li> - <li><a href="chapter-11.html#s4">Named vs. Anonymous Schemas</a></li> + <li><a href="chapter-12.html#s4">Named vs. Anonymous Schemas</a></li> - <li><a href="chapter-11.html#s5">Extending Schemas</a></li> + <li><a href="chapter-12.html#s5">Extending Schemas</a></li> - <li><a href="chapter-11.html#s6">Limitations</a></li> + <li><a href="chapter-12.html#s6">Limitations</a></li> </ol> </li> <li> - <a href="chapter-12.html"> + <a href="chapter-13.html"> Listeners and Event Producers </a> <ol type="1"> - <li><a href="chapter-12.html#s1">Event Producers</a></li> + <li><a href="chapter-13.html#s1">Event Producers</a></li> - <li><a href="chapter-12.html#s2">Listeners</a></li> + <li><a href="chapter-13.html#s2">Listeners</a></li> - <li><a href="chapter-12.html#s3">The Registry as an Event Producer</a></li> + <li><a href="chapter-13.html#s3">The Registry as an Event Producer</a></li> </ol> </li> </ol> @@ -289,104 +313,153 @@ </td><td valign='top' width="100%"> <div id="content"> - <h1>10. Service Factories</h1> + <h1>10. Logging</h1> <div class="section"> - <p>Sometimes it requires a little more effort (or a lot more effort) to instantiate and initialize a service than simply calling <code>#new</code> on it&#8217;s associated class. In such cases, you need to rely on a <em>service factory</em> to instantiate the service.</p> + <p>Copland has an integrated logging system based on a slight variation of Ruby&#8217;s own &#8220;logger&#8221; library. In addition to the features of that library, Copland&#8217;s logging system provides:</p> - <p>A service factory is just a regular service as far as Copland is concerned. It is declared in the usual way. However, a service that will be used as a service factory must implement at least one method: <code>#create_instance</code>. This method should accept two parameters, the <em>service point</em> to instantiate, and the <em>parameters</em> associated with this instantiation. (The <code>parameters</code> parameter will always be a hash.)</p> + <ul> + <li>A logger factory for creating shared logger instances</li> + <li><span class="caps">YAML</span>-based configuration of the factory</li> + <li>Customizable message formatting</li> + </ul> + </div> - <p>Here is an example that implements a trivial service factory:</p> -<pre> - class ExampleServiceFactory - def create_instance( point, parms ) - return { :point =&gt; point, - :parms =&gt; parms } - end + <h2> + <a name="s1"></a> + 10.1. Log Factory + </h2> - end -</pre> + - <p>This service would be introduced into Copland via the following package descriptor:</p> + <div class="section"> + <p>Each registry instance has its own instance of <code>Copland::LogFactory</code>. This provides a way to map a name to a logger instance, and allows each service point (for example) to have its own logger instance. This allows various logger settings to be tweaked <em>per service-point</em>.</p> -<pre> - --- - id: example + <p>For example, if I want debugging information to be logged for one service point, but not for another, I can specify the logging priority level for the one service point to include debug messages, and have such messages excluded for the other service point.</p> - service-points: + <p>The log factory allows default values to be set for the priority level, date format string, and message format string, which will be applied to any logger that does not have a specific value set for those values. The log factory also allows configuration of the IO device to log to, the filename to log to, and various other (Logger-specific) data items (see the <span class="caps">API</span> documentation for <code>Copland::LogFactory</code> for more information).</p> - ExampleServiceFactory: - implementor: some/file/ExampleServiceFactory -</pre> - - <p>And it would be employed like this:</p> - -<pre> - --- - id: demo - - service-points: - - ExampleService: - implementor: - factory: example.ExampleServiceFactory -</pre> - - <p>This service factory, when used to instantiate a service, would always return a new Hash object consisting of the service point and its parameters. Thus, the <code>ExampleService</code> service point would, when instantiated, always consist of a hash containing its own service point, and any parameters that were given when it was instantiated (none, in this case). Not a very useful service factory, but it demonstrates what it ought to do.<br /> + <p>If you ever need to access the current log factory instance, you can get it from the registry as the <code>copland.LogFactory</code> service.<br /> </p> </div> <h2> - <a name="s1"></a> - 10.1. Schemas + <a name="s2"></a> + 10.2. Configuration </h2> <div class="section"> - <p>As mentioned in the chapter on service points, a service point may be associated with a <em>schema</em>. More will be said on schemas (and specifically, on their formats) in the next chapter, but suffice it to say here that the schema allows a service point to specify what parameters it accepts when invoked as a factory service.<br /> -</p> - </div> + <p>There are three ways to configure loggers. The first is programmatically, manually tweaking each log instance directly. The second is to pass configuration options to the registry itself when it is built, and the third is to use a configuration file.</p> + <h3>Programmatic Configuration</h3> + <p>To programmatically configure a logger, just obtain a handle to a logger instance and then tweak it via the accessor methods of the logger:</p> - <h2> - <a name="s2"></a> - 10.2. How do they work? - </h2> +<pre> + factory = registry.service( "copland.LogFactory" ) + log = factory.get( "some.log.name" ) - + # Standard Logger attributes + log.level = Logger::INFO + log.progname = "different.log.name" + log.datetime_format = "%Y%m%d" - <div class="section"> - <p>When you specify a factory service as the implementor of another service, Copland automatically marks that service point as needing a <em>complex instantiator</em>. Thus, when it comes time to instantiate the service point, the parameters are collected, and if the factory has a schema, the parameters are validated against that schema. Then, the parameters are preprocessed (to translate values to their appropriate and expected types), and the factory&#8217;s <code>#create_instance</code> method is called. The result of that call is then treated as the new service.</p> + # Extended Logger attributes (provided by Copland) + log.message_format = "[%-5p] %d %c: %m" +</pre> - <p>Contrast this with the <em>simple instantiator</em>. When the simple instantiator is used, all it does (more or less) is invoke <code>#new</code> on the named class and return the result. The existence of factory services allows for much more complex (and powerful) behavior.<br /> -</p> - </div> + <p><em>Note:</em> programmatically changing the logger <code>progname</code> does not change the name by which the logger is known to the factory!</p> + <p>In general, programmatic tweaking of the logger attributes as demonstrated above is not going to be the best way to do it. First of all, it is tedious, and bug prone. Secondly, it is difficult to know where the best place to do such work is, since you need to set those attributes before any other service tries to access that log.</p> + <h3>Configuration via <code>Registry.build</code></h3> - <h2> - <a name="s3"></a> - 10.3. BuilderFactory - </h2> + <p>The second approach is to pass logger configuration options to <code>Registry.build</code>, when you create a registry instance:</p> - +<pre> + registry = Copland::Registry.build( + :log_device =&gt; STDOUT, + :log_default_level =&gt; Logger::INFO, + ... + ) +</pre> - <div class="section"> - <p>Copland comes with one predefined factory service: <code>copland.BuilderFactory</code>. With this factory you can implement most of the more common types of services. (There are always special cases, though&#8212;the <code>copland.lib</code>, <code>copland.remote</code> and <code>copland.webrick</code> libraries all define additional factory services for specialized uses.)</p> + <p>This gives you much greater flexibility than the previous approach, since it allows you to specify a greater array of options. The allowed options are:</p> - <p>The BuilderFactory allows you to not only instantiate a class, but to specify constructor parameters and set properties on the new object. It also allows you to specify methods that should be invoked in order to initialize a service. It is by this means that the &#8220;dependency injection&#8221; aspect of Copland comes into play.<br /> -</p> + <table class="list"> + <tr> + <td style="vertical-align:top;"><code>:log_config_file</code></td> + <td>This is the name of the configuration file to use to configure the log factory. It defaults to &#8220;logger.yml&#8221;. (See the next section for more information.)</td> + </tr> + <tr> + <td style="vertical-align:top;"><code>:log_device</code></td> + <td>This is the <span class="caps">IO </span>(or pseudo-IO) object to write log messages to. If this option is specified, <code>:log_filename</code> must not be specified (and vice versa). This allows you to log messages to arbitrary IO streams.</td> + </tr> + <tr> + <td style="vertical-align:top;"><code>:log_filename</code></td> + <td>This is the name of the file to write log messages to. If this option is specified, <code>:log_device</code> must not be specified (and vice versa). This defaults to &#8221;./copland.log&#8221;.</td> + </tr> + <tr> + <td style="vertical-align:top;"><code>:log_roll_age</code></td> + <td>This specifies the number of days before the log should be rolled. (This option is only useful when used with <code>:log_filename</code>.)</td> + </tr> + <tr> + <td style="vertical-align:top;"><code>:log_roll_frequency</code></td> + <td>This should be either <code>nil</code>, &#8220;daily&#8221;, &#8220;weekly&#8221;, or &#8220;monthly&#8221;, and specifies how frequently the log should be rolled. This option cannot be used with <code>:log_roll_age</code>.</td> + </tr> + <tr> + <td style="vertical-align:top;"><code>:log_roll_size</code></td> + <td>This specifies the maximum size of a log file. Once the log file gets larger than this value, the log will be rolled. (This option is only useful when used with <code>:log_filename</code>.)</td> + </tr> + <tr> + <td style="vertical-align:top;"><code>:log_default_date_format</code></td> + <td>This is the default date format string to use for the loggers that are created. It may be any string that is understood by <code>Time#strftime</code>. If <code>nil</code> (the default), then the default Logger date format string will be used.</td> + </tr> + <tr> + <td style="vertical-align:top;"><code>:log_default_message_format</code></td> + <td>This is the default message format string to use for the loggers that are created. It is a printf-styled string with special format specifiers&#8212;see the <span class="caps">API</span> documentation for <code>Copland::Logger#message_format=</code> for the available specifiers.</td> + </tr> + <tr> + <td style="vertical-align:top;"><code>:log_default_level</code></td> + <td>This is the default level for each logger. Messages that are logged below this level (also &#8220;priority&#8221; or &#8220;severity&#8221;) will not be logged. By default, all messages are logged.</td> + </tr> + <tr> + <td style="vertical-align:top;"><code>:log_levels</code></td> + <td>This is a hash. Each key should be a string containing a regular expression. For every logger whose name matches one of these patterns, the corresponding value will be used to define specific values for that logger. The value for each key must either be a string (in which case it is the name of the severity level to use), or a hash (containing any of the keys &#8220;level&#8221;, &#8220;date-format&#8221;, or &#8220;message-format&#8221;).</td> + </tr> + </table> + + <h3>Configuration via <span class="caps">YAML</span></h3> + + <p>The third option (and the most flexible) is to use a <span class="caps">YAML</span> configuration file to define the parameters of the log factory and its loggers. By default, this file is called &#8220;logger.yml&#8221;, but you can specify a different logger configuration file via the <code>:log_config_file</code> option to <code>Copland::Registry.build</code>. By default, the configuration file must reside in current working directory, but you can specify an explicit path in the string you give to <code>:log_config_file</code>.</p> + + <p>The file has options that correspond to the configuration parameters described above:</p> + +<pre> + --- + filename: log/filename.log + roll-age: 5 + default-date-format: %Y%m%d%H%M%S + default-message-format: %p %d %t %C: %m (%l) + default-level: DEBUG + levels: + copland.*: WARN + project.*: + level: INFO + date-format: %Y%m%d::%H%M%S + message-format: %p %d: %m +</pre> </div>