This feature was available since CXF 2.3.0, as part of the cxf-rt-management-web component, but no longer exists since CXF 3.2.0.

CXF supports collecting log events, converting them to ATOM Syndication Format and either pushing them to the Atom-aware consumers or making them available for polling. Logging is based on a custom java.util.logging (JUL) handler that can be registered with loggers extending today's publishing protocols.

CXF JAXRS and JAXWS endpoints can avail of this feature.

Push Style

Push-style handler enqueues log records as they are published from loggers. After the queue size exceeds configurable "batch size", processing of collection of these records (in size of batch size) is triggered. Batch of log events is transformed by converter to ATOM element and then it is pushed out by deliverer to client. Both converter and deliverer are configurable units that allow to change transformation and transportation strategies. Next to predefined own custom implementations can be used when necessary – see examples. Batches are processed sequentially to allow client side to recreate stream of events.

Limitations: Reliability is not supported out of the box, however there is predefined retrying delivery strategy. Persistence is also not supported, any enqueued and undelivered log events are lost on shutdown. Definitions of delivery endpoints is static, subscription of callback URIs is not yet supported.

Spring configuration

In simplest case pushing ATOM Feeds can be declared this way:

   <bean class="org.apache.cxf.management.web.logging.atom.AtomPushBean" init-method="init">
       <property name="url" value="http://somewhere.com/foo/bar"/>
       <property name="level" value="ALL" />
   </bean>

Spring bean creates ATOM push handler and registers it with root logger for all log levels. This setup leads to logging everything CXF, Spring and others included. Since batch size is not specified default value of one is used - each event is packed up as single feed pushed out to specified URL. Default conversion strategy and default WebClient-based deliver are used.

More complex example shows how to specify non-root logger and define batch size:

   <bean class="org.apache.cxf.management.web.logging.atom.AtomPushBean" init-method="init">
       <property name="url" value="http://somewhere.com/foo/bar"/>
       <property name="logger" value="org.apache.cxf.jaxrs" />
       <property name="level" value="INFO" />
       <property name="batchSize" value="10" />
   </bean>

To push to client events generated by different loggers on different levels, "loggers" property must be used instead of pair "logger" and "level":

   <bean class="org.apache.cxf.jaxrs.management.web.atom.AtomPushBean" init-method="init">
       <property name="url" value="http://somewhere.com/foo/bar"/>
       <property name="loggers" value="
           org.apache.cxf:DEBUG,
           org.apache.cxf.jaxrs,
           org.apache.cxf.bus:ERROR,
           myNamedLogger:INFO" />
   </bean>

In example above, second logger does not have specified level, in such case default level of "INFO" is used.

In all above cases, when first delivery fails engine of ATOM push handler is shutdown and no events will be processed and pushed until configuration reload. To avoid this frequent case, retrial can be enabled:

   <bean class="org.apache.cxf.management.web.logging.atom.AtomPushBean" init-method="init">
       <property name="url" value="http://somewhere.com/foo/bar"/>
       <property name="logger" value="org.apache.cxf.jaxrs" />
       <property name="level" value="INFO" />
       <property name="retryPause" value="linear" />
       <property name="retryPauseTime" value="60" />
       <property name="retryTimeout" value="300" />
   </bean>

In this case for 5 minutes ("retryTimeout") after delivery failure there will be 1 minute pause ("retryPauseTime") repeated every time with same value ("retryPause" as "linear"). Instead of same pause time, "exponential" value of "retryPause" can be used - each next time pause time doubles. When timeout value is set to 0 retrying is infinite. In case of invalid or missing values defaults are used: for pause time 30 seconds and for timeout 0 (infinite). Instead of same pause time, "exponential" value of "retryPauseType" can be used - each next time pause time doubles. When timeout value is set to 0 retrying is infinite. In case of invalid or missing values defaults are used: for pause time 30 seconds and for timeout 0 (infinite).

Ultimate control is given by "converter" and "deliverer" properties. Either beans of predefined or custom classes can be used (see "Programming syle" chapter for more details). Example below shows custom class using different transport protocol than default:

   <bean id="soapDeliverer" ...
   ...
   <bean class="org.apache.cxf.management.web.logging.atom.AtomPushBean" init-method="init">
       <property name="deliverer">
           <ref bean="soapDeliverer"/>
       </property>
       <property name="loggers" ... />
   </bean>

Note that specifying custom deliverer cause ignoring "url" and "retryXxx" because underneath configuration replaces employed tandem of RetryingDeliverer and WebClientDeliverer with provided one.

When ATOM feeds must be delivered to more than one endpoint and additionally each endpoint is fed by different loggers simply use multiple ATOM push beans in Spring config:

   <bean id="atom1" class="org.apache.cxf.management.web.logging.atom.AtomPushBean" init-method="init">
       <property name="url" value="http://someplace.com/foo/bar"/>
       ...
   </bean>
   <bean id="atom2" class="org.apache.cxf.jaxrs.management.web.atom.AtomPushBean" init-method="init">
       <property name="url" value="http://otherplace.com/baz/blah"/>
       ...
   </bean>
   ....

Properties file

When CXF is used either without Spring or logging is configured with properties file, support for this type of configuration becomes handy. ATOM push handler supports "simple configuration" with properties file; simple means aligned to expressiveness of JUL configuration that is limited to cases, where each type of handler can be used only once and registered with root logger.

Set of properties is very similar to Spring configuration with following exceptions:

  • Properties specify classes of custom deliverers and converters, instead of instances.
  • Custom deliverer must have public constructor with the only String parameters; created instance will have passed URL of client.
  • Multiple client endpoints is not supported out of the box (cannot instantiate multiple handlers as in Spring)

