Skip to content
Yair Ogen edited this page Apr 1, 2014 · 16 revisions

About

The Foundation Logging API is an extension of the standard log4j library exposed through SLF4J API. Besides complementing some core log4j features (e.g. size based log file rolling), it also introduces Structured Logging in support of log based analytics use cases.

Scope

The Foundation Logging should be used in ALL Foundation java components.

Contact

Yair Ogen

Issue tracking

Github Issues

Maven Info

<groupId>com.cisco.oss.foundation</groupId>
<artifcatId>logging-api</artifcatId>
<version>Latest Version</version>

<groupId>com.cisco.oss.foundation</groupId>
<artifcatId>logging-log4j</artifcatId>
<version>Latest Version</version>
Releases ========

All releases are available in public maven repo.

Features and Design

Following is a high level capture of the overall design of Foundation Logging:

Image

  • Users are expected to use the SLF4J API
  • Below that you can (optionally) use the Markers for Structured Logging (see below more info)
  • Foundation logging adds a binding (on logging-log4j lib) between SLF4J and log4j
  • Log4j Implementation allows the following main features:
    • Support writing to a log file (or any other output) asynchronously.
    • Verify all logs are in UTC timezone. This improves greatly the ability to compare logs from different nodes from (possibly) different sites.
    • Use common Log Patterns - I.e. the time-stamp format, logger name, thread name etc.
    • Add support for Archiving
    • Allow rolling to a new file every time your process is restarted.
    • Allow rolling to a new file based on a specific time of day (e.g. Midnight).
    • Allow rolling to a new file based on file size OR based on History (i.e. every 5 days - regardless of file size).

User Guide

Following is a simple, developer oriented, user guide on how to use Foundation Logging.

Configuring Log4j

As we currently only support log4j, you must add the log4j configuration file to your classpath. Following are some tips and tricks on how to tweak the logging library to your needs..

Enable Foundation Logging

In order to enable Foundation Logging you need to add this row in your config file:

log4j.loggerFactory=com.cisco.oss.foundation.logging.FoundationLogFactory

Enable Logging Pattern

In order to use the Foundation Logging logging pattern as detailed in the requirements, you need to use the "LogFileAppender" appender for logfile. This appender is tailored specific to meet the different requirements of foundation logging (utc log, common pattern, archiving to name a few) with nearly zero configuration needed.

There are some parameters that have default values passed to these appenders but they can all be overridden in the config file.

Mandatory parameter in the config file is:

file - file name including path of the base file.

Sample of minimum appender definition:

log4j.appender.logfile=com.cisco.oss.foundation.logging.appender.LogFileAppender
log4j.appender.logfile.file=./log/stat_rolling.log

Optional parameters in the config file are:

  • DatePattern - the date pattern that will appear in the log file name after rolling the file.
  • MaxFileSize - the size of the file that when reached will cause file rolling.
  • MaxRollFileCount - the maximum number of rolling files to be create. It is highly recommended to use default values here as wrong values may cause data loss.

Override this property only if you want to omit part of the Foundation Logging default parts. Note that is not recommended. For an explanation about the pattern different parts please see the requirements document.

Sample of full appender definition:

log4j.appender.logfile=com.cisco.oss.foundation.logging.appender.LogFileAppender
log4j.appender.logfile.file=./log/stat_rolling.log
log4j.appender.logfile.DatePattern=.yyyy-MM-dd
log4j.appender.logfile.MaxFileSize=100MB
log4j.appender.logfile.MaxRollFileCount=100

Conversion Pattern

The LogFileAppender automatically sets the ConversionPattern:

In LogFileAppender the pattern is:

%d{yyyy/MM/dd HH:mm:ss.SSS}{UTC} [%c{1}] [%t]: %audit %p: %X{flowCtxt} %sl %errorcode %throwable{full} %n.

Changing ConversionPattern

Example of changing LogFile ConversionPattern:

log4j.appender.logfile=com.cisco.oss.foundation.logging.appender.FoundationFileRollingAppender
log4j.appender.logfile.file=./log/stat_rolling.log
log4j.appender.logfile.layout=com.cisco.oss.foundation.logging.FoundationLoggingPatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d{yyyy/MM/dd HH:mm:ss.SSS}{UTC} [%c{1}] [%t]: %p: %X{flowCtxt} %m%n

Turn Off Archiving Support

By default the Foundation Loggger supports archiving of files after they are rolled. The default is to archive where a zip file is created in Windows OS and a gzip file is created in Linux or HP-UX. You can override this by adding the following property to the log4j.properties file:

FoundationLogger.archiveFiles=false

Configure Reload Period

By default the Foundation Loggger will reload the properties if a change to configuration file was done - every 10 seconds. You can override this by adding the following property to the log4j.properties file:

FoundationLogger.fileReloadDelay=60000 // value is in milliseconds

Configure Roll on Startup

By default each time you start your program a rolling will occur and the existing file will be rolled over and a new empty file will be created. In order to disable this feature you can into your log4j.properties the following:

FoundationLogger.rollOnStartup=false

Configure Max File Rolling Size

By default Foundation logging sets rolling to be a maximum of 100mb. You can override this on any specific file appender instance like this:

log4j.appender.logfile.MaxFileSize=100MB

Configure Max File Rolling Count

By default Foundation logging sets number of rolling files to 100 files. this is to reduce the possibility of loosing logs dues to rolling. You can override this on any specific file appender instance like this:

log4j.appender.logfile.MaxRollFileCount=100

Keeping Files According to their Age

There is an option to keep files according to their age instead of keeping a given number of files.

To apply this:

log4j.appender.logfile.LogFileScavenger=com.cisco.oss.foundation.logging.appender.HistoryLogFileScavenger
#The number of days it keeps files can also be configured. By default maxFileAge=7.
log4j.appender.logfile.MaxFileAge=10

Control Exception Handling

Use the new pattern layout (instead if the EnhancedPatternLayout):

com.cisco.oss.foundation.logging.FoundationLoggingPatternLayout

To enable new pattern features:

  • %throwable{none} - remove any exception logging when using the log4j API that received Throwable in it's method signature.
  • %throwable{short} - Display only short abbreviation of the exception stack trace. I.e. "Cause by: " - repeasred for exception in the trace.
  • %throwable{full} - Display full exception stack trace.
  • %errorcode - Display the error code in case throwable is of type: Application Exception. See Application Exception ({color:red}TODO:Add link{color})

How to Write to More than One File

If you want to write to more than one file, you need to add another appender and point to that new appender from one or more loggers:

log4j.rootCategory=error, logfile
log4j.logger.otherLogger=info, logfile2
log4j.loggerFactory=com.cisco.oss.foundation.logging.FoundationLogFactory

# "logfile" is just a suggested name, you can rename as needed.
# of course you can add as many as file appenders you may need

log4j.appender.logfile=com.cisco.oss.foundation.logging.appender.LogFileAppender
# the file is a sample and of course could be any name you want.
log4j.appender.logfile.file=./log/stat_rolling.log

# "logfile2" is just a suggested name, you can rename as needed.
# This is an example of adding a new appender

#log4j.appender.logfile2=com.cisco.oss.foundation.logging.appender.LogFileAppender
# the file is a sample and of course could be any name you want.
#log4j.appender.logfile2.file=./log/stat_rolling2.log

How to Indent Multi Line Messages

If you have in your messages more than one line (e.g. you use 'n'), you can format them so they all start aligned and start on the same position.

To enable this, we now support using "%slpretty" in the conversion pattern instead of "%sl" (in the past we used %pm for pretty messagem, and %m for regular message - which still works but does not support StrecturedLogging).

Example of use:

log4j.appender.logfile.layout.ConversionPattern=%d[yyyy/MM/dd HH:mm:ss.SSS][UTC] %p: %sl[pretty] %n

How to Enable Asynchronous Logging

It is quite common that logging every request can have a performance burden - for high load components this can be a problem. When using Asynchronous logging, the logging thread does not do the brunt of the logging work itself, instead it is delegated to a different thread to complete, without blocking the main application sequence.

To enable Asynchronous logging you need to add a new parameter in the log4j.properties file that will point to the appenders you wish to wrap with Asynchronous calls. The way it works is that you define appenders as you are used to and you can wrap any one of them Asynchronously.

Usage example (The square braces are a must and are NOT place holders):

FoundationLogger.asyncAppenderReferences=[rollingfile]

How to enable the Java Util Logging support

Since the slf4j jul bridge is highly costly we support a simplified approach to bridge java util logging and log4j. You can optionally add the following to your log4j config file:

#First off enable the support by setting this flag. By default it is shut down.
FoundationLogger.julSupportEnabled=true
 
#Second thing to do is point java util logging to an '''existing''' log4j appender. Only File or Console appenders are supported.
FoundationLogger.julAppenderRef=logfile
 
