// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

package com.yahoo.search.logging;

import org.slf4j.LoggerFactory;

import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

abstract class AbstractThreadedLogger implements Logger {

    private final static org.slf4j.Logger log = LoggerFactory.getLogger(AbstractThreadedLogger.class);

    final static int DEFAULT_MAX_THREADS = 1;
    final static int DEFAULT_QUEUE_SIZE = 1000;

    private final WorkerThreadExecutor executor;

    AbstractThreadedLogger() {
        this(DEFAULT_MAX_THREADS, DEFAULT_QUEUE_SIZE);
    }

    AbstractThreadedLogger(int threads, int queueSize) {
        executor = new WorkerThreadExecutor(threads, queueSize);
    }

    AbstractThreadedLogger(int threads, int queueSize, ThreadFactory factory) {
        executor = new WorkerThreadExecutor(threads, queueSize, factory);
    }

    @Override
    public boolean send(LoggerEntry entry) {
        return enqueue(entry);
    }

    protected boolean enqueue(LoggerEntry entry) {
        // Todo: metric things
        try {
            executor.execute(() -> dequeue(entry));
        } catch (RejectedExecutionException e) {
            return false;
        }
        return true;
    }

    protected void dequeue(LoggerEntry entry) {
        transport(entry);  // This happens in worker thread
    }

    /**
     * Actually transports the entry to it's destination
     */
    abstract void transport(LoggerEntry entry);


    private static class WorkerThread extends Thread {

        public WorkerThread(Runnable r) {
            super(r);
        }

        @Override
        public void run() {
            try {
                super.run();
            } catch (Exception e) {
                log.error(String.format("Error while sending logger entry: %s", e), e);
            }
        }

    }

    private static class WorkerThreadExecutor implements Executor {

        protected final ThreadPoolExecutor executor;

        WorkerThreadExecutor(int threads, int queueSize) {
            this(threads, queueSize, WorkerThread::new);
        }

        WorkerThreadExecutor(int threads, int queueSize, ThreadFactory threadFactory) {
            executor = new ThreadPoolExecutor(
                    threads, threads,
                    0L, TimeUnit.SECONDS,
                    new LinkedBlockingQueue<>(queueSize),
                    threadFactory);
        }

        public void close() {
            try {
                executor.shutdown();
                executor.awaitTermination(5, TimeUnit.SECONDS);
            } catch (InterruptedException e) {
                //
            } finally {
                executor.shutdownNow();
            }
        }

        @Override
        public void execute(Runnable r) {
            executor.execute(r);
        }

    }

}

@@ -138,41 +138,8 @@
 g;%0A%0A
-import org.slf4j.LoggerFactory;%0A%0A
 impo
@@ -164,32 +164,32 @@
 rrent.Executor;%0A
+
 import java.util
@@ -405,16 +405,48 @@
 imeUnit;
+%0Aimport java.util.logging.Level;
 %0A%0Aabstra
@@ -527,44 +527,63 @@
 tic 
-org.slf4j.Logger log = LoggerFactory
+java.util.logging.Logger log = java.util.logging.Logger
 .get
@@ -617,16 +617,26 @@
 er.class
+.getName()
 );%0A%0A    
@@ -728,29 +728,31 @@
 000;%0A%0A    pr
-ivate
+otected
  final Worke
@@ -2041,14 +2041,26 @@
 log.
-error(
+log(Level.SEVERE, 
 Stri
@@ -2147,37 +2147,39 @@
 %7D%0A%0A    %7D%0A%0A    pr
-ivate
+otected
  static class Wo

Use correct logger and allow executor access from subclasses