-
Notifications
You must be signed in to change notification settings - Fork 110
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[LOGMGR-177] Add a new DelayedHandler which will queue messages until…
… at least one child handler is set.
- Loading branch information
Showing
2 changed files
with
483 additions
and
0 deletions.
There are no files selected for viewing
228 changes: 228 additions & 0 deletions
228
src/main/java/org/jboss/logmanager/handlers/DelayedHandler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,228 @@ | ||
/* | ||
* JBoss, Home of Professional Open Source. | ||
* | ||
* Copyright 2017 Red Hat, Inc., and individual contributors | ||
* as indicated by the @author tags. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package org.jboss.logmanager.handlers; | ||
|
||
import java.util.ArrayDeque; | ||
import java.util.Deque; | ||
import java.util.logging.Formatter; | ||
import java.util.logging.Handler; | ||
|
||
import org.jboss.logmanager.ExtHandler; | ||
import org.jboss.logmanager.ExtLogRecord; | ||
import org.jboss.logmanager.StandardOutputStreams; | ||
import org.jboss.logmanager.formatters.PatternFormatter; | ||
|
||
/** | ||
* A handler that queues messages until it's at least one child handler is {@linkplain #addHandler(Handler) added} or | ||
* {@linkplain #setHandlers(Handler[]) set}. If the children handlers are {@linkplain #clearHandlers() cleared} then | ||
* the handler is no longer considered activated and messages will once again be queued. | ||
* | ||
* @author <a href="mailto:[email protected]">James R. Perkins</a> | ||
*/ | ||
@SuppressWarnings({"unused", "WeakerAccess"}) | ||
public class DelayedHandler extends ExtHandler { | ||
|
||
private final Deque<ExtLogRecord> logRecords = new ArrayDeque<>(); | ||
|
||
private volatile boolean activated = false; | ||
private volatile int callerCalculationState = -1; | ||
|
||
@Override | ||
protected void doPublish(final ExtLogRecord record) { | ||
// Check one more time to see if we've been activated before queuing the messages | ||
if (activated) { | ||
publishToChildren(record); | ||
super.doPublish(record); | ||
} else { | ||
synchronized (this) { | ||
if (activated) { | ||
publishToChildren(record); | ||
super.doPublish(record); | ||
} else { | ||
// Determine if we need to calculate the caller information before we queue the record | ||
if (isCallerCalculationRequired()) { | ||
// prepare record to move to another thread | ||
record.copyAll(); | ||
} else { | ||
// Disable the caller calculation since it's been determined we won't be using it | ||
record.disableCallerCalculation(); | ||
// Copy the MDC over | ||
record.copyMdc(); | ||
// In case serialization is required by a child handler | ||
record.getFormattedMessage(); | ||
} | ||
logRecords.addLast(record); | ||
} | ||
} | ||
} | ||
} | ||
|
||
@Override | ||
public final void close() throws SecurityException { | ||
checkAccess(this); | ||
synchronized (this) { | ||
if (!logRecords.isEmpty()) { | ||
Formatter formatter = getFormatter(); | ||
if (formatter == null) { | ||
formatter = new PatternFormatter("%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%c] (%t) %s%e%n"); | ||
} | ||
StandardOutputStreams.printError("The DelayedHandler was closed before any children handlers were " + | ||
"configured. Messages will be written to stderr."); | ||
// Always attempt to drain the queue | ||
ExtLogRecord record; | ||
while ((record = logRecords.pollFirst()) != null) { | ||
StandardOutputStreams.printError(formatter.format(record)); | ||
} | ||
} | ||
} | ||
activated = false; | ||
super.close(); | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
* <p> | ||
* Note that once this is invoked the handler will be activated and the messages will no longer be queued. If more | ||
* than one child handler is required the {@link #setHandlers(Handler[])} should be used. | ||
* </p> | ||
* | ||
* @see #setHandlers(Handler[]) | ||
*/ | ||
@Override | ||
public void addHandler(final Handler handler) throws SecurityException { | ||
super.addHandler(handler); | ||
activate(); | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
* <p> | ||
* Note that once this is invoked the handler will be activated and the messages will no longer be queued. | ||
* </p> | ||
*/ | ||
@Override | ||
public Handler[] setHandlers(final Handler[] newHandlers) throws SecurityException { | ||
final Handler[] result = super.setHandlers(newHandlers); | ||
activate(); | ||
return result; | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
* <p> | ||
* Note that if the last child handler is removed the handler will no longer be activated and the messages will | ||
* again be queued. | ||
* </p> | ||
* | ||
* @see #clearHandlers() | ||
*/ | ||
@Override | ||
public void removeHandler(final Handler handler) throws SecurityException { | ||
super.removeHandler(handler); | ||
activated = handlers.length != 0; | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
* <p> | ||
* Note that once this is invoked the handler will no longer be activated and messages will again be queued. | ||
* </p> | ||
* | ||
* @see #removeHandler(Handler) | ||
*/ | ||
@Override | ||
public Handler[] clearHandlers() throws SecurityException { | ||
activated = false; | ||
return super.clearHandlers(); | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
* <p> | ||
* This can be overridden to always require the caller calculation by setting the | ||
* {@link #setCallerCalculationRequired(boolean)} value to {@code true} or permanently disabled for this handler by | ||
* setting the value to {@code false}. | ||
* </p> | ||
* | ||
* @see #setCallerCalculationRequired(boolean) | ||
*/ | ||
@Override | ||
public boolean isCallerCalculationRequired() { | ||
final int callerCalculationState = this.callerCalculationState; | ||
if (callerCalculationState == 0) { | ||
return false; | ||
} else if (callerCalculationState == 1) { | ||
return true; | ||
} | ||
// It's possible being a delayed handler that this could report a false negative. However since the this can | ||
// manually be overridden it's best to assume we don't want the caller information. | ||
return super.isCallerCalculationRequired(); | ||
} | ||
|
||
/** | ||
* Sets whether or not {@linkplain ExtLogRecord#copyAll() caller information} will be required when formatting | ||
* records. | ||
* <p> | ||
* If set to {@code true} the {@linkplain ExtLogRecord#copyAll() caller information} will be calculated for each | ||
* record that is placed in the queue. If set to {@code false} only the required information serialized and the | ||
* caller information is {@linkplain ExtLogRecord#disableCallerCalculation() disabled}. | ||
* </p> | ||
* <p> | ||
* Note that the caller information is only attempted to be calculated when the handler has not been activated. Once | ||
* activated it's up to the {@linkplain #getHandlers() children handlers} to determine how the record is processed. | ||
* </p> | ||
* | ||
* @param value {@code false} to explicitly disable the caller information otherwise {@code false} to always | ||
* ensure the caller information is recorded | ||
*/ | ||
public void setCallerCalculationRequired(final boolean value) { | ||
if (value) { | ||
callerCalculationState = 1; | ||
} else { | ||
callerCalculationState = 0; | ||
} | ||
} | ||
|
||
/** | ||
* Indicates whether or not this handler has been activated. | ||
* | ||
* @return {@code true} if the handler has been activated, otherwise {@code false} | ||
*/ | ||
public final boolean isActivated() { | ||
return activated; | ||
} | ||
|
||
private synchronized void activate() { | ||
// Always attempt to drain the queue | ||
ExtLogRecord record; | ||
while ((record = logRecords.pollFirst()) != null) { | ||
if (isEnabled() && isLoggable(record)) { | ||
publishToChildren(record); | ||
} | ||
} | ||
activated = true; | ||
} | ||
|
||
private void publishToChildren(final ExtLogRecord record) { | ||
for (Handler handler : handlers) { | ||
handler.publish(record); | ||
} | ||
} | ||
} |
Oops, something went wrong.