#Set the java util logger level. Note: you need to use java util levels and not log4j levels
jul.logger.org.glassfish=FINEST
 
#In some cases, the java util logger may have been initialized with the wrong parent.
#If you see the above level setting does not work, try setting the level using standard java util logging standards:
org.glassfish.level=FINEST

Application State

In order to meet the requirement of "enabling persistent data" which is to print some logs at the beginning of each file - please use the ApplicationState class. See java doc for info on the API.

log4j Default Configuration File

Following is the Foundation headend default log4j.properties configuration file. As a rule this sample should be used as is (except for changes to log file names as appropriate).

###################################################################################
#NEVER ERASE OR EDIT THE FOLLOWING LINE.

log4j.loggerFactory=com.cisco.oss.foundation.logging.FoundationLogFactory


###################################################################################
#Root logger definition:
#Defines the default logging level, and should be set to "error" (not "info")
#and overridden for Cisco application specific logs as below.
#Root logger should be assigned one appender only: "logfile" (and not "console")

log4j.rootLogger=error, logfile


###################################################################################
#NDS - functionality - specific logger section:
#By default NDS functionality specific logging should occur from the "info" level.
#For more detailed logging 'log4j.logger.com.nds' can be assigned "debug" or "trace"

log4j.logger.com.nds=info

###################################################################################
#Handle noisy third party software modules:
#Although root logger has a level of "error" by default, the following lines
#guarantee that even if this level is changed, a few very noisy internal libraries
#will remain quiet.

#communication utils
log4j.logger.com.nds.ch.utils.tsocket=error

#spring logs
log4j.logger.org.springframework=error

#connection pool component
log4j.logger.com.mchange=error

###################################################################################
#'logfile' appender settings:
#The logfile appender is of type 'LogFileAppender' which is automatically d
#with the UHE standard layout for log files.
#'log4j.appender.logfile.file' defines the log file path, which should
# be:/var/log/cisco/<appname>/<appname>.log (make sure that "appname" is instantiated
# as appropriate).

log4j.appender.logfile=com.cisco.oss.foundation.logging.appender.LogFileAppender
log4j.appender.logfile.file=/var/log/cisco/<appname>/<appname>.log

###################################################################################
# Additional logging settings:
# By default should be commented out, and appear as place holders.
# default set to true - archiving enabled
#FoundationLogger.archiveFiles=false

#default set to true - log is rolled after restart of process.
#(Use ‘tail –F’ and not ‘tail –f’ to get tail to show the new file after rollover.)
#FoundationLogger.rollOnStartup=false

Foundation Structured Logging

Introduction

What is Structured Logging?

To understand the notion of "structured" logging, it's useful to consider the current situation. Take a look at this snippet.

int transactionId = 6492;
String actionType = "CreateHousehold";
String householdType = "Multi";
int deviceCount = 10;
logger.Log( "Transaction " + transactionId + " of type " + actionType + "( " + householdType + ") " + " was received for creation of " + deviceCount + " devices");

What is happening here? A component is logging information about a request to create a certain type of household. So far so good. Except that once in the log file, all the data points themselves - transactionId, actionType, householdType and deviceCount have essentially been "deconstructed" into a string. To reestablish these values - let's say for the purpose of BI analysis or log file playback, a process of parsing will be necessary. Parsing is expensive (developer time) and error prone.

At the conceptual level there is a flaw here. The component 'knows' the values for transactionId, actionType, householdType and deviceCount, but it actively deconstructs them in the logging method, for lack of a better option.

Structured Logging is that better option. It will allow component developers to use a standard based mechanism to log messages, without having to deconstruct the data points in the process. Consider the following pseudo code:

class HouseHouseholdTransaction
{
int transactionId;
int actionType;
String householdType;
int deviceCount;
}
logger.info( new HouseHouseholdTransaction( 6492,"CreateHousehold", "Multi", 10),"");

A configuration/resource file could define a pattern for the human readable log file, something like:

messagetype.HouseHouseholdTransaction.format = "Transaction $transactionId of type $actionType ( $householdType ) was received for creation of $deviceCount $devices"

Because the data points of HouseHouseholdTransaction are available to Foundation Logging infrastructure, it’s now possible to implement additional appenders that can work with ‘ HouseHouseholdTransaction ’ directly and do all types of interesting things in the area of playback and BI.

In summary, 'Structured Logging' is a mechanism, based on standard logging API, to allow components to maintain a log message's data points in structured fashion, to provide visibility and control for logging to human readable files, BI data files or playback files.

Implementation

The current implementation is based on the popular [http://www.slf4j.org/ slf4j](http://www.slf4j.org/ slf4j "wikilink") library. Slf4j allows users to code their logging against a common API which can in turn be pointed to a large variety of implementations (log4j, java util logging, commons-logging to name a few).

One of the basic concepts in slf4j is the notion of a [http://slf4j.org/api/org/slf4j/Marker.html Marker](http://slf4j.org/api/org/slf4j/Marker.html Marker "wikilink").

Markers allow us to enable users to log their logs, that are targeted to an external consumer (as mentioned above), using markers themselves to enrich the log event with log event specific data which is not part of the regular log message or payload (e.g. source host in case of remote request handler or correlation id etc.).

In Depth Implementation

This section describes the solution in depth and will answer some commonly asked questions.

To use the Marker API you will have to use the slf4j API. Regardless, we expect users to use the fl4j API which will enable switch to another slf4j binding seamlessly to the applicative code.

How to use Foundation Logging Markers

When using Foundation Logging Markers to log transaction activity, you should always use the same Marker type for all steps in the transaction. Thus, the Marker type should have some type of 'state' field that describes the current transaction phage (examples: START, SUCCESS, FAILURE). As per explanation below, it's possible to define multiple human readable output formats for the same Marker, depending on its state.

Human readable logs

If you use a Logger and pass in a Marker and you want the output in a human readable format, you should follow the following guidelines.

First and foremost First and foremost - your marker should extend AbstractFoundationLoggingMarker.

User Field Annotation Any field you want to expose and use in an external pattern should be annotated using @UserField.

The UserField annotation can, optionally, be set with 2 attributes:

  • suppressNull - set to true if you wish a field to be omitted when its value is not set. default is false meaning if a field in the marker is not filled, a "null" string will be used.
  • description - for future use, you may add description of this field.

Marker fields annotated by @UserField are accesible from the pattern layout by using the %u{fieldName} syntax.

For example:

My default format for %u{sourceId} and %u{sourceType}

In the above example, in runtime, the actual values of "sourceId" and "sourceType" fields will be inserted in their designated place holders.

Defining Layout Annotations

The recommended way to define Human Readable Layout formats is by using the following annotations:

  • @DefaultFormat - This is a must in any Marker. You specify here the default human readable format of this Marker. See example below.
  • @ConditionalFormats - Optionally You can define a list of @ConditionalFormat under a single @ConditionalFormats annotation. Each @ConditionalFormat contains a format String and a list of @FieldCriterion annotations. Each @FieldCriterion contains a name and a value.

How Does It Work Upon Component start-up, Foundation Logging scans the classpath and finds all markers that have the above annotations. Once found, these annotations are parse ONCE per marker class and a formatter class is generated in Runtime. This class contains the logic on which format is to be used cased on the marker state.

The logic of the annotations is as follows: Between @FieldCriterion there is an AND relationship. Between the @ConditionalFormat annotations there is a OR relationship. you can see the generated class source in the log file if you turn on TRACE level debug on the "log4j.logger.com.cisco.oss.foudnation.logging" logger in your log4j.properties.

Marker Example

package com.cisco.oss.foundation.logging.structured.test;

import com.cisco.oss.foundation.logging.structured.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@DefaultFormat(TransactionMarker.DEFAULT_FORMAT)

@ConditionalFormats({

@ConditionalFormat( format=TransactionMarker.COND_FORMAT_1,
criteria={
@FieldCriterion(name="sourceId", value="sourceId1"),
@FieldCriterion(name="sourceType", value="sourceType1")}),
@ConditionalFormat( format=TransactionMarker.COND_FORMAT_2,
criteria={
@FieldCriterion(name="sourceId", value="sourceId2"),
@FieldCriterion(name="sourceType", value="sourceType2")
}
),
@ConditionalFormat( format=TransactionMarker.COND_FORMAT_3,
criteria={
@FieldCriterion(name="sourceId", value="sourceId3"),
@FieldCriterion(name="sourceType", value="sourceType3")
}
)
})

public class TransactionMarker extends BaseTransactionLoggingMarker {

Logger LOGGER = LoggerFactory.getLogger(TransactionMarker.class);

private static final long serialVersionUID = 9161271890930513129L;

public static final String DEFAULT_FORMAT = "my default uncoditioned format!!!";
public static final String COND_FORMAT_1 = "my human readbale format 1";
public static final String COND_FORMAT_2 = "my human readbale format 2";
public static final String COND_FORMAT_3 = "my human readbale format 3";

public TransactionMarker(String sessionId) {
this.sessionId = sessionId;
}

@UserField(suppressNull = true)
private FoundationLoggingEventType eventType;
@UserField
private FoundationLoggingTransMsgType transMsgType;
@UserField
private String sessionId;
@UserField
private String sourceId;
@UserField
private String sourceType;
@UserField
private String destinationId;
@UserField
private String destinationType;
@UserField
private String messagePayload;

public TransactionMarker setEventType(FoundationLoggingEventType eventType) {
this.eventType = eventType;
return this;
}
public TransactionMarker setTransMsgType(FoundationLoggingTransMsgType transMsgType) {
this.transMsgType = transMsgType;
return this;
}
public TransactionMarker setSessionId(String sessionId) {
this.sessionId = sessionId;
return this;
}
public TransactionMarker setSourceId(String sourceId) {
this.sourceId = sourceId;
return this;
}
public TransactionMarker setSourceType(String sourceType) {
this.sourceType = sourceType;
return this;
}
public TransactionMarker setDestinationId(String destinationId) {
this.destinationId = destinationId;
return this;
}
public TransactionMarker setDestinationType(String destinationType) {
this.destinationType = destinationType;
return this;
}
public TransactionMarker setMessagePayload(String messagePayload) {
this.messagePayload = messagePayload;
return this;
}

public static long getSerialversionuid() {
return serialVersionUID;
}
public static String getDefaultFormat() {
return DEFAULT_FORMAT;
}
public static String getCondFormat1() {
return COND_FORMAT_1;
}
public static String getCondFormat2() {
return COND_FORMAT_2;
}
public static String getCondFormat3() {
return COND_FORMAT_3;
}
public FoundationLoggingEventType getEventType() {
return eventType;
}
public FoundationLoggingTransMsgType getTransMsgType() {
return transMsgType;
}
public String getSessionId() {
return sessionId;
}
public String getSourceId() {
return sourceId;
}
public String getSourceType() {
return sourceType;
}
public String getDestinationId() {
return destinationId;
}
public String getDestinationType() {
return destinationType;
}
public String getMessagePayload() {
return messagePayload;
}
}

In the above example the generated formatter class will check if getSourceId() is equal to "sourceId1" AND getSourceType() is equal to "sourceType1" - if yes - COND_FORMAT_1 will be used.

If NOT, formatter class will check if getSourceId() is equal to "sourceId2" AND getSourceType() is equal to "sourceType2" - if yes - COND_FORMAT_2 will be used.

If NOT, formatter class will check if getSourceId() is equal to "sourceId3" AND getSourceType() is equal to "sourceType3" - if yes - COND_FORMAT_3 will be used.

If NOT - DEFAULT_FORMAT will be used.

Defining Layout Overrides Via External XML Foundation Logging, when started, will search the class path for a file named: "messageFormat.xml". This is an optional file that allows users to alter the format strings returned by the annotations and even change the conditions internal logic.

The schema file for this xml is located [http://ch-infra.il.nds.com/cabResources/messageFormat.xsd here](http://ch-infra.il.nds.com/cabResources/messageFormat.xsd here "wikilink")

The xml file has an a advantage over the annotations that it supports different formats for different appenders.

Here is an XML example. Note that the minimum required elements and attributes are:

  • One "defaultAppender" element (not more than one). Minimal attribute is:
    • defaultFormat - - the default format of this marker for the default appender - i.e. any appender used that isn't detailed in an "appender" element below.
  • Zero or more "appender" elements. Minimal attributes per appender element are:
    • id - the fully qualified marker class name
    • defaultFormat - the default format of this marker.

All other elements and attributes are optional and follow the same logic described above for the annotations.

<markers xsi:noNamespaceSchemaLocation="http://ch-infra.il.nds.com/cabResources/messageFormat.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<marker id="com.cisco.oss.foundation.logging.structured.test.TransactionMarker">
<defaultAppender defaultFormat="My default format for %u{sourceId} and %u{sourceType} and %u{base}">
<criteria>
<criterion format="my conditional format 1 from xml">
<field name="sourceId" equals="sourceId1" />
<field name="sourceType" equals="sourceType1" />
</criterion>
<criterion format="my conditional format 2 from xml">
<field name="sourceId" equals="sourceId2" />
<field name="sourceType" equals="sourceType2" />
</criterion>
<criterion format="my conditional format 3 from xml">
<field name="sourceId" equals="sourceId3" />
<field name="sourceType" equals="sourceType3" />
</criterion>
<criterion format="my conditional format 4 from xml">
<field name="sourceId" equals="sourceId3" />
<field name="sourceType" equals="sourceType4" />
</criterion>
</criteria>
</defaultAppender>

<appender id="logfile" defaultFormat="My default format for %u{sourceId} and %u{sourceType}">
<criteria>
<criterion format="my conditional format 1 from xml">
<field name="sourceId" equals="sourceId1" />
<field name="sourceType" equals="sourceType1" />
</criterion>
<criterion format="my conditional format 2 from xml">
<field name="sourceId" equals="sourceId2" />
<field name="sourceType" equals="sourceType2" />
</criterion>
</criteria>
</appender>

<appender id="anotherfile" defaultFormat="My default format for %u{sourceId} and %u{sourceType} and %u{base}">
<criteria>
<criterion format="my conditional format 1 from xml - something else">
<field name="sourceId" equals="sourceId1" />
<field name="sourceType" equals="sourceType1" />
</criterion>
</criteria>
</appender>

<appender id="biFile" defaultFormat="My default format for %u{sourceId} and %u{sourceType}" />

</marker>

<marker id="TransactionMarker3">
<defaultAppender defaultFormat="My default format for %u{sourceId} and %u{sourceType}" />

<appender id="logfile" defaultFormat="My default format for %u{sourceId} and %u{sourceType}">
<criteria>
<criterion format=" my conditional format 1 from xml ">
<field name="sourceId" equals="sourceId1" />
<field name="sourceType" equals="sourceType1" />
</criterion>
<criterion format="my conditional format 2 from xml">
<field name="sourceId" equals="sourceId2" />
<field name="sourceType" equals="sourceType2" />
</criterion>
</criteria>
</appender>

<appender id="anotherfile" defaultFormat="My default format for %u{sourceId} and %u{sourceType}">
<criteria>
<criterion format="my conditional format 1 from xml">
<field name="sourceId" equals="sourceId1" />
<field name="sourceType" equals="sourceType1" />
</criterion>
</criteria>
</appender>

<appender id="biFile" defaultFormat="My default format for %u{sourceId} and %u{sourceType}" />

</marker>

<marker id="com.cisco.oss.foundation.logging.structured.test.TransactionMarker2">
<defaultAppender defaultFormat="My default format for %u{sourceId} and %u{sourceType}" />

<appender id="logfile" defaultFormat="My default format for %u{sourceId} and %u{sourceType}">
<criteria>
<criterion format=" my conditional format 1 from xml ">
<field name="sourceId" equals="sourceId1" />
<field name="sourceType" equals="sourceType1" />
</criterion>
<criterion format="my conditional format 2 from xml">
<field name="sourceId" equals="sourceId2" />
<field name="sourceType" equals="sourceType2" />
</criterion>
</criteria>
</appender>

<appender id="anotherfile" defaultFormat="My default format for %u{sourceId} and %u{sourceType}">
<criteria>
<criterion format="my conditional format 1 from xml">
<field name="sourceId" equals="sourceId1" />
<field name="sourceType" equals="sourceType1" />
</criterion>
</criteria>
</appender>

<appender id="biFile" defaultFormat="My default format for %u{sourceId} and %u{sourceType}" />

</marker>

</markers>

UHE Developers Notes

The above guideline has omitted the log/audit guideline you may remember from Ex-NDS Wiki as it is currently under review.

Q&A

Q. When do files get “rolled”? -- e.g. when does one file close and a new one opens?

A. Either on midnight, OR when max file size is reached, OR when component is restarted.

Q. Where are the default parameters controlled?

A. They are hard coded inside Foundation logging library -- but can (optionally) be overridden in log4j.properties.

Q. Does archiving flag control when log files are rolled or deleted?

A. It only controls whether to archive a file once a new one was created. It by no means deleted files or does anything other than archive files.

Q. We’ve seen log messages get deleted when archiving is turned on. Why?

A. there was a bug prior to iteration 35 that rolling during archive caused messages to get lost. Post version 3.35.0-1 (inclusive) this is resolved.

Q. We’ve seen that upon rolling to a new file -- only last 10 files are kept. Is this expected?

A. There was a bug in Foundation logging that mandated override of MaxFileRollCount=100 to overcome this issue. As of version 3.35.0-1 -- this bug was resolved and you can safely rely on the defaults that come with the library itself.

Clone this wiki locally