Example:

 handlers = org.apache.cxf.management.web.logging.atom.AtomPushHandler, java.util.logging.ConsoleHandler
 .level = INFO
 ...
 org.apache.cxf.jaxrs.ext.management.web.AtomPushHandler.url = http://localhost:9080
 org.apache.cxf.jaxrs.ext.management.web.AtomPushHandler.batchSize = 10
 org.apache.cxf.jaxrs.ext.management.web.AtomPushHandler.deliverer = WebClientDeliverer 
 org.apache.cxf.jaxrs.ext.management.web.AtomPushHandler.converter = foo.bar.MyConverter
 org.apache.cxf.jaxrs.ext.management.web.AtomPushHandler.retry.pause = linear
 org.apache.cxf.jaxrs.ext.management.web.AtomPushHandler.retry.pause.time = 10
 org.apache.cxf.jaxrs.ext.management.web.AtomPushHandler.retry.timeout = 360
 ...

Programming style

In most complex cases direct programming using org.apache.cxf.jaxrs.ext.logging.atom package may be necessary. In this case AtomPushHandler class is main artifact and Deliverer and Converter interfaces and their implementations are necessary components.

Following example:

    Deliverer d = new WebClientDeliverer("http://somewhere.com/foo/bar");
    d = new RetryingDeliverer(d, 300, 60, true);
    Converter c = new SingleEntryContentConverter();
    AtomPushHandler h = new AtomPushHandler(1, c, d);    
    Logger l = Logger.getLogger("org.apache.cxf.jaxrs");
    l.setLevel(Level.INFO);
    l.addHandler(h);

is equivalent to Spring configuration:

   <bean class="org.apache.cxf.management.web.logging.atom.AtomPushBean" init-method="init">
       <property name="url" value="http://somewhere.com/foo/bar"/>
       <property name="logger" value="org.apache.cxf.jaxrs" />
       <property name="level" value="INFO" />
       <property name="retryPause" value="linear" />
       <property name="retryPauseTime" value="60" />
       <property name="retryTimeout" value="300" />
   </bean>

Poll Style

AtomPullServer acts as an Atom feed endpoint and makes all log events it has accumulated or read from some external storage available for polling.

Log events are made available in pages, that is a feed instance will list up to a configurable maximum number of entries and will also include atom links of types 'prev', 'next', 'first' and 'last', thus making it possible to browse through all the log records.

Spring configuration

When configuring AtomPullServer endpoints, one can set the 'loggers' property the same way as it is done for AtomPushBeans, for example :

   <bean class="org.apache.cxf.management.web.logging.atom.AtomPullServer" init-method="init">
       <property name="loggers" value="
           org.apache.cxf:DEBUG,
           org.apache.cxf.jaxrs,
           org.apache.cxf.bus:ERROR,
           myNamedLogger:INFO" />
       <property name="pageSize" value="30"/>
   </bean>

In addition to the 'loggers' property, a 'pageSize' property limiting a number of entries per page to 30 is also set (default is 40).

One can have a ReadWriteLogStorage bean injected into AtomPushBean if the log records have to be periodically offloaded from memory and persisted across restarts :

   <bean id="storage" class="org.apache.cxf.systest.jaxrs.JAXRSLoggingAtomPullSpringTest$Storage"/>

   <bean class="org.apache.cxf.management.web.logging.atom.AtomPullServer" init-method="init">
       <property name="loggers" value="org.apache.cxf.jaxrs" />
       <property name="maxInMemorySize" value="400"/>
       <property name="storage">
           <ref bean="storage"/>
       </property>
   </bean>

When a number of records in memory reaches 400 (default is 500) then the records will be offloaded into a provided storage and will be read from it after the restart or when a client has requested a relevant page. If no storage is available then after an in-memory limit is reached the oldest records will be discarded, one can set a maxInMemorySize property to a large enough value if needed.

Another option is to require a given AtomPullServer to read from the external read-only storage by registering a ReadableLogStorage bean. For example, very often, the runtime is already logging to some external file, thus AtomPullServer can be asked to read from this file only with the help of ReadableLogStorage, without AtomPullServer having to catch log events too.

Once AtomPullServer has been configured, it has to be registered as a service bean with the jaxrs endpoint so that Atom aware clients (blog readers, etc) can start polling it :

<jaxrs:server id="atomServer" address="/atom">
 <jaxrs:serviceBeans>
   <ref bean="atomPullServer"/>
 </jaxrs:serviceBeans>

 <jaxrs:providers>
  <ref bean="feed"/>
  <ref bean="entry"/>
 </jaxrs:providers>
</jaxrs:server>

<bean id="feed" class="org.apache.cxf.jaxrs.provider.AtomFeedProvider"/>
<bean id="entry" class="org.apache.cxf.jaxrs.provider.AtomEntryProvider"/>

Linking to Atom endpoints from CXF Services page

If you would like your users to find about the Atom feeds which are collecting log events from your endpoints then AtomPullServers will need to have a CXF bus injected, as well as be told about the address of the corresponding atom feed endpoint and of the jaxrs or jaxws endpoint :

<bean id="atomPullServer" class="org.apache.cxf.management.web.logging.atom.AtomPullServer" init-method="init">
   <property name="loggers" value="org.apache.cxf.systest.jaxrs.JAXRSLoggingAtomPullSpringTest$Resource:ALL"/>
   <property name="bus">
      <ref bean="cxf"/>
   </property>
   <!-- this is a jaxrs:server/@adrress or jaxws:endpoint/@address of the endpoint generating the log events -->
   <property name="endpointAddress" value="/resource"/>
   <!-- this is a jaxrs:server/@address of the endpoint for which this atomPullServer bean is registered as a service bean -->
   <property name="serverAddress" value="/atom"/>
</bean>