Sample Application Example
A banking company is required to keep separate logs of all foreign transactions and of all foreign transactions greater than $10,000.
The foreign transactions to be logged are in the Smalltalk class Transaction. An instance of the class answers a string when sent the message #payee and a monetary amount when sent to message # amount.
C:\Users\documentation\Documents\vastePublisher\stable\VAS Documentation Word\images\pr\log4s_2ex.png
Transaction has a method Transaction>> printOn
printOn: aStream
 
super printOn: aStream.
aStream nextPut: $(.
self payee printOn: aStream.
aStream nextPut: $;.
self amount printOn: aStream.
aStream nextPut: $).
And a method Transaction>>printLog4s
printLog4s
 
| stream |
stream := WriteStream on: ''.
stream
nextPutAll: self payee; space;
nextPutAll: self amount printString.
^stream contents
 
Method 1
One way to achieve this logging is to use two loggers, each with its own file appender set to record all transactions or only transactions above the $10,000 threshhold.
The figure below shows the structure of the log4s framework. One logger is the rootLogger; the second is a custom logger named ‘vast.’
C:\Users\documentation\Documents\vastePublisher\stable\VAS Documentation Word\images\pr\log4s_2ex_m1.png
The first step is to create a file appender named ForeignTxns with a level of Info and add it to the rootLogger: The pattern '%d %p [%c] %o' will log the time, the level, the logger name, and the transaction object. The message portion of the log event will be discarded.
In .ini file under the [log4s] stanza,
fileAppender=ForeignTxns, root, foreignTxns.log, false, Info, EsPatternLayout, '%d %p [%c] %o', true
Equivalent Smalltalk code
| fileAppender1 |
 
fileAppender1 :=
EsFileAppender
name: 'ForeignTxns'
layout: (EsPatternLayout new: '%d %p [%c] %o')
threshold: EsLevel Info
fileName: 'foreignTxns.log'
fileAppend: false
immediateFlush: true.
EsLogManager rootLogger
addAppender: fileAppender1 deepCopy.
 
The next step is to create a file appender named BigForeignTxns with a level of Warn and add it to a newly created logger called 'vast': The pattern of '%d %p [%c] %o' will log the time, the level, the Logger name, and the transaction object. The message portion of the log event will be discarded.
In .ini file under the [log4s] stanza,
createLogger=vast
fileAppender=BigForeignTxns, vast, bigForeignTxns.log, false, Warn, EsPatternLayout, '%d %p [%c] %o', true
 
Equivalent Smalltalk code
| fileAppender2 loggerName |
 
loggerName := 'vast'.
EsLogManager createLogger: loggerName.
fileAppender2 := EsFileAppender
name: 'BigForeignTxns'
layout: (EsPatternLayout new: '%d %p [%c] %o')
threshold: EsLevel Warn
fileName: 'bigForeignTxns.log'
fileAppend: false
immediateFlush: true.
(EsLogManager loggerNamed: loggerName )
addAppender: fileAppender2.
 
The actual application code might look like this method which decides whether the transaction presented should appear in one of the log files:
checkIfLoggingNeeded: aTransaction
 
aTransaction isForeign ifTrue:
[EsLogManager
logInfo: 'Foreign txn'
object: aTransaction. "Goes to the rootLogger"
aTransaction amount > 10000 ifTrue:
[(EsLogManager loggerNamed: 'vast')
warn: 'Large Foreign txn'
object: aTransaction]" Goes to the vast Logger"].
 
In the above example, all transactions result in an Info-level logging event. Only transactions with amounts greater than 10,000 result in a Warn-level logging event. The Info-level 'Foreign txn' logging event goes to the rootLogger and from there through the associated appender to the foreignTxns.log. The same logging event also goes to the vast logger, but this logger does not pass the logging event on to be written in the log file ‘foreignTxns.log.’
The Warn-level 'Large Foreign txn' logging event goes to the vast logger and from there through the associated appender to the bigForeignTxns.log. The same logging event also goes to the rootLogger, but this logger does not pass the logging event on to be written in the log file ‘bigForeignTxns.log.’
The output has three parts: the date, the logger name and the transaction. The transition is rendered as a payee and amount because of the method Transaction>>printLog4s. If the Transaction had no such method, the #printString method would record the transaction. If Transaction does not override #printOn:, it will record the transaction as 'aTransaction'
foreignTxns.log will have output like this.
'28 Aug 2011 08:15:07,000 INFO [root] Mark Twain 13.67'
'28 Aug 2011 08:15:07,050 INFO [root] Fred Smith 23645.23'
 
bigForeignTxns.log will have output like this:
'28 Aug 2011 08:15:07,050 WARN [vast] Fred Smith 23645.23'
 
Method 2
The same output can be achieved using just the rootLogger with two appenders. One of the appenders will have a level match filter. This configuration directs all warning log events, which are sent when a transaction amount exceeds $10,000, to one file and all informational level log events to another.
The figure below shows the structure of the log4s framework. The ForeignTxns appender has the filter and is on the right hand of the figure below. The BigForeignTxns appender is on the left side of the figure.
C:\Users\documentation\Documents\vastePublisher\stable\VAS Documentation Word\images\pr\log4s_2ex_m2.png
Just as for Method 1, the first step is to create a file appender named ForeignTxns with a level of Info and add it to the rootLogger. However for Method 2, this file appender has a level match filter to reject logging events of level Warn. Without the filter, the appender allows all logging events at level Info or greater to be written to the appender’s log file. Adding the filter will cause Warning-level logging events not to be output.
In the .ini file under [log4s],
fileAppender=ForeignTxns, root, foreignTxns.log, false, Info, EsPatternLayout, '%d %p [%c] %o', true
levelMatchFilter=root, ForeignTxns, LevelMatchFilter, Warn, false
 
