com.jkoolcloud.tnt4j.logger.log4j.TNT4JManager Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of tnt4j-log4j Show documentation
Show all versions of tnt4j-log4j Show documentation
Log4j Appender over TNT4J API
The newest version!
/*
* Copyright 2014-2023 JKOOL, LLC.
*
* 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
*
* https://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 com.jkoolcloud.tnt4j.logger.log4j;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.appender.AbstractManager;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.LoggerConfig;
import org.apache.logging.log4j.message.Message;
import com.jkoolcloud.tnt4j.TrackingLogger;
import com.jkoolcloud.tnt4j.config.ConfigFactory;
import com.jkoolcloud.tnt4j.config.DefaultConfigFactory;
import com.jkoolcloud.tnt4j.config.TrackerConfig;
import com.jkoolcloud.tnt4j.core.*;
import com.jkoolcloud.tnt4j.logger.AppenderConstants;
import com.jkoolcloud.tnt4j.logger.AppenderTools;
import com.jkoolcloud.tnt4j.source.SourceType;
import com.jkoolcloud.tnt4j.tracker.TimeTracker;
import com.jkoolcloud.tnt4j.tracker.TrackingActivity;
import com.jkoolcloud.tnt4j.tracker.TrackingEvent;
import com.jkoolcloud.tnt4j.utils.Utils;
/**
* Manages Log4j logged messages to be reported over TNT4J API.
*
* @version $Revision: 1 $
*
* @see com.jkoolcloud.tnt4j.logger.log4j.TNT4JAppender
*/
public class TNT4JManager extends AbstractManager implements AppenderConstants {
private static final String UNKNOWN_VALUE = "UNKNOWN";
private final Configuration configuration;
private TrackingLogger logger;
private String sourceName;
private SourceType sourceType;
private String snapCategory;
private int maxActivitySize;
private boolean metricsOnException;
private long metricsFrequency;
private ConfigFactory cFactory = DefaultConfigFactory.getInstance();
private Map cProperties = null;
private long lastSnapshot = 0;
/**
* Constructs a TNT4J manager instance.
*
* @param configuration
* this manager bound appender configuration
* @param loggerContext
* the logger context
* @param name
* this manager bound appender name
* @param sourceName
* tracker source name
* @param sourceType
* tracker source type
* @param snapCategory
* snapshot category name
* @param maxActivitySize
* maximum activity linked items size
* @param metricsOnException
* flag indicating whether to report JVM metrics when exception is logged
* @param metricsFrequency
* JVM metrics reporting frequency, in seconds
*/
protected TNT4JManager(Configuration configuration, LoggerContext loggerContext, String name, String sourceName,
SourceType sourceType, String snapCategory, int maxActivitySize, boolean metricsOnException,
long metricsFrequency) {
super(loggerContext, name);
this.configuration = Objects.requireNonNull(configuration);
this.sourceName = sourceName;
this.sourceType = sourceType;
this.snapCategory = snapCategory;
this.maxActivitySize = maxActivitySize;
this.metricsOnException = metricsOnException;
this.metricsFrequency = metricsFrequency;
}
/**
* Returns this manager bound appender configuration.
*
* @return appender configuration instance
*/
public Configuration getConfiguration() {
return configuration;
}
/**
* Make the Manager available for use.
*/
public void startup() {
try {
if (sourceName == null) {
sourceName = getName();
}
TrackerConfig config = ((cProperties == null) ? cFactory.getConfig(sourceName, sourceType)
: cFactory.getConfig(sourceName, sourceType, cProperties));
logger = TrackingLogger.getInstance(config.build());
logger.open();
} catch (Throwable e) {
logError("Unable to create tracker" //
+ " instance=" + getName() //
+ ", config.factory=" + cFactory //
+ ", source.name=" + sourceName //
+ ", source.type=" + sourceType //
+ ", snapshot.category=" + snapCategory //
, e);
}
}
@Override
public boolean releaseSub(long timeout, TimeUnit timeUnit) {
if (logger != null) {
logger.close();
}
return true;
}
/**
* Checks if logger is initialized.
*
* @return {@code true} if logger is not {@code null}, {@code false} - otherwise
*/
protected boolean isReady() {
return logger != null;
}
/**
* Report single log event.
*
* @param event
* log event to report
*/
public void tnt(LogEvent event) {
if (!isReady()) {
return;
}
long lastReport = System.currentTimeMillis();
Message msg = event.getMessage();
String eventMsg = msg == null ? "" : msg.getFormattedMessage();
Throwable ex = msg == null ? null : msg.getThrowable();
HashMap attrs = new HashMap<>();
AppenderTools.parseEventMessage(attrs, eventMsg, '#');
boolean activityMessage = AppenderTools.isActivityInstruction(attrs);
if (activityMessage) {
AppenderTools.processActivityAttrs(logger, getName(), attrs, getOpLevel(event), ex);
} else {
TrackingActivity activity = logger.getCurrentActivity();
TrackingEvent tev = processEventMessage(attrs, activity, event, eventMsg, ex);
boolean reportMetrics = activity.isNoop() //
&& ((ex != null && metricsOnException) //
|| ((lastReport - lastSnapshot) > (metricsFrequency * 1_000)));
if (reportMetrics) {
String loggerName = event.getLoggerName();
if (loggerName == null) {
loggerName = UNKNOWN_VALUE;
} else if (LogManager.ROOT_LOGGER_NAME.equals(loggerName)) {
loggerName = LoggerConfig.ROOT;
}
String threadName = event.getThreadName();
if (Utils.isEmpty(threadName)) {
threadName = UNKNOWN_VALUE;
}
// report a single tracking event as part of an activity
activity = logger.newActivity(tev.getSeverity(), threadName);
activity.start();
activity.setResource(loggerName);
activity.setSource(tev.getSource()); // use event's source name for this activity
activity.setException(ex);
activity.setStatus(ex != null ? ActivityStatus.EXCEPTION : ActivityStatus.END);
activity.tnt(tev);
activity.stop();
logger.tnt(activity);
lastSnapshot = lastReport;
} else if (activity.isNoop()) {
// report a single tracking event as datagram
logger.tnt(tev);
} else {
activity.tnt(tev);
}
if (activity.getIdCount() >= maxActivitySize) {
activity.setException(ex);
activity.setStatus(ex != null ? ActivityStatus.EXCEPTION : ActivityStatus.END);
activity.stop();
logger.tnt(activity);
}
}
}
/**
* Obtain elapsed nanoseconds since last log4j event
*
* @return elapsed nanoseconds since last log4j event
*/
protected long getUsecsSinceLastEvent() {
return TimeUnit.NANOSECONDS.toMicros(TimeTracker.hitAndGet());
}
/**
* Process a given log4j event into a TNT4J event object {@link com.jkoolcloud.tnt4j.tracker.TrackingEvent}.
*
* @param attrs
* a set of name/value pairs
* @param activity
* TNT4J activity associated with current message
* @param jev
* log4j logging event object
* @param eventMsg
* string message associated with this event
* @param ex
* exception associated with this event
*
* @return TNT4J tracking event object
*/
private TrackingEvent processEventMessage(Map attrs, TrackingActivity activity, LogEvent jev,
String eventMsg, Throwable ex) {
int rCode = 0;
long elapsedTimeUsec = getUsecsSinceLastEvent();
long evTime = jev.getTimeMillis() * 1_000; // convert to usec
long startTime = 0, endTime = 0;
Snapshot snapshot = null;
OpCompCode cCode = getOpCompCode(jev);
OpLevel level = getOpLevel(jev);
StackTraceElement location = jev.getSource();
String loggerName = jev.getLoggerName();
if (LogManager.ROOT_LOGGER_NAME.equals(loggerName)) {
loggerName = LoggerConfig.ROOT;
}
TrackingEvent event = logger.newEvent(location == null ? UNKNOWN_VALUE : location.getMethodName(), eventMsg);
event.getOperation().setSeverity(level);
event.setTag(jev.getThreadName());
event.getOperation().setResource(loggerName == null ? UNKNOWN_VALUE : loggerName);
if (location != null) {
event.setLocation(location.getFileName() + ":" + location.getLineNumber());
}
event.setSource(logger.getConfiguration().getSourceFactory().newSource(loggerName));
for (Map.Entry entry : attrs.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
if (key.equalsIgnoreCase(PARAM_CORRELATOR_LABEL)) {
event.setCorrelator(value);
} else if (key.equalsIgnoreCase(PARAM_TAG_LABEL)) {
event.setTag(value);
} else if (key.equalsIgnoreCase(PARAM_LOCATION_LABEL)) {
event.setLocation(value);
} else if (key.equalsIgnoreCase(PARAM_RESOURCE_LABEL)) {
event.getOperation().setResource(value);
} else if (key.equalsIgnoreCase(PARAM_USER_LABEL)) {
event.getOperation().setUser(value);
} else if (key.equalsIgnoreCase(PARAM_ELAPSED_TIME_LABEL)) {
elapsedTimeUsec = Long.parseLong(value);
} else if (key.equalsIgnoreCase(PARAM_AGE_TIME_LABEL)) {
event.setMessageAge(Long.parseLong(value));
} else if (key.equalsIgnoreCase(PARAM_START_TIME_LABEL)) {
startTime = Long.parseLong(value);
} else if (key.equalsIgnoreCase(PARAM_END_TIME_LABEL)) {
endTime = Long.parseLong(value);
} else if (key.equalsIgnoreCase(PARAM_REASON_CODE_LABEL)) {
rCode = Integer.parseInt(value);
} else if (key.equalsIgnoreCase(PARAM_COMP_CODE_LABEL)) {
cCode = OpCompCode.valueOf(value);
} else if (key.equalsIgnoreCase(PARAM_SEVERITY_LABEL)) {
event.getOperation().setSeverity(OpLevel.valueOf(value));
} else if (key.equalsIgnoreCase(PARAM_OP_TYPE_LABEL)) {
event.getOperation().setType(OpType.valueOf(value));
} else if (key.equalsIgnoreCase(PARAM_OP_NAME_LABEL)) {
event.getOperation().setName(value);
} else if (key.equalsIgnoreCase(PARAM_EXCEPTION_LABEL)) {
event.getOperation().setException(value);
} else if (key.equalsIgnoreCase(PARAM_MSG_DATA_LABEL)) {
event.setMessage(value);
} else if (key.equalsIgnoreCase(PARAM_APPL_LABEL)) {
event.setSource(logger.getConfiguration().getSourceFactory().newSource(value));
} else if (!Utils.isEmpty(key) && !Utils.isEmpty(value)) {
// add unknown attribute into snapshot
if (snapshot == null) {
snapshot = logger.newSnapshot(snapCategory, event.getOperation().getName());
event.getOperation().addSnapshot(snapshot);
}
snapshot.add(AppenderTools.toProperty(key, value));
}
}
startTime = startTime <= 0 ? (evTime - elapsedTimeUsec) : evTime;
endTime = endTime <= 0 ? (startTime + elapsedTimeUsec) : endTime;
event.start(startTime);
event.stop(cCode, rCode, ex, endTime);
return event;
}
/**
* Map log4j logging event level to TNT4J {@link com.jkoolcloud.tnt4j.core.OpLevel}.
*
* @param event
* log4j logging event object
* @return TNT4J {@link com.jkoolcloud.tnt4j.core.OpLevel}.
*/
private OpLevel getOpLevel(LogEvent event) {
Level lvl = event.getLevel();
if (lvl == Level.INFO) {
return OpLevel.INFO;
} else if (lvl == Level.FATAL) {
return OpLevel.FATAL;
} else if (lvl == Level.ERROR) {
return OpLevel.ERROR;
} else if (lvl == Level.WARN) {
return OpLevel.WARNING;
} else if (lvl == Level.DEBUG) {
return OpLevel.DEBUG;
} else if (lvl == Level.TRACE) {
return OpLevel.TRACE;
} else if (lvl == Level.OFF) {
return OpLevel.NONE;
} else {
return OpLevel.INFO;
}
}
/**
* Map log4j logging event level to TNT4J {@link com.jkoolcloud.tnt4j.core.OpCompCode}.
*
* @param event
* log4j logging event object
* @return TNT4J {@link com.jkoolcloud.tnt4j.core.OpCompCode}.
*/
private OpCompCode getOpCompCode(LogEvent event) {
Level lvl = event.getLevel();
if (lvl == Level.INFO) {
return OpCompCode.SUCCESS;
} else if (lvl == Level.FATAL) {
return OpCompCode.ERROR;
} else if (lvl == Level.ERROR) {
return OpCompCode.ERROR;
} else if (lvl == Level.WARN) {
return OpCompCode.WARNING;
} else if (lvl == Level.DEBUG) {
return OpCompCode.SUCCESS;
} else if (lvl == Level.TRACE) {
return OpCompCode.SUCCESS;
} else if (lvl == Level.OFF) {
return OpCompCode.SUCCESS;
} else {
return OpCompCode.SUCCESS;
}
}
/**
* Associate a logger configuration factory with this appender
*
* @param cf
* logger configuration factory instances
* @see com.jkoolcloud.tnt4j.config.ConfigFactory
*/
public void setConfigFactory(ConfigFactory cf) {
cFactory = cf;
}
/**
* Assign a set of TNT4J streaming properties. These properties are used to configure underlying TNT4J
* {@code TrackingLogger}
*
* @param cProps
* user defined TNT4J property map
*/
public void setConfigProperties(Map cProps) {
cProperties = cProps;
}
/**
* Obtain snapshot category associated with this appender. This name is used for reporting user defined metrics
*
* @return snapshot category name string that maps to TNT4J snapshot category
*/
public String getSnapshotCategory() {
return snapCategory;
}
/**
* Set snapshot category associated with this appender. This name is used for reporting user defined metrics
*
* @param name
* snapshot category name
*/
public void setSnapshotCategory(String name) {
snapCategory = name;
}
/**
* Obtain source name associated with this appender. This name is used TNT4J source for loading TNT4J configuration.
*
* @return source name string that maps to TNT4J configuration
*/
public String getSourceName() {
return sourceName;
}
/**
* Set source name associated with this appender. This name is used TNT4J source for loading TNT4J configuration.
*
* @param name
* source name
*/
public void setSourceName(String name) {
sourceName = name;
}
/**
* Obtain source type associated with this appender see {@code SourceType}
*
* @return source type string representation
* @see SourceType
*/
public String getSourceType() {
return sourceType.toString();
}
/**
* Assign default source type string see {@code SourceType}
*
* @param type
* source type string representation, see {@code SourceType}
* @see SourceType
*/
public void setSourceType(String type) {
sourceType = SourceType.valueOf(type);
}
/**
* Assign default source type, see {@code SourceType}
*
* @param type
* source type, see {@code SourceType}
* @see SourceType
*/
public void setSourceType(SourceType type) {
sourceType = type;
}
/**
* Obtain maximum size of any activity
*
* @return source name string that maps to TNT4J configuration
*/
public int getMaxActivitySize() {
return maxActivitySize;
}
/**
* Set maximum size of any activity
*
* @param size
* maximum size must be greater than 0
*/
public void setMaxActivitySize(int size) {
maxActivitySize = size;
}
/**
* Return whether appender generates metrics log entries with exception
*
* @return {@code true} to publish default jvm metrics when exception is logged
*/
public boolean getMetricsOnException() {
return metricsOnException;
}
/**
* Direct appender to generate metrics log entries with exception when set to {@code true}, {@code false} -
* otherwise.
*
* @param flag
* {@code true} to append metrics on exception, {@code false} - otherwise
*/
public void setMetricsOnException(boolean flag) {
metricsOnException = flag;
}
/**
* Appender generates metrics based on a given frequency in seconds.
*
* @return metrics frequency, in seconds
*/
public long getMetricsFrequency() {
return metricsFrequency;
}
/**
* Set metric collection frequency seconds.
*
* @param freq
* number of seconds
*/
public void setMetricsFrequency(long freq) {
metricsFrequency = freq;
}
}