/*************************GO-LICENSE-START********************************* * Copyright 2014 ThoughtWorks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *************************GO-LICENSE-END***********************************/ package com.thoughtworks.go.remote.work; import com.thoughtworks.go.util.SystemEnvironment; import com.thoughtworks.go.util.command.TaggedStreamConsumer; import org.apache.commons.collections.buffer.CircularFifoBuffer; import org.apache.log4j.Logger; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import static java.lang.String.format; public final class ConsoleOutputTransmitter implements TaggedStreamConsumer, Runnable { private static final Logger LOGGER = Logger.getLogger(ConsoleOutputTransmitter.class); private final CircularFifoBuffer buffer = new CircularFifoBuffer(10 * 1024); // maximum 10k lines private final ConsoleAppender consoleAppender; private final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss.SSS"); private final ScheduledThreadPoolExecutor executor; public ConsoleOutputTransmitter(ConsoleAppender consoleAppender) { this(consoleAppender, new SystemEnvironment().getConsolePublishInterval(), new ScheduledThreadPoolExecutor(1)); } protected ConsoleOutputTransmitter(ConsoleAppender consoleAppender, Integer consolePublishInterval, ScheduledThreadPoolExecutor scheduledThreadPoolExecutor) { this.consoleAppender = consoleAppender; this.executor = scheduledThreadPoolExecutor; executor.scheduleAtFixedRate(this, 0L, consolePublishInterval, TimeUnit.SECONDS); } public void consumeLine(String line) { taggedConsumeLine(null, line); } @Override public void taggedConsumeLine(String tag, String line) { synchronized (buffer) { if (null == tag) tag = " "; String date = dateFormat.format(new Date()); String prepend = format("%s|%s", tag, date); String multilineJoin = "\n" + prepend + " "; buffer.add(format("%s %s", prepend, line).replaceAll("\n", multilineJoin)); } } public void run() { try { flushToServer(); } catch (Throwable e) { LOGGER.warn("Could not send console output to server", e); } } public void flushToServer() { if (buffer.isEmpty()) { return; } List sent = new ArrayList(); try { synchronized (buffer) { while (!buffer.isEmpty()) { sent.add(buffer.remove()); } } StringBuilder result = new StringBuilder(); for (Object string : sent) { result.append(string); result.append("\n"); } consoleAppender.append(result.toString()); } catch (IOException e) { LOGGER.warn("Could not send console output to server", e); synchronized (buffer) { sent.addAll(buffer); buffer.clear(); buffer.addAll(sent); } } } public void stop() { flushToServer(); executor.shutdown(); } }