Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
975 views
in Technique[技术] by (71.8m points)

variables - How to automatically print all parameter values of Java function at runtime

I want to print automatically all the parameter values of my functions at runtime.

Just imagin that I have the following two methods:

public void doAction(String firstParam, String SecondParam) {
    Util.printAllParameter(this);
}

public void doAction(String firstParam) {
    Util.printAllParameter(this);
}

If I call to this functions:

doAction("a", "b"); --> Desired result: Print "a, b"

doAction("a"); --> Desired result: Print "a"

I don't want something like this (This is not reusable, it is static):

System.out.println(firstParam + "," + SecondParam);

I need a reusable method that I can use in different functions with different number of parameter. I want to call a function like "Util.printAllParameter()" and then print all the parameters.

Thanks in advance.

question from:https://stackoverflow.com/questions/65899005/how-to-automatically-print-all-parameter-values-of-java-function-at-runtime

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

To do it generally would require rewriting the bytecode (probably with a Java Agent, or library using it) or the source code.

The way do it without hacking the code is to use an interface and a Proxy. Interfaces are often suggested, but Java gets in the way with its old fashioned, super verbose syntax.

import java.io.*;
import java.lang.reflect.*;
import java.util.*;
import java.util.stream.*;

// Gratuitous use of new fangled record feature and streams.
record TraceInvocation(PrintStream out) {
    public <T> T trace(Class<T> type, T target) {
        Objects.requireNonNull(target);
        return type.cast(Proxy.newProxyInstance(
            type.getClassLoader(),
            new Class<?>[] { type },
            (proxy, method, args) -> {
                // Apparently args can be null. Ffs.
                out.println(
                    (target==null ? type.getSimpleName() : escape(target))+
                    "."+method.getName()+
                    // There's probably a better way without {}.
                    "("+(args==null ? "" : String.join(", ", 
                        Stream.of(args)
                            .map(TraceInvocation::escape)
                            .toArray(String[]::new)
                    ))+")"
                );
                return method.invoke(target, args);
            }
        ));
    }
    // Don't even think about allowing log injection.
    // (Okay, weird syntax.)
    private static String escape(Object object) {
        // I am not a fan of streams.
        int[] escaped = String.valueOf(object).codePoints().flatMap(cp ->
            (cp == '\' || cp == '.' || cp == ',') ?
                IntStream.of('\', cp)             :
            (' ' <= cp && cp <= '~'              ) ?
                IntStream.of(cp)                   :
            ("\"+/*(int)*/cp+"\").codePoints()
        ).toArray();
        return new String(escaped, 0, escaped.length);
    }
}

Use as:

    CharSequence cs = new TraceInvocation(System.err)
        .trace(CharSequence.class, "Scunthorpe");
    cs.subSequence(4, 10).length(); // No log for length
    cs.charAt(2);
    cs.length();

Possible variation include filtering which methods to display, logging return values/exceptions, alternative to toString and tracing returned values.

I found this approach really useful when dealing with sending and receiving a stream in a proprietary format.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...