rjb
rjb

Reputation: 11

Java: Is it possible (at runtime) to obtain source filename and line number of a future method call?

I'm working on a project at work, and I've been tasked with implementing a custom Java Swing library that logs an event to a file whenever a Swing component is interacted with by a user. In other words, whenever a user interacts with a button, check box, etc on a GUI, the event is logged to a file. The log entry must contain the source filename and line number of where the event occurred. There's one catch: the logging mechanism needs to happen automatically in the library for a method implemented by the developer outside of the library. Can it be done? Here's what I have so far:

Custom library action listener:

public abstract class MyActionListener implements ActionListener
{
    // Prevents the developer from implementing the "actionPerformed" method
    @Override
    public final void actionPerformed(ActionEvent e)
    {
        // Do nothing
    }

    // Custom method
    public void actionPerformed(MyActionEvent e)
    {
        doCustomMethod();

        // Tried obtaining the stack trace here but there's no entry
        // in the trace for "doCustomMethod()"
        StackTraceElement [] elements = Thread.currentThread().getStackTrace();
        for (StackTraceElement element : elements)
        {
            // print each element
        }
    }

    // Forces the developer to implement this method
    public abstract void doCustomMethod();
}

Custom library button:

public class MyButton extends JButton
{
    ...

    // Custom method
    public void addActionListener(MyActionListener l)
    {
        listenerList.add(MyActionListener.class, l);
    }
}

Developer created GUI:

public class TestGui extends JFrame
{
    TestGui()
    {
        ...

        MyButton button = new MyButton("Push");
        button.addActionListener(new MyActionListener()
        {
            public void doCustomMethod()
            {
                // Want to log this line number and file name,
                // but need the library to do it; NOT the developer.
            }
        });
    }
}

I've already got all the proper logic in place so that the correct addActionListener() and actionPerformed() methods are called. My problem is trying to log the events. I've also tried implementing custom Java annotations, but it didn't work. Plus, the developer has to include the -processor option in building the GUI. We are trying to have the developers use our custom Swing library just like the Java Swing library. The changes I implement would be transparent to the developer.

Any help is greatly appreciated.

Upvotes: 1

Views: 169

Answers (3)

benmmurphy
benmmurphy

Reputation: 2505

If you can force them to call a method in their doCustomMethod() implementation then you can access the line number from the stack trace information. Otherwise, you can use this.getClass() to find out the Class name of their implementation and use ASM ClassReader to find out the line number / source file.

Upvotes: 0

richarbernal
richarbernal

Reputation: 1059

Maybe Logback + SLF4J can help you

First put logback-classic.jar, logback-core.jar and slf4j-api in your classpath

There's a default configuration if none is present, but if you want more detailed configuration you can add a logback.xml config file with something like this:

<?xml version="1.0" encoding="UTF-8" ?>

<configuration scan="true" scanPeriod="60 seconds">

<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <layout class="ch.qos.logback.classic.PatternLayout">
        <pattern>[%d{ISO8601}] [%-5level] \(%F:%L\) - %msg%n</pattern>
    </layout>
</appender>

<logger name="org.hibernate" level="WARN" />
<logger name="org.springframework" level="INFO" />

<root level="INFO">
    <appender-ref ref="CONSOLE" />
</root>

</configuration>

Upvotes: 1

Xeon
Xeon

Reputation: 5989

What about traversing Component tree and adding your listeners to each component (JButton, etc. - that can interact with user and you need it)?

Then developer could use simple JButton (not MyButton). But when you use this approach - you should invoke traversing components method after all components are created. If other components will be added you could add also ComponentListener that listens to Component#add methods and update your listeners.

Upvotes: 1

Related Questions