/**
* Helios, OpenSource Monitoring
* Brought to you by the Helios Development Group
*
* Copyright 2007, Helios Development Group and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*
*/
package org.helios.apmrouter.metric;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.GZIPOutputStream;
import org.helios.apmrouter.destination.accumulator.MetricTextFormatter;
import org.helios.apmrouter.trace.TXContext;
import org.helios.apmrouter.util.SystemClock;
import org.json.JSONException;
import org.json.JSONObject;
/**
* <p>Title: JSONFormatterImpl</p>
* <p>Description: </p>
* <p>Company: Helios Development Group LLC</p>
* @author Whitehead (nwhitehead AT heliosdev DOT org)
* <p><code>org.helios.apmrouter.metric.JSONFormatterImpl</code></p>
* Note: Query: http://localhost:3133/helios/_query?from=2012-10-09T12:20:00.000Z&to=2012-10-09T12:24:00.000Z&group=3600000&ptr=/data/0/value&reducer=count
*/
public class JSONFormatterImpl implements JSONFormatter, MetricTextFormatter {
/** If true, the metric identifier will use only the metric id. Otherwise uses the FQN */
protected final boolean compressedIdentifier;
/** If true, the requests for byte array json content will be gzipped */
protected final boolean gzip;
/**
* Creates a new JSONFormatterImpl
* @param compressedIdentifier If true, the metric identifier will use only the metric id. Otherwise uses the FQN
* @param gzip If true, the requests for byte array json content will be gzipped
*/
public JSONFormatterImpl(boolean compressedIdentifier, boolean gzip) {
this.compressedIdentifier = compressedIdentifier;
this.gzip = gzip;
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.metric.JSONFormatter#toJSONObject(org.helios.apmrouter.metric.IMetric[])
*/
@Override
public JSONObject toJSONObject(IMetric... metrics) throws JSONException {
return toJSONObject(new int[]{0}, metrics);
}
/**
* Formats the passed metrics into a JSONObject
* @param counter A counter reference
* @param metrics The metrics to format
* @return a JSONObject representing the formatted metrics
* @throws JSONException thrown on JSON creation errors
*/
protected JSONObject toJSONObject(int[] counter, IMetric... metrics) throws JSONException {
if(metrics==null || metrics.length<1) return new JSONObject();
JSONObject root = new JSONObject();
// Map<Object, JSONObject> map = new HashMap<Object, JSONObject>(metrics.length);
final Map<String, JSONObject> uncompresseddata = new HashMap<String, JSONObject>();
final Map<Long, JSONObject> compresseddata = new HashMap<Long, JSONObject>();
final long ts = SystemClock.time();
for(IMetric metric: metrics) {
//root.put(arg0, arg1)
if(metric==null || metric.getType()==MetricType.ERROR || metric.getType()==MetricType.BLOB || metric.getType()==MetricType.PDU || (compressedIdentifier && metric.getToken()==-1)) continue;
counter[0]++;
JSONObject jsonMetric = new JSONObject();
jsonMetric.put("ts", ts);
if(metric.getType().isLong()) {
jsonMetric.put("value", metric.getLongValue());
} else {
jsonMetric.put("event", metric.getValue());
}
TXContext txc = metric.getTXContext();
if(txc!=null) {
JSONObject txcNode = new JSONObject();
txcNode.put("id", txc.getTxId().toString());
txcNode.put("q", txc.getTxQualifier());
txcNode.put("t", txc.getTxThreadId());
jsonMetric.put("tx", txcNode);
}
if(compressedIdentifier) {
jsonMetric.put("type", metric.getType().ordinal());
compresseddata.put(metric.getToken(), jsonMetric);
} else {
jsonMetric.put("type", metric.getType().name());
uncompresseddata.put(metric.getFQN().replace('/', '_'), jsonMetric);
}
}
root.put("data", compressedIdentifier ? compresseddata : uncompresseddata);
root.put("type", "metrics");
return root;
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.metric.JSONFormatter#toJSON(org.helios.apmrouter.metric.IMetric[])
*/
@Override
public String toJSON(IMetric... metrics) throws JSONException {
return toJSONObject(metrics).toString();
}
/**
* Formats the passed metrics into json bytes
* @param counter A counter reference
* @param metrics The metrics to format
* @return a byte array representing the JSON formatted metrics
* @throws JSONException thrown on JSON creation errors
*/
public byte[] toJSONBytes(int[] counter, IMetric... metrics) throws JSONException {
if(!gzip) return toJSONObject(counter, metrics).toString().getBytes();
byte[] bytes = toJSONObject(counter, metrics).toString().getBytes();
ByteArrayOutputStream baos = new ByteArrayOutputStream(bytes.length);
try {
GZIPOutputStream gzout = new GZIPOutputStream(baos);
gzout.write(bytes);
gzout.close();
baos.flush();
byte[] gzipped = baos.toByteArray();
baos.close();
return gzipped;
} catch (IOException e) {
throw new RuntimeException("Failed to gzip json content", e);
}
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.metric.JSONFormatter#toJSONBytes(org.helios.apmrouter.metric.IMetric[])
*/
@Override
public byte[] toJSONBytes(IMetric... metrics) throws JSONException {
return toJSONBytes(new int[]{0}, metrics);
}
/**
* Returns true if the metric identifier will use only the metric id. Otherwise uses the FQN
* @return the compressedIdentifier
*/
public boolean isCompressedIdentifier() {
return compressedIdentifier;
}
/**
* Returns true if byte array json content will be gzipped
* @return the gzip
*/
public boolean isGzip() {
return gzip;
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.destination.accumulator.MetricTextFormatter#format(java.io.OutputStream, org.helios.apmrouter.metric.IMetric[])
*/
@Override
public int format(OutputStream os, IMetric... metrics) throws IOException {
int[] counter = new int[]{0};
try {
os.write(toJSONBytes(counter, metrics));
return counter[0];
} catch (JSONException e) {
throw new IOException("Failed to json format", e);
}
}
}