Redirecting Log Entries
This section describes how to create appenders in code. Log4s comes with several varieties of EsAppender named based on the destination to which the appender directs the EsLoggingEvent. This section discusses EsAppenders which direct logging events to
•Transcript,
•Console,
•File and
•Socket.
In the case of console, file and sockets, an appender can direct logging events synchronously or asynchronously. When an asynchronous appender is added to a logger, two appenders are added: an EsAsyncAppender to control the asynchronous behavior and another EsAppender to control where the output goes The EsAsyncAppender has a queue and a background task that writes the log entries. The figure below graphically describes the situation.
An EsFileAppender directs logging events to a generic log file. However, a EsRollingFileAppender and a EsDailyRollingFileAppender direct logging events to several log files named in such a way that identify the log files as belonging together.
transcriptAppender
The transcriptAppender is named 'Transcript'. There is at most one instance of EsTranscriptAppender; creating a second one will not override the first.
For example,
"Execute this code in a workspace.
Create a transcriptAppender.
Add the appender to the root logger.
Test the logging."
| transcriptAppender |
EsLogManager reset. "Clean up logging setup for the example."
transcriptAppender := EsTranscriptAppender
level: EsLevel Warn
layout: (EsPatternLayout new: '%d %m').
EsLogManager rootLogger addAppender: transcriptAppender deepCopy.
EsLogManager logWarn: ' Hi Mom'.
EsLogManager shutDown. "Discard any log4s state."
The above example will result in a line in the transcript for the 'warning' message. The transcript will look something like this. The first part of the line is the date and time including the milliseconds; the second part is the warning message itself.
2017-06-07 11:57:57,028 Hi Mom
consoleAppender
The consoleAppender is named 'Console'. There is at most one instance of EsConsoleAppender; creating a second one will not override the first. By default, it logs synchronously.
On Windows, to see the console, start the image with -l con in the command line. Without this command line option, console output is not visible.
For example,
"Execute this code in a workspace.
Create a consoleAppender.
Add the appender to the root logger.
Test the logging."
| consoleAppender |
EsLogManager reset. "Clean up logging setup for the example."
consoleAppender := EsConsoleAppender
level: EsLevel Warn
layout: (EsPatternLayout new: '%d %m').
EsLogManager rootLogger addAppender: consoleAppender deepCopy.
EsLogManager logWarn: ' Hi Mom'.
EsLogManager shutDown. "Discard any log4s state."
The above example will result in a line on the console for the 'warning' message. The console will look something like this. The first part of the line is the date and time including the milliseconds; the second part is the warning message itself.
2017-06-07 11:57:57,028 Hi Mom
asyncConsoleAppender
The asynchronous console appender is similar to the consoleAppender. Since there can only be one console appender, any existing synchronous console appender must be removed before creating an asynchronous console appender. The example below resets the EsLogManager to clear out any existing appender then creates two new appenders, one to handle the asynchronous nature of the logging; the other to handle the direction of logging events to the console.
On Windows, to see the console, start the image with -l con in the command line. Without this command line option, console output is not visible.
For example,
"Execute this code in a workspace.
Create an asyncAppender.
Add the asyncAppender to the root logger.
Create a consoleAppender.
Add the consoleAppender to the asyncAppender.
Test the logging."
| consoleAppender asyncAppender asyncAppenderName |
EsLogManager reset. "Clean up logging setup for the example."
asyncAppenderName := 'anAsyncAppender'.
asyncAppender := EsAsyncAppender name: asyncAppenderName.
EsLogManager rootLogger addAppender: asyncAppender.
consoleAppender := EsConsoleAppender
level: EsLevel Warn
layout: (EsPatternLayout new: '%d %m').
(EsLogManager rootLogger getAppender: asyncAppenderName)
addAppender: consoleAppender.
25 timesRepeat: [
EsLogManager logWarn: 'Hi Mom'.
(Delay forMilliseconds: 500) wait.].
EsLogManager shutDown. "Discard any log4s state."
The above example will result in 25 lines on the console for the 'warning' message. Each line in the console will look something like this. The first part of the line is the date and time including the milliseconds; the second part is the warning message itself.
2017-06-11 13:58:57,063 Hi Mom
fileAppender
An EsFileAppender directs logging events to a named file. One or more file appenders in addition to or instead of the singleton EsTranscriptAppender / EsConsoleAppender instance may be used.
In the example below, the file directory must exist; log4s creates the file, but not the directory.
For example,
"Execute this code in a workspace.
Create a fileAppender.
Add the fileAppender to the root logger.
Test the logging."
| fileAppender |
EsLogManager reset. "Clean up logging setup for the example."
fileAppender := EsFileAppender
name: 'aFileAppender' "someCaseSensitiveAppenderName"
layout: (EsPatternLayout new: '%d %m')
threshold: EsLevel Warn
fileName: 'c:\temp\fileAppender.txt'
fileAppend: true
immediateFlush: true.
EsLogManager rootLogger addAppender: fileAppender deepCopy.
5 timesRepeat: [EsLogManager logWarn: 'Hi Mom'.
(Delay forMilliseconds: 500) wait].
EsLogManager shutDown. "Discard any log4s state."
The above example will result in 5 lines written to the file c:\temp\fileAppender.txt. Its contents will look something like this. The first part of the line is the date and time including the milliseconds; the second part is the warning message itself.
2017-06-07 14:41:34,553 Hi Mom
2017-06-07 14:41:35,138 Hi Mom
2017-06-07 14:41:35,738 Hi Mom
2017-06-07 14:41:36,338 Hi Mom
2017-06-07 14:41:36,938 Hi Mom
asyncFileAppender
The asynchronous file appender is similar to the fileAppender. The example below creates two new appenders, one to handle the asynchronous nature of the logging; the other to handle the direction of logging events to the file.
In the example below, the file directory must exist; log4s creates the file, but not the directory.
For example,
"Execute this code in a workspace.
Create an asyncAppender.
Add the asyncAppender to the root logger.
Create a fileAppender.
Add the fileAppender to the asyncAppender.
Test the logging."
| fileAppender asyncAppender asyncAppenderName |
EsLogManager reset. "Clean up logging setup for the example."
asyncAppenderName := 'anAsyncAppender'.
asyncAppender := EsAsyncAppender name: asyncAppenderName.
EsLogManager rootLogger addAppender: asyncAppender.
fileAppender := EsFileAppender
name: 'aFileAppender' "someCaseSensitiveAppenderName"
layout: (EsPatternLayout new: '%d %m')
threshold: EsLevel Warn
fileName: 'c:\temp\asyncFileAppender.txt'
fileAppend: true
immediateFlush: true.
(EsLogManager rootLogger getAppender: asyncAppenderName)
addAppender: fileAppender.
25 timesRepeat: [
EsLogManager logWarn: 'Hi Mom'.
(Delay forMilliseconds: 500) wait].
EsLogManager shutDown. "Discard any log4s state."
The above example will write 25 lines to the file c:\temp\asyncFileAppender.txt. Each line in the file contents will look something like this. The first part of the line is the date and time including the milliseconds; the second part is the warning message itself.
2017-06-13 15:50:18,226 Hi Mom
rollingFileAppender
An EsRollingFileAppender will write initially to the file specified in the fileName value. The EsRollingFileAppender will check its size after each time it logs a logging event. If the new size exceeds the maxFileSize value, the EsRollingFileAppender will then close that file, and open a new one with an integer appended to the original file name. It will repeat this process maxBackupIndex number of times, and then start overwriting the previously written files starting with the oldest one.
In the example below, the file directory must exist; log4s creates the file, but not the directory.
For example,
"Execute this code in a workspace.
Create a rollingFileAppender.
Add the rollingFileAppender to the root logger.
Test the logging."
| rollingFileAppender |
EsLogManager reset. "Clean up logging setup for the example."
rollingFileAppender := EsRollingFileAppender
name: 'aRollingFileAppender' "someCaseSensitiveAppenderName"
layout: (EsPatternLayout new: '%d %m')
threshold: EsLevel Warn
fileName: 'c:\temp\rollingFileAppender.txt'
fileAppend: true
immediateFlush: true
maxFileSize: 100 "bytes"
maxBackupIndex: 2.
EsLogManager rootLogger addAppender: rollingFileAppender deepCopy.
15 timesRepeat: [
EsLogManager logWarn: 'Four score and seven years ago'.
(Delay forMilliseconds: 500) wait].
EsLogManager shutDown. "Discard any log4s state."
The above example will result in 15 lines written to 3 files: c:\temp\rollingFileAppender.txt, c:\temp\rollingFileAppender.txt1, and c:\temp\rollingFileAppender.txt2. Each line in the file contents will look something like this. The first part of the line is the date and time including the milliseconds; the second part is the warning message itself.
2017-06-07 17:08:07,099 Four score and seven years ago
In the example, not all 15 lines are visible in the files since 100 bytes are allowed in each file, but the output for each logging event takes about 50 bytes. So the rolling file appender overwrites files which were previously written to during the logging process Increasing the maximum file size to 300 would preserve all log output.
asyncRollingFileAppender
The asynchronous rolling file appender is similar to the rollingFileAppender. The example below creates two new appenders, one to handle the asynchronous nature of the logging; the other to handle the direction of logging events to the set of files.
In the example below, the file directory must exist; log4s creates the file, but not the directory.
For example,
"Execute this code in a workspace.
Create an asyncAppender.
Add the asyncAppender to the root logger.
Create a rollingFileAppender.
Add the rollingFileAppender to the asyncAppender.
Test the logging."
| rollingFileAppender asyncAppenderName asyncAppender |
EsLogManager reset. "Clean up logging setup for the example."
asyncAppenderName := 'anAsyncAppender'.
asyncAppender := EsAsyncAppender name: asyncAppenderName.
EsLogManager rootLogger addAppender: asyncAppender.
rollingFileAppender := EsRollingFileAppender
name: 'aRollingFileAppender' "someCaseSensitiveAppenderName"
layout: (EsPatternLayout new: '%d %m')
threshold: EsLevel Warn
fileName: 'c:\temp\asyncRollingFileAppender.txt'
fileAppend: true
immediateFlush: true
maxFileSize: 100 "bytes"
maxBackupIndex: 2.
(EsLogManager rootLogger getAppender: asyncAppenderName)
addAppender: rollingFileAppender.
15 timesRepeat: [
EsLogManager logWarn: 'Four score and seven years ago'.
(Delay forMilliseconds: 500) wait ].
EsLogManager shutDown. "Discard any log4s state."
The above example will result in 15 lines written to 3 files: c:\temp\asyncRollingFileAppender.txt, c:\temp\ asyncRollingFileAppender.txt1, and c:\temp\ asyncRollingFileAppender.txt2. Each line in the file contents will look something like this. The first part of the line is the date and time including the milliseconds; the second part is the warning message itself.
2017-06-13 16:34:15,454 Four score and seven years ago
In the example, not all 15 lines are visible in the files since 100 bytes are allowed in each file, but the output for each logging event takes about 50 bytes. So the rolling file appender overwrites files which were previously written to during the logging process Increasing the maximum file size to 300 would preserve all log output.
dailyRollingFileAppender
An EsDailyRollingFileAppender will write initially to the file specified in the fileName value, e.g. dailyRollLog4s.log. When it starts, the EsDailyRollingFileAppender will calculate the time and date when it should rollover or start a new file. This calculation is done based on what is specified in the whenToRoll value. The name of the new file is the original fileName augmented by a suffix as defined in the table below.
| | |
topOfMinute | every minute | dailyRollLog4s.log.2011-08-17-11-17 |
topOfHour | every hour | dailyRollLog4s.log.2011-08-17-12 |
halfDay | midnight and noon | dailyRollLog4s.log.2011-08-17-pm |
topOfDay | every midnight | dailyRollLog4s.log.2011-08-18 |
topOfWeek | every Sunday 00:00:00,001 | dailyRollLog4s.log.2011-Sun |
topOfMonth | 00:00:00,001 the first of each month | dailyRollLog4s.log.2011-09 |
For example this code creates and runs a topOfMinute appender.
"Execute this code in a workspace; it will run for about 75 seconds.
Create a dailyRollingFileAppender.
Add the dailyRollingFileAppender to the root logger.
Test the logging."
| dailyRollingFileAppender |
EsLogManager reset. "Clean up logging setup for the example."
dailyRollingFileAppender := EsDailyRollingFileAppender
name: 'aDailyRollingFileAppender' "someCaseSensitiveAppenderName"
layout: (EsPatternLayout new: '%d %m')
threshold: EsLevel Warn
fileName: 'c:\temp\dailyRollingFileAppender.txt'
fileAppend: true
immediateFlush: true
datePattern: 'topOfMinute'.
EsLogManager rootLogger
addAppender: dailyRollingFileAppender deepCopy.
EsLogManager initializeRolloverTimes.
75 timesRepeat: [
EsLogManager logWarn: 'Four score and seven years ago'.
(Delay forMilliseconds: 1000) wait ].
EsLogManager shutDown. "Discard any log4s state."
In the example above, the file directory must exist; log4s creates the file, but not the directory.
The above example will result in 75 lines written to 2 files: c:\temp\dailyRollingFileAppender.txt, and c:\temp\ dailyRollingFileAppender.txt.2017-06-07-17-29 (or the date the file is created). Each line in the file contents will look something like this. The first part of the line is the date and time including the milliseconds; the second part is the warning message itself.
2017-06-07 17:28:17,134 Four score and seven years ago
When examining the contents of the files latest entries are always in dailyRollingFileAppender.txt. In this example, as each minute ends, dailyRollingFileAppender.txt is closed and renamed with the appropriate date as a suffix. A new dailyRollingFileAppender.txt file is created and entries written to it for the next (fraction of a) minute.
In some cases, the timing may be right to generate three files rather than two. In this case, the 75 lines are divided between the three files.
asyncDailyRollingFileAppender
The asynchronous daily rolling file appender is similar to the dailyRollingFileAppender. The example below creates two new appenders, one to handle the asynchronous nature of the logging; the other to handle the direction of logging events to the set of files.
For example this code creates and runs a topOfMinute appender.
"Execute this code in a workspace; it will run for about 75 seconds.
Create an asyncAppender.
Add the asyncAppender to the root logger.
Create a dailyRollingFileAppender.
Add the dailyRollingFileAppender to the root logger.
Test the logging."
| dailyRollingFileAppender asyncAppenderName asyncAppender |
EsLogManager reset. "Clean up logging setup for the example."
asyncAppenderName := 'anAsyncAppender'.
asyncAppender := EsAsyncAppender name: asyncAppenderName.
EsLogManager rootLogger addAppender: asyncAppender.
dailyRollingFileAppender := EsDailyRollingFileAppender
name: 'aRollingFileAppender' "someCaseSensitiveAppenderName"
layout: (EsPatternLayout new: '%d %m')
threshold: EsLevel Warn
fileName: 'c:\temp\asyncDailyRollingFileAppender.txt'
fileAppend: true
immediateFlush: true
datePattern: 'topOfMinute'.
(EsLogManager rootLogger getAppender: asyncAppenderName)
addAppender: dailyRollingFileAppender.
EsLogManager initializeRolloverTimes.
75 timesRepeat: [
EsLogManager logWarn: 'Four score and seven years ago'.
(Delay forMilliseconds: 1000) wait ].
EsLogManager shutDown. "Discard any log4s state."
In the example above, the file directory must exist; log4s creates the file, but not the directory.
The above example will result in 75 lines written to 2 files: c:\temp\asyncDailyRollingFileAppender.txt, and c:\temp\asyncDailyRollingFileAppender.txt.2017-06-13-16-43 (or the date the file is created). Each line in the file contents will look something like this. The first part of the line is the date and time including the milliseconds; the second part is the warning message itself.
2017- 06-13 16:44:00,880 Four score and seven years ago
When examining the contents of the files latest entries are always in dailyRollingFileAppender.txt. In this example, as each minute ends, dailyRollingFileAppender.txt is closed and renamed with the appropriate date as a suffix. A new dailyRollingFileAppender.txt file is created and entries written to it for the next (fraction of a) minute.
In some cases, the timing may be right to generate three files rather than two. In this case, the 75 lines are divided between the three files.
socketAppender
An EsSocketAppender directs logging events to a socket. In order for any useful logging to occur, the socket should have a listener running on it.
The EsSocketAppender should identify its destination by an IP address or a host name and a port. For example '127.0.0.1' or 'localhost.If the string specifies both, then the IP address will be used. See the example below.
If the listener is not running when the EsSocketAppender starts, the appender will time out. If the EsSocketAppender times out, it will try to connect retryCount number of times after waiting retrySeconds between attempts. If all attempts fail, no logging occurs.
In the example below, a listener is started on the loopback Internet protocol, '127.0.0.1' at port 4433. Then an EsSocketAppender on the same socket address and port is added to the (default) root logger. The socket appender will retry 40 times separated by 2 second intervals in case the listener does not respond.
The example relies on SCI sockets found in the Communications, TCP feature.
For example,
LISTENER
"Execute this code in a workspace to start a listener."
[
| config anSciSocketAddress listenSocket secureSocket rv msg continue cnt index |
anSciSocketAddress := (SciSocketAddress fromString: '127.0.0.1')
family: SciSocketConstants::AFINET;
port: 4433.
listenSocket := SciSocket newStreamSocket.
(rv := listenSocket bind: anSciSocketAddress) isSciError ifTrue:
[Transcript show: 'bind failed'; cr. self halt ].
(rv := listenSocket listen: 5) isSciError ifTrue:
[Transcript show: 'listen: failed'; cr. self halt ].
(secureSocket := listenSocket accept) isSciError ifTrue:
[Transcript show: 'accept failed'; cr. self halt ].
listenSocket close. continue := true. cnt := 0.
[continue] whileTrue: [(rv := secureSocket accept) isSslError
ifTrue: [ secureSocket close. self halt ].
msg := ByteArray new: 4096. cnt := cnt + 1.
(rv := secureSocket recv: msg length: 4096 startingAt: 1 flags: 0)
isSciError ifTrue: [ self halt ].
Transcript cr; nextPutAll: 'SslServer Got: ', msg asString; cr.
continue := (index := (msg asString indexOfSubCollection: 'Bye'
startingAt: 1 ifAbsent: [0]) = 0) ].
Transcript show: 'done'; cr.
secureSocket close ] fork.
SOCKET APPENDER
"Execute this code in a workspace to use the appender to log events.
Create a socketAppender.
Add the socketAppender to the root logger.
Test the logging."
| socketAppender |
EsLogManager reset. "Clean up logging setup for the example."
socketAppender := EsSocketAppender
name: 'aSocketAppender'
layout: (EsPatternLayout new: '%d %m')
level: EsLevel Warn
host: 'localHost'
port: 4433
retryCount: 40
retrySeconds: 2.
EsLogManager rootLogger addAppender: socketAppender deepCopy.
2 timesRepeat: [
EsLogManager logWarn: 'Hi Mom'.
(Delay forMilliseconds: 500) wait ].
EsLogManager logWarn: 'Bye'.
EsLogManager shutDown. "Discard any log4s state."
The above example will result in 3 lines written to the socket at IP address '127.0.0.1'. Each line will look something like this. The first part of the line is the date and time including the milliseconds; the second part is the warning message itself.
2017-06-27 17:28:17, 704 Hi, Mom
Since the example listener writes to the Transcript, the Transcript will show something like this:
SslServer Got: 2017-06-27 17:28:17,704 Hi Mom
SslServer Got: 2017-06-27 17:28:17,276 Hi Mom
2017-06-30 14:11:13,778 Bye
done
asyncSocketAppender
The asynchronous socket appender specifies its attributes just as the socketAppender. The example below creates two new appenders, one to handle the asynchronous nature of the logging; the other to handle the direction of logging events to the set of files.
For example,
LISTENER
"Execute this code in a workspace to start a listener."
[
| config anSciSocketAddress listenSocket secureSocket rv msg continue cnt index |
anSciSocketAddress := (SciSocketAddress fromString: '127.0.0.1')
family: SciSocketConstants::AFINET;
port: 4433.
listenSocket := SciSocket newStreamSocket.
(rv := listenSocket bind: anSciSocketAddress) isSciError ifTrue:
[Transcript show: 'bind failed'; cr. self halt ].
(rv := listenSocket listen: 5) isSciError ifTrue:
[Transcript show: 'listen: failed' ; cr. self halt ].
(secureSocket := listenSocket accept) isSciError ifTrue:
[Transcript show: 'accept failed'; cr. self halt ].
listenSocket close. continue := true. cnt := 0.
[continue] whileTrue: [(rv := secureSocket accept) isSslError
ifTrue: [ secureSocket close. self halt ].
msg := ByteArray new: 4096. cnt := cnt + 1.
(rv := secureSocket recv: msg length: 4096 startingAt: 1 flags: 0)
isSciError ifTrue: [ self halt ].
Transcript cr; nextPutAll: 'SslServer Got: ', msg asString; cr.
continue := (index := (msg asString indexOfSubCollection: 'Bye'
startingAt: 1 ifAbsent: [0]) = 0) ].
Transcript show: 'done'; cr.
secureSocket close ] fork.
ASYNCHRONOUS SOCKET APPENDER
"Execute this code in a workspace to use the appender to log events.
Create an asyncAppender.
Add the asyncAppender to the root logger.
Create a socketAppender.
Add the socketAppender to the asyncAppender.
Test the logging."
| socketAppender asyncAppender asyncAppenderName |
EsLogManager reset. "Clean up logging setup for the example."
asyncAppenderName := 'anAsyncAppender'.
asyncAppender := EsAsyncAppender name: asyncAppenderName.
EsLogManager rootLogger addAppender: asyncAppender.
socketAppender := EsSocketAppender
name: 'aSocketAppender' "someCaseSensitiveAppenderName"
layout: (EsPatternLayout new: '%d %m')
level: EsLevel Warn
host: 'localHost'
port: 4433
retryCount: 40
retrySeconds: 2.
(EsLogManager rootLogger getAppender: asyncAppenderName)
addAppender: socketAppender deepCopy.
2 timesRepeat: [EsLogManager logWarn: 'Hi Mom'.
(Delay forMilliseconds: 500) wait ].
EsLogManager logWarn: 'Bye'.
EsLogManager shutDown. "Discard any log4s state."
The above example will result in 3 lines written to the socket at IP address '192.168.1.1'. Each line will look something like this. The first part of the line is the date and time including the milliseconds; the second part is the warning message itself.
2017-06-27 17: 36:52,291 Hi, Mom
Since the example listener writes to the Transcript, the Transcript will show something like this:
SslServer Got: 2017-06-27 17:36:52,291 Hi Mom
SslServer Got: 2017-06-27 17:36:52,822 Hi Mom
SslServer Got: 2017-06-27 17:36:53,324 Bye
Done