All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.quartzdesk.api.agent.log.log4j2.Log4j2InterceptionAppender Maven / Gradle / Ivy

Go to download

QuartzDesk Public API library required for QuartzDesk Standard and Enterprise edition installations. This library must be placed on the classpath of the Quartz scheduler based application that is managed by QuartzDesk. It is important that this library is loaded by the same classloader that loads the Quartz scheduler API used by the application.

There is a newer version: 5.0.3
Show newest version
/*
 * Copyright (c) 2013-2019 QuartzDesk.com. All Rights Reserved.
 * QuartzDesk.com PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package com.quartzdesk.api.agent.log.log4j2;

import com.quartzdesk.api.agent.log.LoggingInterceptorWrapper;
import com.quartzdesk.api.agent.log.LoggingInterceptorWrapperException;
import com.quartzdesk.api.agent.log.WorkerThreadLoggingInterceptorRegistry;

import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.Core;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.appender.AbstractAppender;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginElement;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
import org.apache.logging.log4j.core.layout.AbstractStringLayout;
import org.apache.logging.log4j.core.layout.PatternLayout;

import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

/**
 * Implementation of a Log4j2 appender that passes the log events to the QuartzDesk JVM agent that intercepts log
 * messages produced by executed jobs.
 * 
 * ==== Example log4j2.xml ====
 * ...
 *
 * <QuartzDesk name="QUARTZDESK_JVM_AGENT">
 *   <PatternLayout pattern="[%d{ISO8601}] %-5p [%t] [%C:%L] - %m%n"/>
 *   <filters>
 *     <ThresholdFilter level="trace"/>
 *   </filters>
 * </QuartzDesk>
 *
 * ...
 *
 * <root level="warn">
 *   ...
 *   <appender-ref ref="QUARTZDESK_JVM_AGENT"/>
 * </root>
 * 
* * This implementation is not statically bound to the QuartzDesk JVM Agent API, nor to the domain object API. * Therefore it is safe to use this appender on JVMs running without an installed QuartzDesk JVM Agent. * * @version $Id:$ * @see LoggingInterceptorWrapper */ @Plugin( name = "QuartzDeskJvmAgent", category = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE ) public class Log4j2InterceptionAppender extends AbstractAppender { /* * !!! IMPORTANT NOTE !!! * * Log4j2 appenders must be registered in org.apache.logging.log4j.core.config.plugins.Log4j2Plugins.dat * resources located on the classpath. Log4j2Plugins.dat is a binary file that can be generated using * Maven and the org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor annotation processor. */ /** * Flag indicating if the LogEvent class in the Log4j2 API provides the getThreadId() method. This method is * available since version 2.6. */ private boolean eventHasGetThreadIdMethod; private LoggingInterceptorWrapper loggingInterceptor; /** * Creates a new {@link Log4j2InterceptionAppender} instance. * * @param name The Appender name. * @param filter The Filter to associate with the Appender. * @param layout The layout to use to format the event. * @param ignoreExceptions If true, exceptions will be logged and suppressed. If false errors will be logged and * then passed to the application. */ protected Log4j2InterceptionAppender( String name, Filter filter, Layout layout, boolean ignoreExceptions ) { super( name, filter, layout, ignoreExceptions ); LOGGER.info( "Starting " + Log4j2InterceptionAppender.class.getName() + ", name=" + name ); try { // LogEvent.getThreadId() method is available in Log4j2 API >= 2.6 LogEvent.class.getMethod( "getThreadId" ); eventHasGetThreadIdMethod = true; } catch ( NoSuchMethodException e ) { eventHasGetThreadIdMethod = false; } try { loggingInterceptor = LoggingInterceptorWrapper.create(); } catch ( LoggingInterceptorWrapperException e ) { // LOGGER.warn( "Cannot initialize " + LoggingInterceptorWrapper.class.getName() + ". Appender " + getName() + // " will be disabled." + Constants.LINE_SEP, e ); LOGGER.warn( "Cannot initialize " + LoggingInterceptorWrapper.class.getName() + ". Appender " + getName() + " will be disabled. Cause: " + e.getMessage() ); } } @PluginFactory public static Log4j2InterceptionAppender createAppender( @PluginAttribute( "name" ) String name, @PluginAttribute( "ignoreExceptions" ) boolean ignoreExceptions, @PluginElement( "layout" ) Layout layout, @PluginElement( "filters" ) Filter filter ) { if ( name == null ) { LOGGER.error( "No name provided for QuartzDeskAppender appender." ); return null; } if ( layout == null ) { //noinspection unchecked layout = PatternLayout.createDefaultLayout(); } return new Log4j2InterceptionAppender( name, filter, layout, ignoreExceptions ); } @Override public void append( LogEvent event ) { Long threadId = getThreadId( event ); /* * The emitter thread can be a worker thread (a thread created / used by the main job thread). */ Long jobThreadId = WorkerThreadLoggingInterceptorRegistry.INSTANCE.getJobThreadForWorkerThread( threadId ); if ( jobThreadId != null ) { threadId = jobThreadId; } if ( loggingInterceptor != null && loggingInterceptor.isIntercepting( threadId ) ) { LoggingInterceptorWrapper.MessagePriority priority = convertLevel2Priority( event.getLevel() ); Layout layout = getLayout(); if ( layout instanceof AbstractStringLayout ) { byte[] eventBytes = layout.toByteArray( event ); // we need to determine the charset of the layout that was used to extract bytes from the formatted log event Charset charset = getLayoutCharset( (AbstractStringLayout) layout ); String message = new String( eventBytes, charset ); loggingInterceptor.intercept( threadId, priority, message ); } else { LOGGER.error( "Layout in QuartzDeskAppender must extend AbstractStringLayout. It is typically PatternLayout." ); } } } /** * Returns the {@link LoggingInterceptorWrapper.MessagePriority} for the specified Log4J level. * * @param level a log4j level. * @return the {@link LoggingInterceptorWrapper.MessagePriority} for the specified Log4J level. */ protected LoggingInterceptorWrapper.MessagePriority convertLevel2Priority( Level level ) { // OFF, FATAL, ERROR, WARN, INFO, DEBUG, TRACE, ALL; if ( level.equals( Level.TRACE ) ) return LoggingInterceptorWrapper.MessagePriority.TRACE; else if ( level.equals( Level.DEBUG ) ) return LoggingInterceptorWrapper.MessagePriority.DEBUG; else if ( level.equals( Level.INFO ) ) return LoggingInterceptorWrapper.MessagePriority.INFO; else if ( level.equals( Level.WARN ) ) return LoggingInterceptorWrapper.MessagePriority.WARN; else if ( level.equals( Level.ERROR ) ) return LoggingInterceptorWrapper.MessagePriority.ERROR; else if ( level.equals( Level.FATAL ) ) // FATAL mapped to ERROR return LoggingInterceptorWrapper.MessagePriority.ERROR; else { LOGGER.warn( "Cannot map logging level: " + level + ". Using default message priority: " + LoggingInterceptorWrapper.DEFAULT_MESSAGE_PRIORITY ); return LoggingInterceptorWrapper.DEFAULT_MESSAGE_PRIORITY; } } /** * Returns the {@link Charset} used by the specified layout to format log events and produce their byte array * representations. * * @param layout a layout. * @return the {@link Charset}. */ protected Charset getLayoutCharset( L layout ) { try { // getCharset method is protected Method method = getCharsetMethod( layout.getClass() ); if ( method != null ) { method.setAccessible( true ); Object result = method.invoke( layout ); if ( result instanceof Charset ) { return (Charset) result; } } } catch ( InvocationTargetException | IllegalAccessException e ) { // should not happen } // fall-back charset return StandardCharsets.UTF_8; } /** * Returns the {@code getCharset} method from the specified class, or null if not found. * * @param clazz a class. * @return the {@code getCharset} method, or null if not found. */ private Method getCharsetMethod( Class clazz ) { while ( clazz != null ) { try { return clazz.getDeclaredMethod( "getCharset" ); } catch ( NoSuchMethodException e ) { // search in super-class clazz = clazz.getSuperclass(); } } return null; } /** * Returns the thread ID of the thread that produced the specified log event. * * @param event the log event. * @return the thread ID of the thread that produced the specified log event. */ private long getThreadId( LogEvent event ) { // Log4j2 API provides LogEvent.getThreadId() method from version 2.6 return eventHasGetThreadIdMethod ? event.getThreadId() : Thread.currentThread().getId(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy