Bytebuddy - Intercept java.lang.RuntimeException constructor - java

Iam trying to intercept the constructor RuntimeException(String). Iam trying to use Advice as mentioned here and shown here. But the methods onEnter(String message) or onExit(String message). My instrumenting class (inside a different jar):
public class Instrumenting {
private static final String CLASS_NAME = "java.lang.RuntimeException";
public static void instrument(Instrumentation instrumentation) throws Exception {
System.out.println("[Instrumenting] starting to instrument '" + CLASS_NAME + "'");
instrumentation.appendToBootstrapClassLoaderSearch(new JarFile("C:\\Users\\Moritz\\Instrumenting\\dist\\Instrumenting.jar"));
File temp = Files.createTempDirectory("tmp").toFile();
ClassInjector.UsingInstrumentation.of(temp, ClassInjector.UsingInstrumentation.Target.BOOTSTRAP, instrumentation).inject(Collections.singletonMap(
new TypeDescription.ForLoadedType(RuntimeExceptionIntercept.class),
ClassFileLocator.ForClassLoader.read(RuntimeExceptionIntercept.class)));
new AgentBuilder.Default()
.ignore(ElementMatchers.none())
.with(new AgentBuilder.InjectionStrategy.UsingInstrumentation(instrumentation, temp))
.type(ElementMatchers.named(CLASS_NAME))
.transform((DynamicType.Builder<?> builder, TypeDescription td, ClassLoader cl, JavaModule jm) ->
builder
.visit(Advice.to(RuntimeExceptionIntercept.class)
.on(ElementMatchers.isConstructor())
)
).installOn(instrumentation);
System.out.println("[Instrumenting] done");
}
public static class RuntimeExceptionIntercept {
#Advice.OnMethodEnter
public static void onEnter(String message) throws Exception {
System.err.println("onEnter: " + message);
}
#Advice.OnMethodExit
public static void onExit(String message) throws Exception {
System.err.println("onExit: " + message);
}
}
}
How its called:
public class Main {
public static void premain(String agentArgs, Instrumentation instrumentation) throws Exception {
Instrumenting.instrument(instrumentation);
}
public static void main(String[] args) throws MalformedURLException, IOException {
new RuntimeException("message");
}
}
Output:
[Instrumenting] starting to instrument 'java.lang.RuntimeException'
[Instrumenting] done
What am I doing wrong?

The class is already loaded when your agent is running and you have not specified for example RedefinitionStrategy.RETRANSFORM. Therefore, your agent will not reconsider already loaded classes. Note that you should set a more specific ignore matcher then that.
By the way, advice is inlined in the target class, your injection is not required.

Related

Class Retransformation with Bytebuddy Agent

I am writing a Java agent with ByteBuddy API. Therefore, I want to get in touch with the method delegation of classes that are alredy loaded using the retransformation capabilities of the Bytebuddy DSL. When I start the application with the -javaagent parameter everything works fine and the console output gets changed but when attaching the java agent at runtime the agentmain method is executed but the console output does not get changed. Maybe im missing some further ByteBuddy configuration. Any help would be appreciate!
Here is the agent code :
public class AgentMain {
private static final String CLASS = "testing.Test";
private static final String METHOD = "doSomething";
public static void premain(String agentArgs, Instrumentation instrumentation) {
System.out.println("premain...");
new AgentBuilder.Default()
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.type(ElementMatchers.named(INSTRUMENTED_CLASS))
.transform(new AgentBuilder.Transformer() {
#Override
public DynamicType.Builder transform(
DynamicType.Builder builder,
TypeDescription typeDescription,
ClassLoader classLoader,
JavaModule javaModule
)
{
return builder.method(named(INTERCEPTED_METHOD))
.intercept(MethodDelegation.to(Interceptor.class));
}
}).installOn(instrumentation);
}
public static void agentmain(String agentArgs, Instrumentation instrumentation) {
System.out.println("Running agentmain...");
new AgentBuilder.Default()
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.type(ElementMatchers.named(INSTRUMENTED_CLASS))
.transform(new AgentBuilder.Transformer() {
#Override
public DynamicType.Builder transform(
DynamicType.Builder builder,
TypeDescription typeDescription,
ClassLoader classLoader,
JavaModule javaModule
)
{
return builder.method(named(INTERCEPTED_METHOD))
.intercept(MethodDelegation.to(Interceptor.class));
}
}).installOn(instrumentation);
}
}
public class Interceptor {
public static void doSomething(String string) throws Exception {
System.out.println("Intercepted! ");
}
}
Here is the application code :
public class Main {
public static void main(String args[]) throws Exception {
while (true) {
Thread.sleep(1000);
String say = "Not intercepted!";
Test test = new Test();
test.doSomething(say);
}
}
}
Here is the code to attach the agent:
public class Attacher {
public static void attach(String jarFile, String pid) {
try {
ByteBuddyAgent.attach(new File(jarFile), pid);
}
catch (Exception e) {
e.printStackTrace();
}
}
}
You probably need to add .disableClassFormatChanges() as most JVMs do not support changing the shape of classes upon a retransformation.
Also, consider registering an AgentBuilder.Listener to see why a class cannot be transformed. The instrumentation API suppresses all errors otherwise.
Normally, when retransforming, the Advice API is better suited for transformation. It supports most features of the delegation API but works slightly different.

How to advice constructor of original class after define a field using bytebuddy

I am trying to define a field to a class and use it with advice. I try this with normal methods but i can't use this with constructor.I try using
.constructor(ElementMatchers.any())
.intercept(SuperMethodCall.INSTANCE.andThen(MethodDelegation.to(MethodListener.class)))
but then advice not running. I have code as follows..
Agent
new AgentBuilder.Default()
.with(new AgentBuilder.InitializationStrategy.SelfInjection.Eager())
.type(ElementMatchers.nameContains("BalConnectorCallback"))
.transform((builder, typeDescription, classLoader, module) -> builder
.defineField("contextTimer", Timer.Context.class)
.method(ElementMatchers.any())
.intercept(Advice.to(MethodListener.class))
).installOn(instrumentation);
This is my advice
public class MethodListener {
#Advice.OnMethodEnter
public static void enter(#Advice.Origin String method,
#Advice.AllArguments Object[] para,
#Advice.FieldValue(value = "contextTimer", readOnly = false) Timer.Context contextTimer)
throws Exception {
if (getMethodName(method).equals("BalConnectorCallback")) {
contextTimer = metricServer.getResponsesTime().start();
}
}
#Advice.OnMethodExit
public static void exit(#Advice.Origin String method,
#Advice.FieldValue("contextTimer") Timer.Context contextTimer)
throws Exception {
if (getMethodName(method).equals("done")) {
contextTimer.stop();
}
}
}
How to advice constructor with define a field?
Hi got this problem corrected by using
.constructor(ElementMatchers.any())
.intercept(Advice.to(ConstructorAdvice.class))
and Created a another Advice for constructor as follows
public class ConstructorAdvice {
#Advice.OnMethodExit
public static void enter(#Advice.Origin String method,
#Advice.AllArguments Object[] para,
#Advice.FieldValue(value = "contextTimer", readOnly = false) Timer.Context contextTimer)
throws Exception {
if (getMethodName(method).equals("BalConnectorCallback")) {
contextTimer = metricServer.getResponsesTime().start();
}
}

ThreadWeaver always throws IllegalArgumentException

I am trying to use Google ThreadWeaver to write a unit test for concurrent code. No matter what I do, I will get an IllegalArgumentException. I am still working with an example, but even that does not work. This is what I tried:
public class ExampleTest {
public static class ExampleMain implements MainRunnable<Example> {
private Example example;
#Override
public Class<Example> getClassUnderTest() {
return Example.class;
}
#Override
public String getMethodName() {
return null;
}
#Override
public Method getMethod() throws NoSuchMethodException {
return null;
}
#Override
public void initialize() throws Exception {
example = new Example();
}
#Override
public Example getMainObject() {
return example;
}
#Override
public void terminate() throws Exception {
}
#Override
public void run() throws Exception {
example.test("second");
}
}
public static class ExampleSecondary implements SecondaryRunnable<Example, ExampleMain> {
private ExampleMain exampleMain;
#Override
public void initialize(ExampleMain main) throws Exception {
exampleMain = main;
}
#Override
public void terminate() throws Exception {
}
#Override
public boolean canBlock() {
return false;
}
#Override
public void run() throws Exception {
exampleMain.getMainObject().test("main");
}
}
public static class Example {
private List<String> list = new ArrayList<String>();
public String test(String s) {
System.out.println("1" + s);
list.add(s);
System.out.println("2" + s);
return list.get(0);
}
}
#Test
public void testThreadWeaver() throws Exception {
ClassInstrumentation instrumentation = Instrumentation.getClassInstrumentation(Example.class);
Method tested = Example.class.getDeclaredMethod("test", String.class);
Method breakpoint = List.class.getDeclaredMethod("add", Object.class);
CodePosition codePosition = instrumentation.afterCall(tested, breakpoint);
InterleavedRunner.interleave(new ExampleMain(), new ExampleSecondary(), Arrays.asList(codePosition)).throwExceptionsIfAny();
}
}
The stack trace says:
java.lang.IllegalArgumentException: Class Example is not instrumented
at
com.google.testing.threadtester.CallLoggerFactory.getClassInstrumentation(CallLoggerFactory.java:108)
at
com.google.testing.threadtester.Instrumentation.getClassInstrumentation(Instrumentation.java:65)
at MyTest.testThreadWeaver(MyTest.java:92
I followed the instructions at the official Google code webpage, but it does not seem to work. Any ideas?
ThreadWeaver needs to instrument your classes in order to add breakpoints to your methods. Therefore, you cannot run the tests with JUnit directly but you must run your test from a specific test runner. For your case this would be ThreadedTestRunner. The actual test methods must then be annotated with #ThreadedTest instead of #Test. This should work:
#Test
public void startTest() throws Exception {
new ThreadedTestRunner().runTests(getClass(), Example.class);
}
#ThreadedTest
public void testThreadWeaver() throws Exception {
// here comes your test
}

How to move methods to AspectJ class?

In a simple RMI program I managed to pass Context between two Threads. Now I need to move setting/reporting from Context to AspectJ class.
My problem is: How to move Context if I need to use it as an argument in greeting(Context)
HelloIF
public interface HelloIF extends Remote {
String greeting(Context c) throws RemoteException;
}
Hello
public class Hello extends UnicastRemoteObject implements HelloIF {
public Hello() throws RemoteException {
}
public String greeting(Context c) throws RemoteException {
c.report();
return "greeting";
}
}
RMIServer
public class RMIServer {
public static void main(String[] args) throws RemoteException, MalformedURLException {
LocateRegistry.createRegistry(1099);
HelloIF hello = new Hello();
Naming.rebind("server.Hello", hello);
System.out.println("server.RMI Server is ready.");
}
}
RMIClient
public class RMIClient {
public static void main(String[] args) throws RemoteException, MalformedURLException, NotBoundException {
Context context = new Context("request1", Thread.currentThread().getName()+System.currentTimeMillis());
Registry registry = LocateRegistry.getRegistry("localhost");
HelloIF hello = (HelloIF) registry.lookup("server.Hello");
System.out.println(hello.greeting(context));
context.report();
}
}
Context
public class Context implements Serializable
{
private String requestType;
private String distributedThreadName;
public Context(String requestType, String distributedThreadName)
{
this.requestType = requestType;
this.distributedThreadName = distributedThreadName;
}
(...)
public void report() {
System.out.println("thread : "
+ Thread.currentThread().getName() + " "
+ Thread.currentThread().getId());
System.out.println("context : "
+ this.getDistributedThreadName() + " " + this.getRequestType());
}
}
and finally an empty AspectJ class
#Aspect
public class ReportingAspect {
#Before("call(void main(..))")
public void beforeReportClient(JoinPoint joinPoint) throws Throwable {
}
#After("call(void main(..))")
public void afterReportClient(JoinPoint joinPoint) throws Throwable {
}
#Before("call(String greeting(..))")
public void beforeReportGreeting(JoinPoint joinPoint) throws Throwable {
}
#After("call(String greeting(..))")
public void afterReportGreeting(JoinPoint joinPoint) throws Throwable {
}
}
How can I move from Hello and RMIClient Context() constructor and c/context.report()s to ReportingAspect?
You can pass the arguments to a function, and the underlying object, to Advice, thus:
#Before("execution(* greeting(..)) && target(target) && " +
"args(context)")
public void beforeReportGreeting(HelloIF target, Context context) {
context.doSomething();
target.doSomething();
}
Study the AspectJ annotation documentation for the full details. It can be done for all the advice types.
Edit Reading the question in more details, it sounds as if you want to make the Context object something constructed and controlled by the aspect, while still passing it as an argument to Hello.greeting().
That's not something that makes sense. Your underlying system ought to work OK without any AOP going on. So if the Context object is part of that underlying domain, then it's not a good idea for the Aspect to be in charge of its construction and management.
If the Context is only relevant to the Aspect, then you would remove all reference to the context from the domain classes (so greeting() would take no parameters) and build the Context object(s) in the Aspect.

Java logger that automatically determines caller's class name

public static Logger getLogger() {
final Throwable t = new Throwable();
final StackTraceElement methodCaller = t.getStackTrace()[1];
final Logger logger = Logger.getLogger(methodCaller.getClassName());
logger.setLevel(ResourceManager.LOGLEVEL);
return logger;
}
This method would return a logger that knows the class it's logging for.
Any ideas against it?
Many years later: https://github.com/yanchenko/droidparts/blob/master/droidparts/src/org/droidparts/util/L.java
The MethodHandles class (as of Java 7) includes a Lookup class that, from a static context, can find and return the name of the current class. Consider the following example:
import java.lang.invoke.MethodHandles;
public class Main {
private static final Class clazz = MethodHandles.lookup().lookupClass();
private static final String CLASSNAME = clazz.getSimpleName();
public static void main( String args[] ) {
System.out.println( CLASSNAME );
}
}
When run this produces:
Main
For a logger, you could use:
private static Logger LOGGER =
Logger.getLogger(MethodHandles.lookup().lookupClass().getSimpleName());
Creating a stack trace is a relatively slow operation. Your caller already knows what class and method it is in, so the effort is wasted. This aspect of your solution is inefficient.
Even if you use static class information, you should not fetch the Logger again for each message. From the author of Log4j,Ceki Gülcü:
The most common error in wrapper classes is the invocation of the Logger.getLogger method on each log request. This is guaranteed to wreak havoc on your application's performance. Really!!!
This is the conventional, efficient idiom for getting a Logger is during class initialization:
private static final Logger log = Logger.getLogger(MyClass.class);
Note that this gives you a separate Logger for each type in a hierarchy. If you come up with a method that invokes getClass() on an instance, you will see messages logged by a base type showing up under the subtype's logger. Maybe this is desirable in some cases, but I find it confusing (and I tend to favor composition over inheritance anyway).
Obviously, using the dynamic type via getClass() will require you to obtain the logger at least once per instance, rather than once per class like the recommended idiom using static type information.
I guess it adds a lot of overhead for every class. Every class has to be 'looked up'. You create new Throwable objects to do that... These throwables don't come for free.
We actually have something quite similar in a LogUtils class. Yes, it's kind of icky, but the advantages are worth it as far as I'm concerned. We wanted to make sure we didn't have any overhead from it being repeatedly called though, so ours (somewhat hackily) ensures that it can ONLY be called from a static initializer context, a la:
private static final Logger LOG = LogUtils.loggerForThisClass();
It will fail if it's invoked from a normal method, or from an instance initializer (i.e. if the 'static' was left off above) to reduce the risk of performance overhead. The method is:
public static Logger loggerForThisClass() {
// We use the third stack element; second is this method, first is .getStackTrace()
StackTraceElement myCaller = Thread.currentThread().getStackTrace()[2];
Assert.equal("<clinit>", myCaller.getMethodName());
return Logger.getLogger(myCaller.getClassName());
}
Anyone who asks what advantage does this have over
= Logger.getLogger(MyClass.class);
has probably never had to deal with someone who copies and pastes that line from somewhere else and forgets to change the class name, leaving you dealing with a class which sends all its stuff to another logger.
Assuming you are keeping static refs to the loggers, here's a standalone static singleton:
public class LoggerUtils extends SecurityManager
{
public static Logger getLogger()
{
String className = new LoggerUtils().getClassName();
Logger logger = Logger.getLogger(className);
return logger;
}
private String getClassName()
{
return getClassContext()[2].getName();
}
}
Usage is nice and clean:
Logger logger = LoggerUtils.getLogger();
For every class that you use this with, you're going to have to look up the Logger anyway, so you might as well just use a static Logger in those classes.
private static final Logger logger = Logger.getLogger(MyClass.class.getName());
Then you just reference that logger when you need to do your log messages. Your method does the same thing that the static Log4J Logger does already so why reinvent the wheel?
A good alternative is to use (one of) the lombok logs annotations :
https://projectlombok.org/features/Log.html
It generate the corresponding log statement with the current class.
Then the best thing is mix of two .
public class LoggerUtil {
public static Level level=Level.ALL;
public static java.util.logging.Logger getLogger() {
final Throwable t = new Throwable();
final StackTraceElement methodCaller = t.getStackTrace()[1];
final java.util.logging.Logger logger = java.util.logging.Logger.getLogger(methodCaller.getClassName());
logger.setLevel(level);
return logger;
}
}
And then in every class:
private static final Logger LOG = LoggerUtil.getLogger();
in code :
LOG.fine("debug that !...");
You get static logger that you can just copy&paste in every class and with no overhead ...
Alaa
From reading through all the other feedback on this site, I created the following for use with Log4j:
package com.edsdev.testapp.util;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.log4j.Level;
import org.apache.log4j.Priority;
public class Logger extends SecurityManager {
private static ConcurrentHashMap<String, org.apache.log4j.Logger> loggerMap = new ConcurrentHashMap<String, org.apache.log4j.Logger>();
public static org.apache.log4j.Logger getLog() {
String className = new Logger().getClassName();
if (!loggerMap.containsKey(className)) {
loggerMap.put(className, org.apache.log4j.Logger.getLogger(className));
}
return loggerMap.get(className);
}
public String getClassName() {
return getClassContext()[3].getName();
}
public static void trace(Object message) {
getLog().trace(message);
}
public static void trace(Object message, Throwable t) {
getLog().trace(message, t);
}
public static boolean isTraceEnabled() {
return getLog().isTraceEnabled();
}
public static void debug(Object message) {
getLog().debug(message);
}
public static void debug(Object message, Throwable t) {
getLog().debug(message, t);
}
public static void error(Object message) {
getLog().error(message);
}
public static void error(Object message, Throwable t) {
getLog().error(message, t);
}
public static void fatal(Object message) {
getLog().fatal(message);
}
public static void fatal(Object message, Throwable t) {
getLog().fatal(message, t);
}
public static void info(Object message) {
getLog().info(message);
}
public static void info(Object message, Throwable t) {
getLog().info(message, t);
}
public static boolean isDebugEnabled() {
return getLog().isDebugEnabled();
}
public static boolean isEnabledFor(Priority level) {
return getLog().isEnabledFor(level);
}
public static boolean isInfoEnabled() {
return getLog().isInfoEnabled();
}
public static void setLevel(Level level) {
getLog().setLevel(level);
}
public static void warn(Object message) {
getLog().warn(message);
}
public static void warn(Object message, Throwable t) {
getLog().warn(message, t);
}
}
Now in your code all you need is
Logger.debug("This is a test");
or
Logger.error("Look what happened Ma!", e);
If you need more exposure to log4j methods, just delegate them from the Logger class listed above.
You could of course just use Log4J with the appropriate pattern layout:
For example, for the class name "org.apache.xyz.SomeClass", the pattern %C{1} will output "SomeClass".
http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/PatternLayout.html
I prefer creating a (static) Logger for each class (with it's explicit class name). I than use the logger as is.
You don't need to create a new Throwable object. You can just call
Thread.currentThread().getStackTrace()[1]
I just have the following line at the beginning of most of my classes.
private static final Logger log =
LoggerFactory.getLogger(new Throwable().getStackTrace()[0].getClassName());
yes there is some overhead the very first time an object of that class is created, but I work mostly in webapps, so adding microseconds onto a 20 second startup isn't really a problem.
Google Flogger logging API supports this e.g.
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
See https://github.com/google/flogger for more details.
A nice way to do this from Java 7 onwards:
private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
The logger can be static and that fine.
Here its using the SLF4J API
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
But in principal can be used with any logging framework. If the logger needs a string argument add toString()
Simple and trivial OLD SCHOOL:
Just create your own class and pass there class name, method name + comment (if class /method changed they're refactored automatically Shift+F6)
public class MyLogs {
public static void LOG(String theClass, String theMethod, String theComment) {
Log.d("MY_TAG", "class: " + theClass + " meth : " + theMethod + " comm : " + theComment);
}
}
and just use it anywhere in the app (no context required, no initialzation, no extra libs and no look up) - can be used for any programing language!
MyLogs.LOG("MainActivity", "onCreate", "Hello world");
this will print in your console:
MY_TAG class: MainActivity meth: onCreate comm: Hello world
Why not?
public static Logger getLogger(Object o) {
final Logger logger = Logger.getLogger(o.getClass());
logger.setLevel(ResourceManager.LOGLEVEL);
return logger;
}
And then when you need a logger for a class:
getLogger(this).debug("Some log message")
This mechanism puts in a lot of extra effort at runtime.
If you use Eclipse as your IDE, consider using Log4e. This handy plugin will generate logger declarations for you using your favourite logging framework. A fraction more effort at coding time, but much less work at runtime.
Unless you really need your Logger to be static, you could use
final Logger logger = LoggerFactory.getLogger(getClass());
Please see my static getLogger() implementation (use same "sun.*" magic on JDK 7 as default java Logger doit)
note static logging methods (with static import) without ugly log property...
import static my.pakg.Logger.*;
And their speed is equivalent to native Java implementation (checked with 1 million of log traces)
package my.pkg;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.IllegalFormatException;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import sun.misc.JavaLangAccess;
import sun.misc.SharedSecrets;
public class Logger {
static final int CLASS_NAME = 0;
static final int METHOD_NAME = 1;
// Private method to infer the caller's class and method names
protected static String[] getClassName() {
JavaLangAccess access = SharedSecrets.getJavaLangAccess();
Throwable throwable = new Throwable();
int depth = access.getStackTraceDepth(throwable);
boolean lookingForLogger = true;
for (int i = 0; i < depth; i++) {
// Calling getStackTraceElement directly prevents the VM
// from paying the cost of building the entire stack frame.
StackTraceElement frame = access.getStackTraceElement(throwable, i);
String cname = frame.getClassName();
boolean isLoggerImpl = isLoggerImplFrame(cname);
if (lookingForLogger) {
// Skip all frames until we have found the first logger frame.
if (isLoggerImpl) {
lookingForLogger = false;
}
} else {
if (!isLoggerImpl) {
// skip reflection call
if (!cname.startsWith("java.lang.reflect.") && !cname.startsWith("sun.reflect.")) {
// We've found the relevant frame.
return new String[] {cname, frame.getMethodName()};
}
}
}
}
return new String[] {};
// We haven't found a suitable frame, so just punt. This is
// OK as we are only committed to making a "best effort" here.
}
protected static String[] getClassNameJDK5() {
// Get the stack trace.
StackTraceElement stack[] = (new Throwable()).getStackTrace();
// First, search back to a method in the Logger class.
int ix = 0;
while (ix < stack.length) {
StackTraceElement frame = stack[ix];
String cname = frame.getClassName();
if (isLoggerImplFrame(cname)) {
break;
}
ix++;
}
// Now search for the first frame before the "Logger" class.
while (ix < stack.length) {
StackTraceElement frame = stack[ix];
String cname = frame.getClassName();
if (isLoggerImplFrame(cname)) {
// We've found the relevant frame.
return new String[] {cname, frame.getMethodName()};
}
ix++;
}
return new String[] {};
// We haven't found a suitable frame, so just punt. This is
// OK as we are only committed to making a "best effort" here.
}
private static boolean isLoggerImplFrame(String cname) {
// the log record could be created for a platform logger
return (
cname.equals("my.package.Logger") ||
cname.equals("java.util.logging.Logger") ||
cname.startsWith("java.util.logging.LoggingProxyImpl") ||
cname.startsWith("sun.util.logging."));
}
protected static java.util.logging.Logger getLogger(String name) {
return java.util.logging.Logger.getLogger(name);
}
protected static boolean log(Level level, String msg, Object... args) {
return log(level, null, msg, args);
}
protected static boolean log(Level level, Throwable thrown, String msg, Object... args) {
String[] values = getClassName();
java.util.logging.Logger log = getLogger(values[CLASS_NAME]);
if (level != null && log.isLoggable(level)) {
if (msg != null) {
log.log(getRecord(level, thrown, values[CLASS_NAME], values[METHOD_NAME], msg, args));
}
return true;
}
return false;
}
protected static LogRecord getRecord(Level level, Throwable thrown, String className, String methodName, String msg, Object... args) {
LogRecord record = new LogRecord(level, format(msg, args));
record.setSourceClassName(className);
record.setSourceMethodName(methodName);
if (thrown != null) {
record.setThrown(thrown);
}
return record;
}
private static String format(String msg, Object... args) {
if (msg == null || args == null || args.length == 0) {
return msg;
} else if (msg.indexOf('%') >= 0) {
try {
return String.format(msg, args);
} catch (IllegalFormatException esc) {
// none
}
} else if (msg.indexOf('{') >= 0) {
try {
return MessageFormat.format(msg, args);
} catch (IllegalArgumentException exc) {
// none
}
}
if (args.length == 1) {
Object param = args[0];
if (param != null && param.getClass().isArray()) {
return msg + Arrays.toString((Object[]) param);
} else if (param instanceof Throwable){
return msg;
} else {
return msg + param;
}
} else {
return msg + Arrays.toString(args);
}
}
public static void severe(String msg, Object... args) {
log(Level.SEVERE, msg, args);
}
public static void warning(String msg, Object... args) {
log(Level.WARNING, msg, args);
}
public static void info(Throwable thrown, String format, Object... args) {
log(Level.INFO, thrown, format, args);
}
public static void warning(Throwable thrown, String format, Object... args) {
log(Level.WARNING, thrown, format, args);
}
public static void warning(Throwable thrown) {
log(Level.WARNING, thrown, thrown.getMessage());
}
public static void severe(Throwable thrown, String format, Object... args) {
log(Level.SEVERE, thrown, format, args);
}
public static void severe(Throwable thrown) {
log(Level.SEVERE, thrown, thrown.getMessage());
}
public static void info(String msg, Object... args) {
log(Level.INFO, msg, args);
}
public static void fine(String msg, Object... args) {
log(Level.FINE, msg, args);
}
public static void finer(String msg, Object... args) {
log(Level.FINER, msg, args);
}
public static void finest(String msg, Object... args) {
log(Level.FINEST, msg, args);
}
public static boolean isLoggableFinest() {
return isLoggable(Level.FINEST);
}
public static boolean isLoggableFiner() {
return isLoggable(Level.FINER);
}
public static boolean isLoggableFine() {
return isLoggable(Level.FINE);
}
public static boolean isLoggableInfo() {
return isLoggable(Level.INFO);
}
public static boolean isLoggableWarning() {
return isLoggable(Level.WARNING);
}
public static boolean isLoggableSevere() {
return isLoggable(Level.SEVERE);
}
private static boolean isLoggable(Level level) {
return log(level, null);
}
}
Take a look at Logger class from jcabi-log. It does exactly what you're looking for, providing a collection of static methods. You don't need to embed loggers into classes any more:
import com.jcabi.log.Logger;
class Foo {
public void bar() {
Logger.info(this, "doing something...");
}
}
Logger sends all logs to SLF4J, which you can redirect to any other logging facility, in runtime.

Categories