1

I have written a custom MyLogger library based on Observer design pattern. What I am trying to achieve is this: Every time I call writeLog(LOG_LEVEL,"Text") method I want it to execute in a new thread. Can someone please suggest what will be the way to achieve this. As in where shall I create threads.

This is how my Logger call looks.

public class Logger extends Subject{
     void writeLog(String type, String message)
        {       setData(message);
                notifyy(type);
        }

}

And this is how I am calling writeLog

appLogger.writeLog("ERROR", "This is error");
Andy897
  • 6,915
  • 11
  • 51
  • 86
  • Uh, why don't you use a producer/consumer model instead? – fge Jan 22 '15 at 08:58
  • Some more discussion about asynchronous logging over at http://stackoverflow.com/questions/17018420/asynchronous-logging – Thilo Jan 22 '15 at 09:05

2 Answers2

1

You can use an ExecutorService

// Somewhere in your logging framework
ExecutorService service = Executors.new...(); // Choose the one you want
...

public MessageRelayingTask implements Runnable {
    // private fields
    ...

    public MessageRelayingTask(String type, String message) {
         ...
    }

    @Override
    public void run() {
        setData(message);
        notifyy(type);
    }     
}

public class Logger extends Subject implements Runnable {
    void writeLog(String type, String message) {
        service.submit(new MessageRelayingTask(type, message));
    }
}

Some pointers to get you started:

Stephan
  • 41,764
  • 65
  • 238
  • 329
  • Thanks a lot for replying Stephan. Are there any alternative ways as well ? If yes can you please suggest advantages of doing it one way over other. – Andy897 Jan 22 '15 at 09:04
  • But why a new thread for every message? Seems like a lot of overhead. Wouldn't a single background thread that takes from a shared queue be enough? – Thilo Jan 22 '15 at 09:04
  • @Stephan .. There is some trivial problem in above code. "message" and "type" inside run() .. how do we pass them there ? CUrrently "message can not be resolved to a variable" error – Andy897 Jan 22 '15 at 09:06
  • @Thilo It's a simple approach... Just to get OP started. There is not enough room here to describe a full system. – Stephan Jan 22 '15 at 09:06
  • @Thilo. Didn't get you .. Can I please request you to elaborate a bit when you say "Wouldn't a single background thread that takes from a shared queue be enough?". I am new to concurrency. – Andy897 Jan 22 '15 at 09:06
  • @Andy897: take a look at http://stackoverflow.com/questions/17018420/asynchronous-logging – Thilo Jan 22 '15 at 09:07
  • @Andy897 Alternatively, you should use an ExecutorService – Stephan Jan 22 '15 at 09:07
  • @Stephan Thanks. And anything on "message can not be resolved to a variable" error in the version that you shared. – Andy897 Jan 22 '15 at 09:10
1

You could use the Producer-Consumer-Pattern like this:

public class Logger {

    /**
     * The ExecutorService runs the Thread that processes the logs.
     */
    private final ExecutorService loggingService = Executors.newSingleThreadExecutor();

    /**
     * A queue that contains the logs.
     */
    final BlockingQueue<YourLogObject> logs = new LinkedBlockingQueue<>();

    /**
     * Creates a new Logger object and starts the Thread that processes the logs.
     */
    public Logger() {
        loggingService.submit(new Runnable() {

            @Override
            public void run() {
                try {
                    for (;;) {
                        final YourLogObject log = logs.take();

                        // setData(log.getMessage());
                        // notify(log.getLevel());
                    }
                } catch (final InterruptedException e) {
                    e.printStackTrace();
                    return;
                }
            }

        });
    }

    /**
     * Creates a new YourLogObject from the given parameters and puts it at the end of the queueu.
     */
    public void writeLog(final String level, final String message) {
        final YourLogObject log = new YourLogObject(level, message);

        try {
            logs.put(log);
        } catch (final InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 
     */
    public void shutdown() {
        loggingService.shutdownNow();
    }

}

public class YourLogObject {

    private final String level;

    private final String message;

    public YourLogObject(final String level, final String message) {
        this.level = level;
        this.message = message;
    }

    public String getLevel() {
        return level;
    }

    public String getMessage() {
        return message;
    }

}
stevecross
  • 5,588
  • 7
  • 47
  • 85
  • Thanks for replying. Can you please suggest how is this conceptually different from Stephan's answer – Walt Jan 22 '15 at 10:37
  • With this solution you have only one background-thread, that runs all the time. You have one message-queue. The method `writeLog()` puts new logs into this queue and the background-thread continously takes messages out of the queue. In Stephan's approach one thread is created per call to `writeLog()`. This might be much overhead. – stevecross Jan 22 '15 at 13:02