
org.apache.logging.log4j.weaver.log4j2.LoggerConversionHandler Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you 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.apache.logging.log4j.weaver.log4j2;
import static org.apache.logging.log4j.weaver.Constants.AT_DEBUG_METHOD;
import static org.apache.logging.log4j.weaver.Constants.AT_ERROR_METHOD;
import static org.apache.logging.log4j.weaver.Constants.AT_FATAL_METHOD;
import static org.apache.logging.log4j.weaver.Constants.AT_INFO_METHOD;
import static org.apache.logging.log4j.weaver.Constants.AT_LEVEL_METHOD;
import static org.apache.logging.log4j.weaver.Constants.AT_TRACE_METHOD;
import static org.apache.logging.log4j.weaver.Constants.AT_WARN_METHOD;
import static org.apache.logging.log4j.weaver.Constants.ENTRY_MESSAGE_TYPE;
import static org.apache.logging.log4j.weaver.Constants.LEVEL_TYPE;
import static org.apache.logging.log4j.weaver.Constants.LOGGER_TYPE;
import static org.apache.logging.log4j.weaver.Constants.LOG_AND_GET_METHOD;
import static org.apache.logging.log4j.weaver.Constants.LOG_BUILDER_TYPE;
import static org.apache.logging.log4j.weaver.Constants.MARKER_TYPE;
import static org.apache.logging.log4j.weaver.Constants.MESSAGE_SUPPLIER_TYPE;
import static org.apache.logging.log4j.weaver.Constants.MESSAGE_TYPE;
import static org.apache.logging.log4j.weaver.Constants.OBJECT_TYPE;
import static org.apache.logging.log4j.weaver.Constants.STRING_TYPE;
import static org.apache.logging.log4j.weaver.Constants.SUPPLIER_ARRAY_TYPE;
import static org.apache.logging.log4j.weaver.Constants.SUPPLIER_TYPE;
import static org.apache.logging.log4j.weaver.Constants.THROWABLE_TYPE;
import static org.apache.logging.log4j.weaver.Constants.WITH_MARKER_METHOD;
import static org.apache.logging.log4j.weaver.Constants.WITH_THROWABLE_METHOD;
import java.util.Arrays;
import org.apache.logging.log4j.weaver.ClassConversionHandler;
import org.apache.logging.log4j.weaver.Constants;
import org.apache.logging.log4j.weaver.ConversionException;
import org.apache.logging.log4j.weaver.LocationMethodVisitor;
import org.apache.logging.log4j.weaver.SupplierLambdaType;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.Method;
public class LoggerConversionHandler implements ClassConversionHandler {
private static final String CATCHING = "Catching";
private static final String CATCHING_MARKER = "CATCHING_MARKER";
private static final String ENTRY_MARKER = "ENTRY_MARKER";
private static final String EXIT_MARKER = "EXIT_MARKER";
private static final String THROWING = "Throwing";
private static final String THROWING_MARKER = "THROWING_MARKER";
// Argument list of `LogBuilder.log(String, Supplier...)`
private static final Type[] LOG_BUILDER_LOG_STRING_SUPPLIER = new Type[] {STRING_TYPE, SUPPLIER_ARRAY_TYPE};
// Argument list of `LogBuilder.log(Supplier)`
private static final Type[] LOG_BUILDER_LOG_SUPPLIER_MESSAGE = new Type[] {SUPPLIER_TYPE};
private static final Method LOG_BUILDER_LOG_SUPPLIER_METHOD =
new Method("log", Type.getMethodDescriptor(Type.VOID_TYPE, LOG_BUILDER_LOG_SUPPLIER_MESSAGE));
private static final Method LOG_BUILDER_LOG_STRING_METHOD =
new Method("log", Type.getMethodDescriptor(Type.VOID_TYPE, STRING_TYPE));
private static final Type ABSTRACT_LOGGER_TYPE = Type.getObjectType("org/apache/logging/log4j/spi/AbstractLogger");
private static final Type[] MESSAGE_OBJECT_ARRAY = {MESSAGE_TYPE, OBJECT_TYPE};
@Override
public String getOwner() {
return Constants.LOGGER_TYPE.getInternalName();
}
private void createLogBuilder(LocationMethodVisitor mv, String methodName) {
final Method method;
switch (methodName) {
case "debug":
method = AT_DEBUG_METHOD;
break;
case "error":
method = AT_ERROR_METHOD;
break;
case "fatal":
method = AT_FATAL_METHOD;
break;
case "info":
method = AT_INFO_METHOD;
break;
case "log":
method = AT_LEVEL_METHOD;
break;
case "trace":
method = AT_TRACE_METHOD;
break;
case "warn":
method = AT_WARN_METHOD;
break;
default:
throw new ConversionException("Unknown logging method " + methodName);
}
mv.invokeInterface(LOGGER_TYPE, method);
}
@Override
public void handleMethodInstruction(LocationMethodVisitor mv, String name, String descriptor) {
switch (name) {
case "debug":
case "error":
case "fatal":
case "info":
case "log":
case "trace":
case "warn":
handleLogMethods(mv, name, descriptor);
break;
case "printf":
handlePrintfMethods(mv, descriptor);
break;
case "always":
case "atDebug":
case "atError":
case "atFatal":
case "atInfo":
case "atLevel":
case "atTrace":
case "atWarn":
mv.invokeInterface(LOGGER_TYPE, new Method(name, descriptor));
mv.storeLocation();
break;
case "catching":
case "throwing":
handleCatchingThrowing(mv, descriptor, "throwing".equals(name));
break;
case "isDebugEnabled":
case "isEnabled":
case "isErrorEnabled":
case "isFatalEnabled":
case "isInfoEnabled":
case "isTraceEnabled":
case "isWarnEnabled":
case "logMessage":
// These are NOPs
mv.invokeInterface(LOGGER_TYPE, new Method(name, descriptor));
break;
case "traceEntry":
handleTraceEntry(mv, descriptor);
break;
case "traceExit":
handleTraceExit(mv, descriptor);
break;
default:
throw new ConversionException("Unsupported method 'org.apache.logging.log4j.Logger#" + name + "'.");
}
}
/**
* Rewrites the most common methods: {@code log} and its level specializations.
*/
private void handleLogMethods(LocationMethodVisitor mv, String name, String descriptor) {
final Type[] types = Type.getArgumentTypes(descriptor);
final int[] varIndexes = new int[types.length];
int from = types.length > 0 && types[0].equals(LEVEL_TYPE) ? 1 : 0;
int to = types.length;
// Store arguments to local variables
// TODO: most of the time there is a more efficient way
for (int i = to - 1; i >= from; i--) {
varIndexes[i] = mv.nextLocal();
mv.storeLocal(varIndexes[i], types[i]);
}
// create the LogBuilder
createLogBuilder(mv, name);
mv.storeLocation();
// Marker argument
if (from < to && types[from].equals(MARKER_TYPE)) {
mv.loadLocal(varIndexes[from], MARKER_TYPE);
mv.invokeInterface(LOG_BUILDER_TYPE, WITH_MARKER_METHOD);
from++;
}
// Throwable argument
if (from < to && types[to - 1].equals(THROWABLE_TYPE)) {
mv.loadLocal(varIndexes[to - 1], THROWABLE_TYPE);
mv.invokeInterface(LOG_BUILDER_TYPE, WITH_THROWABLE_METHOD);
to--;
}
// Call log(...)
final Type[] arguments;
// We need to replace (Supplier>) with ("{}", Supplier>)
if (SUPPLIER_TYPE.equals(types[from])) {
mv.push("{}");
mv.push(1);
mv.newArray(SUPPLIER_TYPE);
mv.dup();
mv.push(0);
mv.loadLocal(varIndexes[from], types[from]);
mv.arrayStore(SUPPLIER_TYPE);
arguments = LOG_BUILDER_LOG_STRING_SUPPLIER;
// We need to convert MessageSupplier to Supplier
} else if (MESSAGE_SUPPLIER_TYPE.equals(types[from])) {
mv.loadLocal(varIndexes[from], types[from]);
mv.invokeSupplierLambda(SupplierLambdaType.MESSAGE_SUPPLIER);
arguments = LOG_BUILDER_LOG_SUPPLIER_MESSAGE;
} else {
for (int i = from; i < to; i++) {
mv.loadLocal(varIndexes[i], types[i]);
}
arguments = Arrays.copyOfRange(types, from, to);
}
final Method logMethod = new Method("log", Type.VOID_TYPE, arguments);
mv.invokeInterface(LOG_BUILDER_TYPE, logMethod);
}
private void handlePrintfMethods(LocationMethodVisitor mv, String descriptor) {
final Type[] types = Type.getArgumentTypes(descriptor);
// Transform the last two arguments into a supplier
mv.invokeSupplierLambda(SupplierLambdaType.FORMATTED_MESSAGE);
int supplierIndex = mv.nextLocal();
mv.storeLocal(supplierIndex, SUPPLIER_TYPE);
int markerIndex = -1;
if (types[1].equals(MARKER_TYPE)) {
markerIndex = mv.nextLocal();
mv.storeLocal(markerIndex, MARKER_TYPE);
}
mv.invokeInterface(LOGGER_TYPE, AT_LEVEL_METHOD);
mv.storeLocation();
if (markerIndex >= 0) {
mv.loadLocal(markerIndex, MARKER_TYPE);
mv.invokeInterface(LOG_BUILDER_TYPE, WITH_MARKER_METHOD);
}
mv.loadLocal(supplierIndex, SUPPLIER_TYPE);
mv.invokeInterface(LOG_BUILDER_TYPE, LOG_BUILDER_LOG_SUPPLIER_METHOD);
}
private void handleCatchingThrowing(LocationMethodVisitor mv, String descriptor, boolean throwing) {
final boolean hasLevel = Type.getArgumentTypes(descriptor).length > 1;
final int throwableIndex = mv.nextLocal();
mv.storeLocal(throwableIndex, THROWABLE_TYPE);
if (hasLevel) {
mv.invokeInterface(LOGGER_TYPE, AT_LEVEL_METHOD);
} else {
mv.invokeInterface(LOGGER_TYPE, AT_ERROR_METHOD);
}
mv.storeLocation();
mv.loadLocal(throwableIndex, THROWABLE_TYPE);
mv.invokeInterface(LOG_BUILDER_TYPE, WITH_THROWABLE_METHOD);
mv.getStatic(ABSTRACT_LOGGER_TYPE, throwing ? THROWING_MARKER : CATCHING_MARKER, MARKER_TYPE);
mv.invokeInterface(LOG_BUILDER_TYPE, WITH_MARKER_METHOD);
mv.push(throwing ? THROWING : CATCHING);
mv.invokeInterface(LOG_BUILDER_TYPE, LOG_BUILDER_LOG_STRING_METHOD);
if (throwing) {
mv.loadLocal(throwableIndex, THROWABLE_TYPE);
}
}
private void handleTraceEntry(LocationMethodVisitor mv, String descriptor) {
final Type[] types = Type.getArgumentTypes(descriptor);
final int[] vars = new int[types.length];
for (int i = vars.length - 1; i >= 0; i--) {
vars[i] = mv.nextLocal();
mv.storeLocal(vars[i]);
}
// only Logger on stack
mv.dup();
final int loggerIdx = mv.nextLocal();
mv.storeLocal(loggerIdx, LOGGER_TYPE);
mv.invokeInterface(LOGGER_TYPE, AT_TRACE_METHOD);
mv.storeLocation();
mv.getStatic(ABSTRACT_LOGGER_TYPE, ENTRY_MARKER, MARKER_TYPE);
mv.invokeInterface(LOG_BUILDER_TYPE, WITH_MARKER_METHOD);
mv.loadLocal(loggerIdx, LOGGER_TYPE);
if (types.length == 0) {
mv.push((String) null);
mv.push((String) null);
mv.invokeSupplierLambda(SupplierLambdaType.ENTRY_MESSAGE_STRING_OBJECTS);
} else if (types[0].equals(MESSAGE_TYPE)) {
mv.loadLocal(vars[0]);
mv.invokeSupplierLambda(SupplierLambdaType.ENTRY_MESSAGE_MESSAGE);
} else {
if (types.length == 1) {
mv.push((String) null);
}
for (int i = 0; i < vars.length; i++) {
mv.loadLocal(vars[i]);
}
final boolean usesSuppliers = types[types.length - 1].equals(SUPPLIER_ARRAY_TYPE);
mv.invokeSupplierLambda(
usesSuppliers
? SupplierLambdaType.ENTRY_MESSAGE_STRING_SUPPLIERS
: SupplierLambdaType.ENTRY_MESSAGE_STRING_OBJECTS);
}
mv.invokeInterface(LOG_BUILDER_TYPE, LOG_AND_GET_METHOD);
}
private void handleTraceExit(LocationMethodVisitor mv, String descriptor) {
final Type[] types = Type.getArgumentTypes(descriptor);
final int[] vars = new int[types.length];
for (int i = vars.length - 1; i >= 0; i--) {
vars[i] = mv.nextLocal();
mv.storeLocal(vars[i]);
}
// only Logger on stack
mv.dup();
final int loggerIdx = mv.nextLocal();
mv.storeLocal(loggerIdx, LOGGER_TYPE);
mv.invokeInterface(LOGGER_TYPE, AT_TRACE_METHOD);
mv.storeLocation();
mv.getStatic(ABSTRACT_LOGGER_TYPE, EXIT_MARKER, MARKER_TYPE);
mv.invokeInterface(LOG_BUILDER_TYPE, WITH_MARKER_METHOD);
mv.loadLocal(loggerIdx, LOGGER_TYPE);
if (types.length == 0) {
mv.push((String) null);
mv.push((String) null);
mv.invokeSupplierLambda(SupplierLambdaType.EXIT_MESSAGE_STRING_OBJECT);
} else if (Arrays.deepEquals(types, MESSAGE_OBJECT_ARRAY)) {
// Invert arguments
mv.loadLocal(vars[1]);
mv.loadLocal(vars[0]);
mv.invokeSupplierLambda(SupplierLambdaType.EXIT_MESSAGE_OBJECT_MESSAGE);
} else if (ENTRY_MESSAGE_TYPE.equals(types[0])) {
final boolean hasResult = types.length == 2;
if (hasResult) {
mv.loadLocal(vars[1]);
}
mv.loadLocal(vars[0]);
mv.invokeSupplierLambda(
hasResult
? SupplierLambdaType.EXIT_MESSAGE_OBJECT_ENTRY_MESSAGE
: SupplierLambdaType.EXIT_MESSAGE_ENTRY_MESSAGE);
} else {
final boolean hasFormat = STRING_TYPE.equals(types[0]);
if (hasFormat) {
mv.loadLocal(vars[0]);
} else {
mv.push((String) null);
}
mv.loadLocal(vars[hasFormat ? 1 : 0], OBJECT_TYPE);
mv.invokeSupplierLambda(SupplierLambdaType.EXIT_MESSAGE_STRING_OBJECT);
}
mv.invokeInterface(LOG_BUILDER_TYPE, LOG_BUILDER_LOG_SUPPLIER_METHOD);
// except void methods traceExit() and traceExit(EntryMessage)
if (types.length != 0 && (types.length > 1 || !ENTRY_MESSAGE_TYPE.equals(types[0]))) {
mv.loadLocal(vars[vars.length - 1]);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy