-
Notifications
You must be signed in to change notification settings - Fork 11
Home
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.
The Foundation Logging should be used in ALL Foundation java components.
https://github.com/foundation-runtime/logging/issues Github Issues
<syntaxhighlight lang="xml"> <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></pre> </syntaxhighlight>>All releases are available in public maven repo.
Following is a high level capture of the overall design of Foundation Logging:
~~ATTACHED_IMAGE~~VCSF Logging Design.jpg
- 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).
Following is a simple, developer oriented, user guide on how to use Foundation Logging.
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..
In order to enable Foundation Logging you need to add this row in your config file:
<syntaxhighlight lang="ini"> log4j.loggerFactory=com.cisco.oss.foundation.logging.FoundationLogFactory </syntaxhighligt></syntaxhighlight>
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:
<syntaxhighlight lang="ini"> log4j.appender.logfile=com.cisco.oss.foundation.logging.appender.LogFileAppender log4j.appender.logfile.file=./log/stat_rolling.log </syntaxhighlight>
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.
Sample of full appender definition:
<syntaxhighlight lang="ini"> 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 </syntaxhighlight>
The LogFileAppender automatically sets the ConversionPattern:
In LogFileAppender the pattern is:
<syntaxhighlight lang="ini"> %d{yyyy/MM/dd HH:mm:ss.SSS}{UTC} [%c{1}] [%t]: %audit %p: %X{flowCtxt} %sl %errorcode %throwable{full} %n. </syntaxhighlight>
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
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
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
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
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
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
There is an option to keep files according to their age instead of keeping a given number of files.
To apply this:
#The number of days it keeps files can also be configured. By default maxFileAge=7. log4j.appender.logfile.MaxFileAge=10
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: <exception></exception> <exception></exception>" - 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})
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:
&#35; &quot;logfile&quot; is just a suggested name, you can rename as needed. &#35; of course you can add as many as file appenders you may need log4j.appender.logfile&#61;com.cisco.oss.foundation.logging.appender.LogFileAppender &#35; the file is a sample and of course could be any name you want. log4j.appender.logfile.file&#61;./log/stat_rolling.log &#35; &quot;logfile2&quot; is just a suggested name, you can rename as needed. &#35; This is an example of adding a new appender &#35;log4j.appender.logfile2&#61;com.cisco.oss.foundation.logging.appender.LogFileAppender &#35; the file is a sample and of course could be any name you want. &#35;log4j.appender.logfile2.file&#61;./log/stat_rolling2.log
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).
INFO: Note |
Example of use:
log4j.appender.logfile.layout.ConversionPattern&amp;&#35;61&#59;%d&amp;&#35;91&#59;yyyy/MM/dd HH&amp;&#35;58&#59;mm&amp;&#35;58&#59;ss.SSS&amp;&#35;93&#59;&amp;&#35;91&#59;UTC&amp;&#35;93&#59; %p&amp;&#35;58&#59; %sl&amp;&#35;91&#59;pretty&amp;&#35;93&#59; %n
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&amp;&#35;61&#59;&amp;&#35;91&#59;rollingfile&amp;&#35;93&#59;
INFO: Note |
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.
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).
&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59; &amp;&#35;35&#59;NDS &amp;&#35;45&#59; functionality &amp;&#35;45&#59; specific logger section&amp;&#35;58&#59; &amp;&#35;35&#59;By default NDS functionality specific logging should occur from the &amp;quot&#59;info&amp;quot&#59; level. &amp;&#35;35&#59;For more detailed logging &amp;&#35;39&#59;log4j.logger.com.nds&amp;&#35;39&#59; can be assigned &amp;quot&#59;debug&amp;quot&#59; or &amp;quot&#59;trace&amp;quot&#59; log4j.logger.com.nds&amp;&#35;61&#59;info &amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59; &amp;&#35;35&#59;Handle noisy third party software modules&amp;&#35;58&#59; &amp;&#35;35&#59;Although root logger has a level of &amp;quot&#59;error&amp;quot&#59; by default, the following lines &amp;&#35;35&#59;guarantee that even if this level is changed, a few very noisy internal libraries &amp;&#35;35&#59;will remain quiet. &amp;&#35;35&#59;communication utils log4j.logger.com.nds.ch.utils.tsocket&amp;&#35;61&#59;error &amp;&#35;35&#59;spring logs log4j.logger.org.springframework&amp;&#35;61&#59;error &amp;&#35;35&#59;connection pool component log4j.logger.com.mchange&amp;&#35;61&#59;error &amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59; &amp;&#35;35&#59;&amp;&#35;39&#59;logfile&amp;&#35;39&#59; appender settings&amp;&#35;58&#59; &amp;&#35;35&#59;The logfile appender is of type &amp;&#35;39&#59;LogFileAppender&amp;&#35;39&#59; which is automatically d &amp;&#35;35&#59;with the UHE standard layout for log files. &amp;&#35;35&#59;&amp;&#35;39&#59;log4j.appender.logfile.file&amp;&#35;39&#59; defines the log file path, which should &amp;&#35;35&#59; be&amp;&#35;58&#59;/var/log/cisco/&amp;lt&#59;appname&amp;gt&#59;/&amp;lt&#59;appname&amp;gt&#59;.log (make sure that &amp;quot&#59;appname&amp;quot&#59; is instantiated &amp;&#35;35&#59; as appropriate). log4j.appender.logfile&amp;&#35;61&#59;com.cisco.oss.foundation.logging.appender.LogFileAppender log4j.appender.logfile.file&amp;&#35;61&#59;/var/log/cisco/&amp;lt&#59;appname&amp;gt&#59;/&amp;lt&#59;appname&amp;gt&#59;.log &amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59;&amp;&#35;35&#59; &amp;&#35;35&#59; Additional logging settings&amp;&#35;58&#59; &amp;&#35;35&#59; By default should be commented out, and appear as place holders. &amp;&#35;35&#59; default set to true &amp;&#35;45&#59; archiving enabled &amp;&#35;35&#59;FoundationLogger.archiveFiles&amp;&#35;61&#59;false &amp;&#35;35&#59;default set to true &amp;&#35;45&#59; log is rolled after restart of process. &amp;&#35;35&#59;(Use ‘tail –F’ and not ‘tail –f’ to get tail to show the new file after rollover.) &amp;&#35;35&#59;FoundationLogger.rollOnStartup&amp;&#35;61&#59;false
INFO: Note |
To understand the notion of "structured" logging, it's useful to consider the current situation. Take a look at this snippet.
int transactionId &amp;&#35;61&#59; 6492&amp;&#35;59&#59; String actionType &amp;&#35;61&#59; &amp;quot&#59;CreateHousehold&amp;quot&#59;&amp;&#35;59&#59; String householdType &amp;&#35;61&#59; &amp;quot&#59;Multi&amp;quot&#59;&amp;&#35;59&#59; int deviceCount &amp;&#35;61&#59; 10&amp;&#35;59&#59;
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:
logger.info( new HouseHouseholdTransaction( 6492,&amp;quot&#59;CreateHousehold&amp;quot&#59;, &amp;quot&#59;Multi&amp;quot&#59;, 10),&amp;quot&#59;&amp;quot&#59;)&amp;&#35;59&#59;
A configuration/resource file could define a pattern for the human readable log file, something like:
messagetype.HouseHouseholdTransaction.format &amp;&#35;61&#59; &amp;quot&#59;Transaction $transactionId of type $actionType ( $householdType ) was received for creation of $deviceCount $devices&amp;quot&#59;
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.
The current implementation is based on the popular http://www.slf4j.org/ slf4j 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.
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.).
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.
INFO: Note |
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.
WARNING: Warning |
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&amp;&#35;123&#59;sourceId&amp;&#35;125&#59; and %u&amp;&#35;123&#59;sourceType&amp;&#35;125&#59;
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.
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.
WARNING: Warning |
Marker Example
@ConditionalFormats(&amp;&#35;123&#59; @ConditionalFormat( format&amp;&#35;61&#59;TransactionMarker.COND_FORMAT_1, criteria&amp;&#35;61&#59;&amp;&#35;123&#59; @FieldCriterion(name&amp;&#35;61&#59;&amp;quot&#59;sourceId&amp;quot&#59;, value&amp;&#35;61&#59;&amp;quot&#59;sourceId1&amp;quot&#59;), @FieldCriterion(name&amp;&#35;61&#59;&amp;quot&#59;sourceType&amp;quot&#59;, value&amp;&#35;61&#59;&amp;quot&#59;sourceType1&amp;quot&#59;)&amp;&#35;125&#59;), @ConditionalFormat( format&amp;&#35;61&#59;TransactionMarker.COND_FORMAT_2, criteria&amp;&#35;61&#59;&amp;&#35;123&#59; @FieldCriterion(name&amp;&#35;61&#59;&amp;quot&#59;sourceId&amp;quot&#59;, value&amp;&#35;61&#59;&amp;quot&#59;sourceId2&amp;quot&#59;), @FieldCriterion(name&amp;&#35;61&#59;&amp;quot&#59;sourceType&amp;quot&#59;, value&amp;&#35;61&#59;&amp;quot&#59;sourceType2&amp;quot&#59;) &amp;&#35;125&#59; ), @ConditionalFormat( format&amp;&#35;61&#59;TransactionMarker.COND_FORMAT_3, criteria&amp;&#35;61&#59;&amp;&#35;123&#59; @FieldCriterion(name&amp;&#35;61&#59;&amp;quot&#59;sourceId&amp;quot&#59;, value&amp;&#35;61&#59;&amp;quot&#59;sourceId3&amp;quot&#59;), @FieldCriterion(name&amp;&#35;61&#59;&amp;quot&#59;sourceType&amp;quot&#59;, value&amp;&#35;61&#59;&amp;quot&#59;sourceType3&amp;quot&#59;) &amp;&#35;125&#59; ) &amp;&#35;125&#59;) public class TransactionMarker extends BaseTransactionLoggingMarker &amp;&#35;123&#59; Logger LOGGER &amp;&#35;61&#59; LoggerFactory.getLogger(TransactionMarker.class)&amp;&#35;59&#59; private static final long serialVersionUID &amp;&#35;61&#59; 9161271890930513129L&amp;&#35;59&#59; public static final String DEFAULT_FORMAT &amp;&#35;61&#59; &amp;quot&#59;my default uncoditioned format&amp;&#35;33&#59;&amp;&#35;33&#59;&amp;&#35;33&#59;&amp;quot&#59;&amp;&#35;59&#59; public static final String COND_FORMAT_1 &amp;&#35;61&#59; &amp;quot&#59;my human readbale format 1&amp;quot&#59;&amp;&#35;59&#59; public static final String COND_FORMAT_2 &amp;&#35;61&#59; &amp;quot&#59;my human readbale format 2&amp;quot&#59;&amp;&#35;59&#59; public static final String COND_FORMAT_3 &amp;&#35;61&#59; &amp;quot&#59;my human readbale format 3&amp;quot&#59;&amp;&#35;59&#59; public TransactionMarker(String sessionId) &amp;&#35;123&#59; this.sessionId &amp;&#35;61&#59; sessionId&amp;&#35;59&#59; &amp;&#35;125&#59; @UserField(suppressNull &amp;&#35;61&#59; true) private FoundationLoggingEventType eventType&amp;&#35;59&#59; @UserField private FoundationLoggingTransMsgType transMsgType&amp;&#35;59&#59; @UserField private String sessionId&amp;&#35;59&#59; @UserField private String sourceId&amp;&#35;59&#59; @UserField private String sourceType&amp;&#35;59&#59; @UserField private String destinationId&amp;&#35;59&#59; @UserField private String destinationType&amp;&#35;59&#59; @UserField private String messagePayload&amp;&#35;59&#59; public TransactionMarker setEventType(FoundationLoggingEventType eventType) &amp;&#35;123&#59; this.eventType &amp;&#35;61&#59; eventType&amp;&#35;59&#59; return this&amp;&#35;59&#59; &amp;&#35;125&#59; public TransactionMarker setTransMsgType(FoundationLoggingTransMsgType transMsgType) &amp;&#35;123&#59; this.transMsgType &amp;&#35;61&#59; transMsgType&amp;&#35;59&#59; return this&amp;&#35;59&#59; &amp;&#35;125&#59; public TransactionMarker setSessionId(String sessionId) &amp;&#35;123&#59; this.sessionId &amp;&#35;61&#59; sessionId&amp;&#35;59&#59; return this&amp;&#35;59&#59; &amp;&#35;125&#59; public TransactionMarker setSourceId(String sourceId) &amp;&#35;123&#59; this.sourceId &amp;&#35;61&#59; sourceId&amp;&#35;59&#59; return this&amp;&#35;59&#59; &amp;&#35;125&#59; public TransactionMarker setSourceType(String sourceType) &amp;&#35;123&#59; this.sourceType &amp;&#35;61&#59; sourceType&amp;&#35;59&#59; return this&amp;&#35;59&#59; &amp;&#35;125&#59; public TransactionMarker setDestinationId(String destinationId) &amp;&#35;123&#59; this.destinationId &amp;&#35;61&#59; destinationId&amp;&#35;59&#59; return this&amp;&#35;59&#59; &amp;&#35;125&#59; public TransactionMarker setDestinationType(String destinationType) &amp;&#35;123&#59; this.destinationType &amp;&#35;61&#59; destinationType&amp;&#35;59&#59; return this&amp;&#35;59&#59; &amp;&#35;125&#59; public TransactionMarker setMessagePayload(String messagePayload) &amp;&#35;123&#59; this.messagePayload &amp;&#35;61&#59; messagePayload&amp;&#35;59&#59; return this&amp;&#35;59&#59; &amp;&#35;125&#59; public static long getSerialversionuid() &amp;&#35;123&#59; return serialVersionUID&amp;&#35;59&#59; &amp;&#35;125&#59; public static String getDefaultFormat() &amp;&#35;123&#59; return DEFAULT_FORMAT&amp;&#35;59&#59; &amp;&#35;125&#59; public static String getCondFormat1() &amp;&#35;123&#59; return COND_FORMAT_1&amp;&#35;59&#59; &amp;&#35;125&#59; public static String getCondFormat2() &amp;&#35;123&#59; return COND_FORMAT_2&amp;&#35;59&#59; &amp;&#35;125&#59; public static String getCondFormat3() &amp;&#35;123&#59; return COND_FORMAT_3&amp;&#35;59&#59; &amp;&#35;125&#59; public FoundationLoggingEventType getEventType() &amp;&#35;123&#59; return eventType&amp;&#35;59&#59; &amp;&#35;125&#59; public FoundationLoggingTransMsgType getTransMsgType() &amp;&#35;123&#59; return transMsgType&amp;&#35;59&#59; &amp;&#35;125&#59; public String getSessionId() &amp;&#35;123&#59; return sessionId&amp;&#35;59&#59; &amp;&#35;125&#59; public String getSourceId() &amp;&#35;123&#59; return sourceId&amp;&#35;59&#59; &amp;&#35;125&#59; public String getSourceType() &amp;&#35;123&#59; return sourceType&amp;&#35;59&#59; &amp;&#35;125&#59; public String getDestinationId() &amp;&#35;123&#59; return destinationId&amp;&#35;59&#59; &amp;&#35;125&#59; public String getDestinationType() &amp;&#35;123&#59; return destinationType&amp;&#35;59&#59; &amp;&#35;125&#59; public String getMessagePayload() &amp;&#35;123&#59; return messagePayload&amp;&#35;59&#59; &amp;&#35;125&#59; &amp;&#35;125&#59;
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
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.
&amp;lt&#59;appender id&amp;&#35;61&#59;&amp;quot&#59;anotherfile&amp;quot&#59; defaultFormat&amp;&#35;61&#59;&amp;quot&#59;My default format for %u&amp;&#35;123&#59;sourceId&amp;&#35;125&#59; and %u&amp;&#35;123&#59;sourceType&amp;&#35;125&#59; and %u&amp;&#35;123&#59;base&amp;&#35;125&#59;&amp;quot&#59;&amp;gt&#59; &amp;lt&#59;criteria&amp;gt&#59; &amp;lt&#59;criterion format&amp;&#35;61&#59;&amp;quot&#59;my conditional format 1 from xml &amp;&#35;45&#59; something else&amp;quot&#59;&amp;gt&#59; &amp;lt&#59;field name&amp;&#35;61&#59;&amp;quot&#59;sourceId&amp;quot&#59; equals&amp;&#35;61&#59;&amp;quot&#59;sourceId1&amp;quot&#59; /&amp;gt&#59; &amp;lt&#59;field name&amp;&#35;61&#59;&amp;quot&#59;sourceType&amp;quot&#59; equals&amp;&#35;61&#59;&amp;quot&#59;sourceType1&amp;quot&#59; /&amp;gt&#59; &amp;lt&#59;/criterion&amp;gt&#59; &amp;lt&#59;/criteria&amp;gt&#59; &amp;lt&#59;/appender&amp;gt&#59; &amp;lt&#59;appender id&amp;&#35;61&#59;&amp;quot&#59;biFile&amp;quot&#59; defaultFormat&amp;&#35;61&#59;&amp;quot&#59;My default format for %u&amp;&#35;123&#59;sourceId&amp;&#35;125&#59; and %u&amp;&#35;123&#59;sourceType&amp;&#35;125&#59;&amp;quot&#59; /&amp;gt&#59; &amp;lt&#59;/marker&amp;gt&#59; &amp;lt&#59;marker id&amp;&#35;61&#59;&amp;quot&#59;TransactionMarker3&amp;quot&#59;&amp;gt&#59; &amp;lt&#59;defaultAppender defaultFormat&amp;&#35;61&#59;&amp;quot&#59;My default format for %u&amp;&#35;123&#59;sourceId&amp;&#35;125&#59; and %u&amp;&#35;123&#59;sourceType&amp;&#35;125&#59;&amp;quot&#59; /&amp;gt&#59; &amp;lt&#59;appender id&amp;&#35;61&#59;&amp;quot&#59;logfile&amp;quot&#59; defaultFormat&amp;&#35;61&#59;&amp;quot&#59;My default format for %u&amp;&#35;123&#59;sourceId&amp;&#35;125&#59; and %u&amp;&#35;123&#59;sourceType&amp;&#35;125&#59;&amp;quot&#59;&amp;gt&#59; &amp;lt&#59;criteria&amp;gt&#59; &amp;lt&#59;criterion format&amp;&#35;61&#59;&amp;quot&#59; my conditional format 1 from xml &amp;quot&#59;&amp;gt&#59; &amp;lt&#59;field name&amp;&#35;61&#59;&amp;quot&#59;sourceId&amp;quot&#59; equals&amp;&#35;61&#59;&amp;quot&#59;sourceId1&amp;quot&#59; /&amp;gt&#59; &amp;lt&#59;field name&amp;&#35;61&#59;&amp;quot&#59;sourceType&amp;quot&#59; equals&amp;&#35;61&#59;&amp;quot&#59;sourceType1&amp;quot&#59; /&amp;gt&#59; &amp;lt&#59;/criterion&amp;gt&#59; &amp;lt&#59;criterion format&amp;&#35;61&#59;&amp;quot&#59;my conditional format 2 from xml&amp;quot&#59;&amp;gt&#59; &amp;lt&#59;field name&amp;&#35;61&#59;&amp;quot&#59;sourceId&amp;quot&#59; equals&amp;&#35;61&#59;&amp;quot&#59;sourceId2&amp;quot&#59; /&amp;gt&#59; &amp;lt&#59;field name&amp;&#35;61&#59;&amp;quot&#59;sourceType&amp;quot&#59; equals&amp;&#35;61&#59;&amp;quot&#59;sourceType2&amp;quot&#59; /&amp;gt&#59; &amp;lt&#59;/criterion&amp;gt&#59; &amp;lt&#59;/criteria&amp;gt&#59; &amp;lt&#59;/appender&amp;gt&#59; &amp;lt&#59;appender id&amp;&#35;61&#59;&amp;quot&#59;anotherfile&amp;quot&#59; defaultFormat&amp;&#35;61&#59;&amp;quot&#59;My default format for %u&amp;&#35;123&#59;sourceId&amp;&#35;125&#59; and %u&amp;&#35;123&#59;sourceType&amp;&#35;125&#59;&amp;quot&#59;&amp;gt&#59; &amp;lt&#59;criteria&amp;gt&#59; &amp;lt&#59;criterion format&amp;&#35;61&#59;&amp;quot&#59;my conditional format 1 from xml&amp;quot&#59;&amp;gt&#59; &amp;lt&#59;field name&amp;&#35;61&#59;&amp;quot&#59;sourceId&amp;quot&#59; equals&amp;&#35;61&#59;&amp;quot&#59;sourceId1&amp;quot&#59; /&amp;gt&#59; &amp;lt&#59;field name&amp;&#35;61&#59;&amp;quot&#59;sourceType&amp;quot&#59; equals&amp;&#35;61&#59;&amp;quot&#59;sourceType1&amp;quot&#59; /&amp;gt&#59; &amp;lt&#59;/criterion&amp;gt&#59; &amp;lt&#59;/criteria&amp;gt&#59; &amp;lt&#59;/appender&amp;gt&#59; &amp;lt&#59;appender id&amp;&#35;61&#59;&amp;quot&#59;biFile&amp;quot&#59; defaultFormat&amp;&#35;61&#59;&amp;quot&#59;My default format for %u&amp;&#35;123&#59;sourceId&amp;&#35;125&#59; and %u&amp;&#35;123&#59;sourceType&amp;&#35;125&#59;&amp;quot&#59; /&amp;gt&#59; &amp;lt&#59;/marker&amp;gt&#59; &amp;lt&#59;marker id&amp;&#35;61&#59;&amp;quot&#59;com.cisco.oss.foundation.logging.structured.test.TransactionMarker2&amp;quot&#59;&amp;gt&#59; &amp;lt&#59;defaultAppender defaultFormat&amp;&#35;61&#59;&amp;quot&#59;My default format for %u&amp;&#35;123&#59;sourceId&amp;&#35;125&#59; and %u&amp;&#35;123&#59;sourceType&amp;&#35;125&#59;&amp;quot&#59; /&amp;gt&#59; &amp;lt&#59;appender id&amp;&#35;61&#59;&amp;quot&#59;logfile&amp;quot&#59; defaultFormat&amp;&#35;61&#59;&amp;quot&#59;My default format for %u&amp;&#35;123&#59;sourceId&amp;&#35;125&#59; and %u&amp;&#35;123&#59;sourceType&amp;&#35;125&#59;&amp;quot&#59;&amp;gt&#59; &amp;lt&#59;criteria&amp;gt&#59; &amp;lt&#59;criterion format&amp;&#35;61&#59;&amp;quot&#59; my conditional format 1 from xml &amp;quot&#59;&amp;gt&#59; &amp;lt&#59;field name&amp;&#35;61&#59;&amp;quot&#59;sourceId&amp;quot&#59; equals&amp;&#35;61&#59;&amp;quot&#59;sourceId1&amp;quot&#59; /&amp;gt&#59; &amp;lt&#59;field name&amp;&#35;61&#59;&amp;quot&#59;sourceType&amp;quot&#59; equals&amp;&#35;61&#59;&amp;quot&#59;sourceType1&amp;quot&#59; /&amp;gt&#59; &amp;lt&#59;/criterion&amp;gt&#59; &amp;lt&#59;criterion format&amp;&#35;61&#59;&amp;quot&#59;my conditional format 2 from xml&amp;quot&#59;&amp;gt&#59; &amp;lt&#59;field name&amp;&#35;61&#59;&amp;quot&#59;sourceId&amp;quot&#59; equals&amp;&#35;61&#59;&amp;quot&#59;sourceId2&amp;quot&#59; /&amp;gt&#59; &amp;lt&#59;field name&amp;&#35;61&#59;&amp;quot&#59;sourceType&amp;quot&#59; equals&amp;&#35;61&#59;&amp;quot&#59;sourceType2&amp;quot&#59; /&amp;gt&#59; &amp;lt&#59;/criterion&amp;gt&#59; &amp;lt&#59;/criteria&amp;gt&#59; &amp;lt&#59;/appender&amp;gt&#59; &amp;lt&#59;appender id&amp;&#35;61&#59;&amp;quot&#59;anotherfile&amp;quot&#59; defaultFormat&amp;&#35;61&#59;&amp;quot&#59;My default format for %u&amp;&#35;123&#59;sourceId&amp;&#35;125&#59; and %u&amp;&#35;123&#59;sourceType&amp;&#35;125&#59;&amp;quot&#59;&amp;gt&#59; &amp;lt&#59;criteria&amp;gt&#59; &amp;lt&#59;criterion format&amp;&#35;61&#59;&amp;quot&#59;my conditional format 1 from xml&amp;quot&#59;&amp;gt&#59; &amp;lt&#59;field name&amp;&#35;61&#59;&amp;quot&#59;sourceId&amp;quot&#59; equals&amp;&#35;61&#59;&amp;quot&#59;sourceId1&amp;quot&#59; /&amp;gt&#59; &amp;lt&#59;field name&amp;&#35;61&#59;&amp;quot&#59;sourceType&amp;quot&#59; equals&amp;&#35;61&#59;&amp;quot&#59;sourceType1&amp;quot&#59; /&amp;gt&#59; &amp;lt&#59;/criterion&amp;gt&#59; &amp;lt&#59;/criteria&amp;gt&#59; &amp;lt&#59;/appender&amp;gt&#59; &amp;lt&#59;appender id&amp;&#35;61&#59;&amp;quot&#59;biFile&amp;quot&#59; defaultFormat&amp;&#35;61&#59;&amp;quot&#59;My default format for %u&amp;&#35;123&#59;sourceId&amp;&#35;125&#59; and %u&amp;&#35;123&#59;sourceType&amp;&#35;125&#59;&amp;quot&#59; /&amp;gt&#59; &amp;lt&#59;/marker&amp;gt&#59; &amp;lt&#59;/markers&amp;gt&#59;
The above guideline has omitted the log/audit guideline you may remember from Ex-NDS Wiki as it is currently under review.
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.