[
https://issues.apache.org/jira/browse/LOG4J2-491?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=13899741#comment-13899741
]
Bohdan Mushkevych commented on LOG4J2-491:
------------------------------------------
Hi All!
I am using slightly modified version of Joe's MyRolloverStrategy (BTW: Thank
you Joe for publishing your research efforts):
{code:java}
@org.apache.logging.log4j.core.config.plugins.Plugin(name="LogStreamRolloverStrategy",
category="Core", printObject=true)
public class LogStreamRolloverStrategy extends DefaultRolloverStrategy {
protected static final Logger logger = StatusLogger.getLogger();
protected String tableName;
private static final int MIN_WINDOW_SIZE = 1;
private static final int DEFAULT_WINDOW_SIZE = 7;
@PluginFactory
public static LogStreamRolloverStrategy createStrategy(
@PluginAttribute("max") final String max,
@PluginAttribute("min") final String min,
@PluginAttribute("fileIndex") final String fileIndex,
@PluginAttribute("compressionLevel") final String
compressionLevelStr,
@PluginAttribute("tableName") final String tableName,
@PluginConfiguration final Configuration config) {
final boolean useMax = fileIndex == null ? true :
fileIndex.equalsIgnoreCase("max");
int minIndex;
if (min != null) {
minIndex = Integer.parseInt(min);
if (minIndex < 1) {
LOGGER.error("Minimum window size too small. Limited to " +
MIN_WINDOW_SIZE);
minIndex = MIN_WINDOW_SIZE;
}
} else {
minIndex = MIN_WINDOW_SIZE;
}
int maxIndex;
if (max != null) {
maxIndex = Integer.parseInt(max);
if (maxIndex < minIndex) {
maxIndex = minIndex < DEFAULT_WINDOW_SIZE ? DEFAULT_WINDOW_SIZE
: minIndex;
LOGGER.error("Maximum window size must be greater than the
minimum windows size. Set to " + maxIndex);
}
} else {
maxIndex = DEFAULT_WINDOW_SIZE;
}
final int compressionLevel = Integers.parseInt(compressionLevelStr,
Deflater.DEFAULT_COMPRESSION);
return new LogStreamRolloverStrategy(minIndex, maxIndex, useMax,
compressionLevel, tableName, config.getStrSubstitutor());
}
protected LogStreamRolloverStrategy(int minIndex,
int maxIndex,
boolean useMax,
int compressionLevel,
String tableName,
StrSubstitutor subst) {
super(minIndex, maxIndex, useMax, compressionLevel, subst);
this.tableName = tableName;
}
// Wrapper class only for setting a hook to execute()
static class LogStreamAction implements Action {
final Action delegate;
final String fileName;
final String header;
final String footer;
public LogStreamAction(final Action delegate, final String fileName,
final String header, final String footer) {
this.delegate = delegate;
this.fileName = fileName;
this.header = header;
this.footer = footer;
}
@Override
public void run() {
delegate.run();
}
@Override
public boolean execute() throws IOException {
try {
BufferedWriter writer = null;
try {
writer = new BufferedWriter(new FileWriter(new
File(fileName), true));
writer.write(footer + System.getProperty("line.separator"));
} finally {
if (writer != null)
writer.close();
}
} catch (Throwable e) {
logger.error("Writing to bottom of an old logfile \"" +
fileName + "\" with", e);
}
boolean ret = delegate.execute();
try {
BufferedWriter writer = null;
try {
writer = new BufferedWriter(new FileWriter(new
File(fileName), true));
writer.write(header + System.getProperty("line.separator"));
} finally {
if (writer != null)
writer.close();
}
} catch (Throwable e) {
logger.error("Writing to top of a new logfile \"" + fileName +
"\" with", e);
}
return ret;
}
@Override
public void close() {
delegate.close();
}
@Override
public boolean isComplete() {
return delegate.isComplete();
}
}
// Wrapper class only for setting a hook to getSynchronous().execute()
static class LogStreamRolloverDescription implements RolloverDescription {
public static final String EMPTY_STRING = "";
final RolloverDescription delegate;
final String tableName;
public LogStreamRolloverDescription(final RolloverDescription delegate,
final String tableName) {
this.delegate = delegate;
this.tableName = tableName;
}
public String getCsvHeader() {
StringBuilder sbCsvHeader = new StringBuilder();
// compose the csv header
return sbCsvHeader.toString();
}
@Override
public String getActiveFileName() {
return delegate.getActiveFileName();
}
@Override
public boolean getAppend() {
// As soon as we have put some data to the top of the new logfile,
// subsequent writes should be performed with "append".
return true;
}
// The synchronous action is for renaming, here we want to hook
@Override
public Action getSynchronous() {
Action delegateAction = delegate.getSynchronous();
if (delegateAction == null) {
return null;
}
String footer = EMPTY_STRING;
String header = getCsvHeader();
return new LogStreamAction(delegateAction,
delegate.getActiveFileName(), header, footer);
}
// The asynchronous action is for compressing, we don't need to hook
here
@Override public Action getAsynchronous() {
return delegate.getAsynchronous();
}
}
public RolloverDescription rollover(final RollingFileManager manager) {
RolloverDescription ret = super.rollover(manager);
return new LogStreamRolloverDescription(ret, tableName);
}
}
{code}
Corresponding initialization log4j2.xml file is as follows:
{code:xml}
<Routing name="Routing">
<Routes pattern="$${sd:type}">
<Route>
<RollingFile name="RollingFile-${sd:type}"
fileName="logs/${date:yyyyMMdd}/${date:yyyyMMddHH}-${sd:type}-${hostName}.log"
filePattern="logs/${date:yyyyMMdd}/%d{yyyyMMddHH}-${sd:type}-${hostName}.%i.log.gz">
<PatternLayout>
<!-- %p stands for logging level -->
<!-- %K{m} stands for the message passed in
StructuredDataMessage map with key "m" -->
<!-- %n stands for new line -->
<pattern>%K{m}%n</pattern>
</PatternLayout>
<Policies>
<SizeBasedTriggeringPolicy size="500" />
<!--<SizeBasedTriggeringPolicy size="64 MB" />-->
</Policies>
<LogStreamRolloverStrategy tableName="${sd:type}"
max="999"/>
</RollingFile>
</Route>
</Routes>
</Routing>
{code}
Above code works well after the .log file roll-over. However, the very first
.log file contains no header.
What should I override/implement a hook to insert a header into a very first
.log file.
Dan
> RollingFile Appender - callbacks when rolling
> ----------------------------------------------
>
> Key: LOG4J2-491
> URL: https://issues.apache.org/jira/browse/LOG4J2-491
> Project: Log4j 2
> Issue Type: Wish
> Components: Appenders
> Affects Versions: 2.0-beta9
> Environment: Java 1.7, Linux
> Reporter: Joe Merten
>
> I want this callbacks to add some custom info at the top of each logfile,
> like the version string of my application, the application uptime and the
> system uptime. And even writing some »bye, bye / eof« to the bottom of the
> just closed logfile would also be fine.
> See also:
> * [LOG4J2-486]
> *
> [http://stackoverflow.com/questions/20819376/log4j2-rollingfile-appender-add-custom-info-at-the-start-of-each-logfile]
> Currently I need to extend DefaultRolloverStrategy and wrap around
> RolloverDescription and appender.rolling.helper.Action to place my code
> (and therefore I also have to copy some factory code of
> DefaultRolloverStrategy to support all config parameters etc.). Ok, this
> approach currently works but it needs ~150 lines of code and is
> maintenance-unfriendly eg. if DefaultRolloverStrategy gets more config
> parameters in future versions.
> The callbacks should provide at least the filename of the related logfile.
> An access to the configured Layout of the related appender would also
> helpful, so that the custom info could be correctly formatted (e.g. incl.
> timestamp or matching for xml etc.).
> I think there sould be 2 callbacks
> * 1st one for after all outstanding writes to the old logfile has been done
> (so thet I could add some stuff to the end of the old file), but before
> things like compressing are performed
> * 2nd one for to write my stuff to the top of the new logfile
> Ok, there must be take care of Layout.getHeader/Footer() (which is used by
> e.g. XMLLayout) considering if the callbacks should be called e.g. "inside or
> outside of the xml root tags".
> If I want to add my custom info as a kind of LogEvents, then the callbacks
> should be called before writing the Layout.getFooter() and after writing
> Layout.getHeader() to the file.
> But if someone want to add custom info outside of the xml root tags (e.g.
> someone might calculate a checksum over the logfile and write that to the
> very last line of the file) then the callbacks must be called outside
> header/footer.
> For my current needs, I require the callbacks inside of header/footer
> (although it not really matters for me while I currently only using
> PatternLayout).
> Bug finally that looks that we need callbacks for both cases.
--
This message was sent by Atlassian JIRA
(v6.1.5#6160)
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]