class AlertThreadGroup extends ThreadGroup {
    protected int count;
    protected LinkedList<Logger> loggers = new LinkedList<Logger>(); 
    static protected ThreadLocal<Logger> threadLogger = new ThreadLocal<Logger>();
    
    public AlertThreadGroup(String name) {
        super(name);
    }

    public int getAlertCount() {
        return count;
    }

    public void resetAlertCount() {
        count = 0;
    }
    
    public void addLogger(Logger logger) {
        loggers.add(logger);
    }
    
    /** set alternate temporary alert logger */
    public static void setThreadLogger(Logger logger) {
        threadLogger.set(logger); 
    }

    public static AlertThreadGroup current() {<FILL_FUNCTION_BODY>}

    public static void publishCurrent(LogRecord record) {
        AlertThreadGroup atg = AlertThreadGroup.current();
        if (atg == null) {
            Logger tlog = threadLogger.get(); 
            if(tlog!=null) {
                // send to temp-registered logger
                boolean usePar = tlog.getUseParentHandlers();
                tlog.setUseParentHandlers(false);
                tlog.log(record);
                tlog.setUseParentHandlers(usePar);
            }
            return;
        }
        atg.publish(record);
    }

    /**
     * Pass a record to all loggers registered with the 
     * AlertThreadGroup. Adds thread info to the message, 
     * if available.
     * 
     * @param record
     */
    public void publish(LogRecord record) {
        String orig = record.getMessage();
        StringBuilder newMessage = new StringBuilder(256);
        Thread current = Thread.currentThread();
        newMessage.append(orig).append(" (in thread '");
        newMessage.append(current.getName()).append("'");
        if (current instanceof SinkHandlerLogThread) {
            SinkHandlerLogThread tt = (SinkHandlerLogThread)current;
            if(tt.getCurrentProcessorName().length()>0) {
                newMessage.append("; in processor '");
                newMessage.append(tt.getCurrentProcessorName());
                newMessage.append("'");
            }
        }
        newMessage.append(")");
        record.setMessage(newMessage.toString());
        count++;
        for(Logger logger : loggers) {
            // for the relay, suppress use of parent handlers
            // (otherwise endless loop a risk if any target
            // loggers relay through parents to topmost logger)
            synchronized(logger) {
                boolean usePar = logger.getUseParentHandlers();
                logger.setUseParentHandlers(false);
                logger.log(record);
                logger.setUseParentHandlers(usePar); 
            }
        }
    }
}

        Thread t = Thread.currentThread();
        ThreadGroup th = t.getThreadGroup();
        while ((th != null) && !(th instanceof AlertThreadGroup)) {
            th = th.getParent();
        }
        return (AlertThreadGroup)th;
    
<methods>public void <init>(java.lang.String) ,public void <init>(java.lang.ThreadGroup, java.lang.String) ,public int activeCount() ,public int activeGroupCount() ,public boolean allowThreadSuspension(boolean) ,public final void checkAccess() ,public final void destroy() ,public int enumerate(java.lang.Thread[]) ,public int enumerate(java.lang.ThreadGroup[]) ,public int enumerate(java.lang.Thread[], boolean) ,public int enumerate(java.lang.ThreadGroup[], boolean) ,public final int getMaxPriority() ,public final java.lang.String getName() ,public final java.lang.ThreadGroup getParent() ,public final void interrupt() ,public final boolean isDaemon() ,public synchronized boolean isDestroyed() ,public void list() ,public final boolean parentOf(java.lang.ThreadGroup) ,public final void resume() ,public final void setDaemon(boolean) ,public final void setMaxPriority(int) ,public final void stop() ,public final void suspend() ,public java.lang.String toString() ,public void uncaughtException(java.lang.Thread, java.lang.Throwable) <variables>boolean daemon,boolean destroyed,java.lang.ThreadGroup[] groups,int maxPriority,int nUnstartedThreads,java.lang.String name,int ngroups,int nthreads,private final java.lang.ThreadGroup parent,java.lang.Thread[] threads