Equivalent Smalltalk code (alternate 1)
| levelMatchFilter fileAppender1 |
EsLogManager reset. "Needed if not starting from a virgin image"
 
"Create the levelMatchFilter first"
levelMatchFilter := EsLevelMatchFilter
name: 'aLevelMatchFilter'
levelToMatch: EsLevel Warn
acceptOnMatch: false. "Don’t print the message if level = Warn "
 
fileAppender1 :=
EsFileAppender
name: 'ForeignTxns'
layout: (EsPatternLayout new: '%d %p [%c] %o')
threshold: EsLevel Info
fileName: 'foreignTxns.log'
fileAppend: false
immediateFlush: true.
 
fileAppender1 addFilter: levelMatchFilter.
 
EsLogManager rootLogger
addAppender: fileAppender1 deepCopy.
Equivalent Smalltalk code (alternate 2)
| levelMatchFilter fileAppender1 |
EsLogManager reset. “Needed if not starting from a virgin image”
 
fileAppender1 :=
EsFileAppender
name: 'ForeignTxns'
layout: (EsPatternLayout new: '%d %p [%c] %o')
threshold: EsLevel Info
fileName: 'foreignTxns.log'
fileAppend: false
immediateFlush: true.
 
EsLogManager rootLogger
addAppender: fileAppender1 deepCopy.
 
"Create the levelMatchFilter and
associate it with the file appender named 'ForeignTxns' "
EsLogManager singleton levelMatchFilter:
#('root' 'ForeignTxns' 'aLevelMatchFilter' 'Warn' false).
"Don’t print the message if level = Warn”
 
Next, create a file appender named BigForeignTxns with a level of Info, but for Method 2, add it to the rootLogger rather than to a custom logger as in Method 1:
In .ini file under the [log4s] stanza,
fileAppender=BigForeignTxns, root, bigForeignTxns.log, false, Warn, EsPatternLayout, '%d %p [%c] %o', true
Equivalent Smalltalk code
| fileAppender2 |
 
fileAppender2 := EsFileAppender
name: 'BigForeignTxns'
layout: (EsPatternLayout new: '%d %p [%c] %o')
threshold: EsLevel Warn
fileName: 'bigForeignTxns.log'
fileAppend: false
immediateFlush: true.
EsLogManager rootLogger addAppender: fileAppender2 deepCopy.
 
The Smalltalk application code might look like this method which decides whether the transaction presented should appear in one of the log files: The code is the same as for Method1, but when executed with Method 2’s arrangement of log4s players, the logging events go to the same logger. It is the appenders that decide the destination of the logging events.
checkIfLoggingNeeded: aTransaction
 
aTransaction isForeign ifTrue:
[EsLogManager
logInfo: 'Foreign txn'
object: aTransaction. "Goes to the rootLogger"
aTransaction amount > 10000 ifTrue:
[EsLogManager
logWarn: 'Large Foreign txn'
object: aTransaction] "Goes to the rootLogger"].
In the above example, all transactions result in an Info-level logging event. Only transactions with amounts greater than 10,000 result in a Warn-level logging event. The Info-level 'Foreign txn' and Warn-level 'Large Foreign txn' logging events go through the rootLogger to both appenders. The Info-level logging events are output to the foreignTxns.log while the Warn-level logging events are output to the bigForeignTxns.log. How does this happen?
The ForeignTxns appender passes the Info-level logging event on to the levelMatchFilter which allows non-warning events to pass through to the log file for the appender, foreignTxns.log.
The ForeignTxns appender would normally log the Warn-level 'Large Foreign txn' logging event because its level allows logging events of Info or higher to be written, and Warn is higher. But the levelMatchFilter attached to the ForeignTxns appender ignores the logging event because it filters out logging events with a level of Warn. So the Warn-level 'Large Foreign txn' logging event does not appear in foreignTxns.log.
The BigForeignTxns appender ignores the Info-level logging event because the Info level of the event does not rise to the Warn-level required by the appender. So the Info-level 'Foreign txn' logging event does not appear in the bigForeignTxns.log.
The BigForeignTxns appender allows the Warn-level 'Large Foreign txn' logging event to be written to bigForeignTxns.log because the event level is greater or equal to the appender’s level.
Each output has three parts: the date, the logger name and the transaction. The transaction is rendered as a payee and amount because of the method Transaction>>printLog4s. Note that if there were no such method defined for class Transaction, then the method #printString would be called and the output would be 'aTransaction' unless Transaction overrides #printOn.
foreignTxns.log will have output like this:
'28 Aug 2011 08:15:07,000 INFO [root] Mark Twain 13.67'
'28 Aug 2011 08:15:07,035 INFO [root] Fred Smith 23645.23'
bigForeignTxns.log will have output like this:
'28 Aug 2011 08:15:07,035 WARN [root] Fred Smith 23645.23'
More Examples
The configuration map ENVY/Image Logging Examples contains application EsLoggingFrameworkExamples which has many sample scripts that demonstrate the log4s functionality. See EsLogManager class>>readMe to get started.
EsSocketAppender>>readMe has a script you can use to start a socket listener.
Last modified date: 12/19/2017