/*
* 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.
*/
package com.addthis.hydra.store.util;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import com.addthis.basis.jmx.MapMBean;
import com.addthis.basis.util.LessBytes;
import com.addthis.basis.util.LessFiles;
import com.addthis.basis.util.LessStrings;
public class MeterFileLogger implements Runnable {
private final DecimalFormat extFormat = new DecimalFormat("00000");
private final SimpleDateFormat dateFormat = new SimpleDateFormat("yy/MM/dd-HH:mm:ss");
private final boolean meterJMX = System.getProperty("meterFileLogger.jmx", "1").equals("1");
private final MeterDataSource meter;
private final File dirout;
private final String basename;
private final long logInterval;
private final int rollLines;
private final Thread thread;
private AtomicBoolean done;
private int lineCount = 0;
private int currentExt = 0;
private FileOutputStream currentOutput;
private Map<String, Long> lastMap;
private MapMBean mbean;
private ObjectName beanName;
public static interface MeterDataSource {
public Map<String, Long> getIntervalData();
}
public MeterFileLogger(MeterDataSource meter, File dirout, String basename, long logInterval, int rollLines) throws IOException {
this.meter = meter;
this.dirout = dirout;
this.basename = basename;
this.logInterval = logInterval;
this.rollLines = rollLines;
this.done = new AtomicBoolean(false);
LessFiles.initDirectory(dirout);
initNextOutput();
thread = new Thread(this, "MeterFileLogger " + dirout);
thread.setDaemon(true);
thread.start();
}
private synchronized void initNextOutput() throws IOException {
if (currentOutput != null) {
currentOutput.flush();
currentOutput.close();
}
while (!done.get()) {
File next = new File(dirout, basename + "-" + extFormat.format(currentExt++) + ".csv");
if (!next.exists()) {
currentOutput = new FileOutputStream(next);
lineCount = 0;
break;
}
}
}
public void terminate() {
if (done.compareAndSet(false, true)) {
thread.interrupt();
try {
thread.join();
initNextOutput();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override public void run() {
if (meterJMX) {
startBean();
}
while (!done.get()) {
try {
Thread.sleep(logInterval);
Map<String, Long> map = meter.getIntervalData();
if (meterJMX) {
lastMap = new HashMap<>(map);
mbean.setMap(lastMap);
}
if (lineCount++ == 0) {
LinkedList<String> line = new LinkedList<>();
line.add("\"date\"");
for (String label : map.keySet()) {
line.add("\"" + label + "\"");
}
currentOutput.write(LessBytes.toBytes(LessStrings.join(line.toArray(), ",") + "\n"));
}
LinkedList<String> line = new LinkedList<>();
line.add(dateFormat.format(System.currentTimeMillis()));
for (Long val : map.values()) {
line.add(Long.toString(val));
}
currentOutput.write(LessBytes.toBytes(LessStrings.join(line.toArray(), ",") + "\n"));
currentOutput.flush();
} catch (InterruptedException e) {
if (!done.get()) {
e.printStackTrace();
}
continue;
} catch (Exception e) {
e.printStackTrace();
continue;
}
}
if (meterJMX) {
stopBean();
}
}
protected void startBean() {
mbean = new MapMBean(lastMap);
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
try {
beanName = new ObjectName(this.getClass().getName() + ":type=" +
genBeanSuffix());
mbs.registerMBean(mbean, beanName);
} catch (Exception e) {
e.printStackTrace();
}
}
protected void stopBean() {
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
try {
if (beanName != null) {
mbs.unregisterMBean(beanName);
}
} catch (Exception e) {
e.printStackTrace();
}
}
protected String genBeanSuffix() {
String[] path = dirout.getAbsolutePath().split("minion");
int len = path.length;
String basePath;
if (len == 1) {
basePath = path[0];
} else if (len >= 2) {
basePath = path[1];
} else {
basePath = dirout.getPath();
}
String[] className = meter.getClass().getName().split("\\.");
return className[className.length - 1] + basePath; //: todo shortname?
}
}