blob: cefbe6fa80c578e5d3946303143840e6f81ae2e4 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional information regarding
* copyright ownership. The ASF licenses this file to You 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 org.apache.geode.internal.statistics;
import java.io.BufferedOutputStream;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Calendar;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TimeZone;
import java.util.zip.GZIPOutputStream;
import org.apache.logging.log4j.Logger;
import org.apache.geode.GemFireIOException;
import org.apache.geode.InternalGemFireException;
import org.apache.geode.StatisticDescriptor;
import org.apache.geode.annotations.internal.MakeNotStatic;
import org.apache.geode.distributed.internal.DistributionConfig;
import org.apache.geode.internal.NanoTimer;
import org.apache.geode.internal.logging.log4j.LogMarker;
import org.apache.geode.internal.net.SocketCreator;
import org.apache.geode.logging.internal.log4j.api.LogService;
/**
* StatArchiveWriter provides APIs to write statistic snapshots to an archive file.
*
*/
public class StatArchiveWriter implements StatArchiveFormat, SampleHandler {
private static final Logger logger = LogService.getLogger();
@MakeNotStatic
private static volatile String traceStatisticsName = null;
@MakeNotStatic
private static volatile String traceStatisticsTypeName = null;
@MakeNotStatic
private static volatile int traceResourceInstId = -1;
private final boolean trace =
Boolean.getBoolean(DistributionConfig.GEMFIRE_PREFIX + "stats.debug.traceStatArchiveWriter");
private final Set<ResourceInstance> sampleWrittenForResources = new HashSet<ResourceInstance>();
private final Set<ResourceInstance> addedResources = new HashSet<ResourceInstance>();
private final StatArchiveDescriptor archiveDescriptor;
private long initialDate;
private final OutputStream outStream;
private final MyDataOutputStream dataOut;
private final OutputStream traceOutStream;
private final PrintStream traceDataOut;
private long previousMillisTimeStamp;
private int sampleCount;
/**
* Opens a StatArchiveWriter that will archive to the specified file.
*
* @throws GemFireIOException if <code>archiveName</code> can not be written to
*/
public StatArchiveWriter(StatArchiveDescriptor archiveDescriptor) {
this.archiveDescriptor = archiveDescriptor;
if (archiveDescriptor.getArchiveName().endsWith(".gz")) {
try {
this.outStream =
new GZIPOutputStream(new FileOutputStream(archiveDescriptor.getArchiveName()), 32768);
} catch (IOException ex) {
throw new GemFireIOException(String.format("Could not open %s",
archiveDescriptor.getArchiveName()), ex);
}
} else {
try {
this.outStream = new BufferedOutputStream(
new FileOutputStream(archiveDescriptor.getArchiveName()), 32768);
} catch (IOException ex) {
throw new GemFireIOException(String.format("Could not open %s",
archiveDescriptor.getArchiveName()), ex);
}
}
this.dataOut = new MyDataOutputStream(this.outStream);
if (this.trace) {
String traceFileName = archiveDescriptor.getArchiveName() + ".trace";
try {
this.traceOutStream = new BufferedOutputStream(new FileOutputStream(traceFileName), 32768);
} catch (IOException ex) {
throw new GemFireIOException("Could not open " + traceFileName, ex);
}
this.traceDataOut = new PrintStream(this.traceOutStream);
} else {
this.traceOutStream = null;
this.traceDataOut = null;
}
}
public String getArchiveName() {
return this.archiveDescriptor.getArchiveName();
}
public void initialize(long nanosTimeStamp) {
this.previousMillisTimeStamp = initPreviousMillisTimeStamp(nanosTimeStamp);
this.initialDate = initInitialDate();
writeHeader(this.initialDate, this.archiveDescriptor);
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder(getClass().getName());
sb.append("@").append(System.identityHashCode(this)).append("{");
sb.append("archiveName=").append(this.archiveDescriptor.getArchiveName());
sb.append("productDescription=").append(this.archiveDescriptor.getProductDescription());
sb.append("systemDirectoryPath=").append(this.archiveDescriptor.getSystemDirectoryPath());
sb.append("systemId=").append(this.archiveDescriptor.getSystemId());
sb.append("systemStartTime=").append(this.archiveDescriptor.getSystemStartTime());
sb.append("previousMillisTimeStamp=").append(this.previousMillisTimeStamp);
sb.append("initialDate=").append(this.initialDate);
return sb.toString();
}
/**
* Closes the statArchiver by flushing its data to disk a closing its output stream.
*
* @throws GemFireIOException if the archive file could not be closed.
*/
public void close() {
try {
this.dataOut.flush();
if (this.trace) {
this.traceDataOut.flush();
}
} catch (IOException ignore) {
}
try {
outStream.close();
if (this.trace) {
this.traceOutStream.close();
}
} catch (IOException ex) {
throw new GemFireIOException(
"Could not close statArchiver file",
ex);
}
if (getSampleCount() == 0) {
// If we are closing an empty file go ahead and delete it.
// This prevents the fix for 46917 from leaving a bunch of
// empty gfs files around.
deleteFileIfPossible(new File(getArchiveName()));
}
}
@edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "RV_RETURN_VALUE_IGNORED_BAD_PRACTICE",
justification = "Best effort attempt to delete a GFS file without any samples.")
private static void deleteFileIfPossible(File file) {
file.delete();
}
/**
* Returns the number of bytes written so far to this archive. This does not take compression into
* account.
*/
public long bytesWritten() {
return this.dataOut.getBytesWritten();
}
protected long initPreviousMillisTimeStamp(long nanosTimeStamp) {
return NanoTimer.nanosToMillis(nanosTimeStamp);
}
protected long initInitialDate() {
return System.currentTimeMillis();
}
protected TimeZone getTimeZone() {
return Calendar.getInstance().getTimeZone();
}
protected String getOSInfo() {
return System.getProperty("os.name") + " " + System.getProperty("os.version");
}
protected String getMachineInfo() {
String machineInfo = System.getProperty("os.arch");
try {
String hostName = SocketCreator.getHostName(SocketCreator.getLocalHost());
machineInfo += " " + hostName;
} catch (UnknownHostException ignore) {
}
return machineInfo;
}
private void writeHeader(long initialDate, StatArchiveDescriptor archiveDescriptor) {
if (logger.isTraceEnabled(LogMarker.STATISTICS_VERBOSE)) {
logger.trace(LogMarker.STATISTICS_VERBOSE,
"StatArchiveWriter#writeHeader initialDate={} archiveDescriptor={}", initialDate,
archiveDescriptor);
}
try {
this.dataOut.writeByte(HEADER_TOKEN);
this.dataOut.writeByte(ARCHIVE_VERSION);
this.dataOut.writeLong(initialDate);
this.dataOut.writeLong(archiveDescriptor.getSystemId());
this.dataOut.writeLong(archiveDescriptor.getSystemStartTime());
TimeZone timeZone = getTimeZone();
this.dataOut.writeInt(timeZone.getRawOffset());
this.dataOut.writeUTF(timeZone.getID());
this.dataOut.writeUTF(archiveDescriptor.getSystemDirectoryPath());
this.dataOut.writeUTF(archiveDescriptor.getProductDescription());
this.dataOut.writeUTF(getOSInfo());
this.dataOut.writeUTF(getMachineInfo());
if (this.trace) {
this.traceDataOut.println("writeHeader traceStatisticsName: " + traceStatisticsName);
this.traceDataOut
.println("writeHeader traceStatisticsTypeName: " + traceStatisticsTypeName);
this.traceDataOut.println("writeHeader#writeByte HEADER_TOKEN: " + HEADER_TOKEN);
this.traceDataOut.println("writeHeader#writeByte ARCHIVE_VERSION: " + ARCHIVE_VERSION);
this.traceDataOut.println("writeHeader#writeLong initialDate: " + initialDate);
this.traceDataOut.println("writeHeader#writeLong archiveDescriptor.getSystemId(): "
+ archiveDescriptor.getSystemId());
this.traceDataOut.println("writeHeader#writeLong archiveDescriptor.getSystemStartTime(): "
+ archiveDescriptor.getSystemStartTime());
this.traceDataOut
.println("writeHeader#writeInt timeZone.getRawOffset(): " + timeZone.getRawOffset());
this.traceDataOut.println("writeHeader#writeUTF timeZone.getID(): " + timeZone.getID());
this.traceDataOut
.println("writeHeader#writeUTF archiveDescriptor.getSystemDirectoryPath(): "
+ archiveDescriptor.getSystemDirectoryPath());
this.traceDataOut.println("writeHeader#writeUTF archiveDescriptor.getProductDescription(): "
+ archiveDescriptor.getProductDescription());
this.traceDataOut.println("writeHeader#writeUTF getOSInfo(): " + getOSInfo());
this.traceDataOut.println("writeHeader#writeUTF getMachineInfo(): " + getMachineInfo());
}
} catch (IOException ex) {
throw new GemFireIOException(
"Failed writing header to statistic archive",
ex);
}
}
@Override
public void allocatedResourceType(ResourceType resourceType) {
if (logger.isTraceEnabled(LogMarker.STATISTICS_VERBOSE)) {
logger.trace(LogMarker.STATISTICS_VERBOSE,
"StatArchiveWriter#allocatedResourceType resourceType={}", resourceType);
}
if (resourceType.getStatisticDescriptors().length >= ILLEGAL_STAT_OFFSET) {
throw new InternalGemFireException(
String.format("Could not archive type %s because it had more than %s statistics.",
new Object[] {resourceType.getStatisticsType().getName(),
Integer.valueOf(ILLEGAL_STAT_OFFSET - 1)}));
}
// write the type to the archive
try {
this.dataOut.writeByte(RESOURCE_TYPE_TOKEN);
this.dataOut.writeInt(resourceType.getId());
this.dataOut.writeUTF(resourceType.getStatisticsType().getName());
this.dataOut.writeUTF(resourceType.getStatisticsType().getDescription());
StatisticDescriptor[] stats = resourceType.getStatisticDescriptors();
this.dataOut.writeShort(stats.length);
if (this.trace && (traceStatisticsTypeName == null
|| traceStatisticsTypeName.equals(resourceType.getStatisticsType().getName()))) {
this.traceDataOut
.println("allocatedResourceType#writeByte RESOURCE_TYPE_TOKEN: " + RESOURCE_TYPE_TOKEN);
this.traceDataOut.println(
"allocatedResourceType#writeInt resourceType.getId(): " + resourceType.getId());
this.traceDataOut
.println("allocatedResourceType#writeUTF resourceType.getStatisticsType().getName(): "
+ resourceType.getStatisticsType().getName());
this.traceDataOut.println(
"allocatedResourceType#writeUTF resourceType.getStatisticsType().getDescription(): "
+ resourceType.getStatisticsType().getDescription());
this.traceDataOut.println("allocatedResourceType#writeShort stats.length: " + stats.length);
}
for (int i = 0; i < stats.length; i++) {
this.dataOut.writeUTF(stats[i].getName());
this.dataOut.writeByte(((StatisticDescriptorImpl) stats[i]).getTypeCode());
this.dataOut.writeBoolean(stats[i].isCounter());
this.dataOut.writeBoolean(stats[i].isLargerBetter());
this.dataOut.writeUTF(stats[i].getUnit());
this.dataOut.writeUTF(stats[i].getDescription());
if (this.trace && (traceStatisticsTypeName == null
|| traceStatisticsTypeName.equals(resourceType.getStatisticsType().getName()))) {
this.traceDataOut
.println("allocatedResourceType#writeUTF stats[i].getName(): " + stats[i].getName());
this.traceDataOut.println(
"allocatedResourceType#writeByte ((StatisticDescriptorImpl)stats[i]).getTypeCode(): "
+ ((StatisticDescriptorImpl) stats[i]).getTypeCode());
this.traceDataOut.println(
"allocatedResourceType#writeBoolean stats[i].isCounter(): " + stats[i].isCounter());
this.traceDataOut.println("allocatedResourceType#writeBoolean stats[i].isLargerBetter(): "
+ stats[i].isLargerBetter());
this.traceDataOut
.println("allocatedResourceType#writeUTF stats[i].getUnit(): " + stats[i].getUnit());
this.traceDataOut.println("allocatedResourceType#writeUTF stats[i].getDescription(): "
+ stats[i].getDescription());
}
}
} catch (IOException ex) {
throw new GemFireIOException(
"Failed writing new resource type to statistic archive",
ex);
}
}
@Override
@edu.umd.cs.findbugs.annotations.SuppressWarnings(
value = "ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD",
justification = "This is only for debugging and there is never more than one instance being traced because there is only one stat sampler.")
public void allocatedResourceInstance(ResourceInstance statResource) {
if (logger.isTraceEnabled(LogMarker.STATISTICS_VERBOSE)) {
logger.trace(LogMarker.STATISTICS_VERBOSE,
"StatArchiveWriter#allocatedResourceInstance statResource={}", statResource);
}
if (statResource.getResourceType().getStatisticDescriptors().length >= ILLEGAL_STAT_OFFSET) {
throw new InternalGemFireException(
String.format("Could not archive type %s because it had more than %s statistics.",
new Object[] {statResource.getResourceType().getStatisticsType().getName(),
Integer.valueOf(ILLEGAL_STAT_OFFSET - 1)}));
}
if (statResource.getStatistics().isClosed()) {
return;
}
this.addedResources.add(statResource);
try {
this.dataOut.writeByte(RESOURCE_INSTANCE_CREATE_TOKEN);
this.dataOut.writeInt(statResource.getId());
this.dataOut.writeUTF(statResource.getStatistics().getTextId());
this.dataOut.writeLong(statResource.getStatistics().getNumericId());
this.dataOut.writeInt(statResource.getResourceType().getId());
if (this.trace
&& (traceStatisticsName == null
|| traceStatisticsName.equals(statResource.getStatistics().getTextId()))
&& (traceStatisticsTypeName == null || traceStatisticsTypeName
.equals(statResource.getResourceType().getStatisticsType().getName()))) {
traceResourceInstId = statResource.getId();
this.traceDataOut.println("writeHeader traceResourceInstId: " + traceResourceInstId);
this.traceDataOut
.println("allocatedResourceInstance#writeByte RESOURCE_INSTANCE_CREATE_TOKEN: "
+ RESOURCE_INSTANCE_CREATE_TOKEN);
this.traceDataOut.println(
"allocatedResourceInstance#writeInt statResource.getId(): " + statResource.getId());
this.traceDataOut
.println("allocatedResourceInstance#writeUTF statResource.getStatistics().getTextId(): "
+ statResource.getStatistics().getTextId());
this.traceDataOut.println(
"allocatedResourceInstance#writeLong statResource.getStatistics().getNumericId(): "
+ statResource.getStatistics().getNumericId());
this.traceDataOut
.println("allocatedResourceInstance#writeInt statResource.getResourceType().getId(): "
+ statResource.getResourceType().getId());
}
} catch (IOException ex) {
throw new GemFireIOException(
"Failed writing new resource instance to statistic archive",
ex);
}
}
@Override
public void destroyedResourceInstance(ResourceInstance resourceInstance) {
if (logger.isTraceEnabled(LogMarker.STATISTICS_VERBOSE)) {
logger.trace(LogMarker.STATISTICS_VERBOSE,
"StatArchiveWriter#destroyedResourceInstance resourceInstance={}", resourceInstance);
}
if (!this.addedResources.contains(resourceInstance)) { // Fix for bug #45377
return;
}
this.sampleWrittenForResources.remove(resourceInstance);
this.addedResources.remove(resourceInstance);
try {
this.dataOut.writeByte(RESOURCE_INSTANCE_DELETE_TOKEN);
this.dataOut.writeInt(resourceInstance.getId());
if (this.trace
&& (traceStatisticsName == null
|| traceStatisticsName.equals(resourceInstance.getStatistics().getTextId()))
&& (traceStatisticsTypeName == null || traceStatisticsTypeName
.equals(resourceInstance.getResourceType().getStatisticsType().getName()))) {
this.traceDataOut
.println("destroyedResourceInstance#writeByte RESOURCE_INSTANCE_DELETE_TOKEN: "
+ RESOURCE_INSTANCE_DELETE_TOKEN);
this.traceDataOut.println("destroyedResourceInstance#writeInt resourceInstance.getId(): "
+ resourceInstance.getId());
}
} catch (IOException ex) {
throw new GemFireIOException(
"Failed writing delete resource instance to statistic archive",
ex);
}
}
static long calcDelta(long previousMillis, long currentMillis) {
long delta = currentMillis - previousMillis;
if (delta <= 0) {
throw new IllegalArgumentException(
"Sample timestamp must be greater than previous timestamp (millisTimeStamp is "
+ currentMillis + ", previousMillis is " + previousMillis + " and delta is " + delta
+ ").");
}
return delta;
}
private void writeTimeStamp(long nanosTimeStamp) throws IOException {
final long millisTimeStamp = NanoTimer.nanosToMillis(nanosTimeStamp);
final long delta = calcDelta(this.previousMillisTimeStamp, millisTimeStamp);
if (logger.isTraceEnabled(LogMarker.STATISTICS_VERBOSE)) {
logger.trace(LogMarker.STATISTICS_VERBOSE,
"StatArchiveWriter#writeTimeStamp millisTimeStamp={}, delta={}", millisTimeStamp,
(int) delta);
}
if (delta > MAX_SHORT_TIMESTAMP) {
if (delta > Integer.MAX_VALUE) {
throw new InternalGemFireException(
String.format("timeStamp delta %s was greater than %s",
new Object[] {Long.valueOf(delta), Integer.valueOf(Integer.MAX_VALUE)}));
}
this.dataOut.writeShort(INT_TIMESTAMP_TOKEN);
this.dataOut.writeInt((int) delta);
if (this.trace) {
this.traceDataOut
.println("writeTimeStamp#writeShort INT_TIMESTAMP_TOKEN: " + INT_TIMESTAMP_TOKEN);
this.traceDataOut.println("writeTimeStamp#writeInt (int)delta: " + (int) delta);
}
} else {
this.dataOut.writeShort((int) delta);
if (this.trace) {
this.traceDataOut.println("writeTimeStamp#writeShort (int)delta: " + (int) delta);
}
}
this.previousMillisTimeStamp = millisTimeStamp;
}
/**
* Writes the resource instance id to the <code>dataOut</code> stream.
*/
private void writeResourceInst(int instId) throws IOException {
if (logger.isTraceEnabled(LogMarker.STATISTICS_VERBOSE)) {
logger.trace(LogMarker.STATISTICS_VERBOSE, "StatArchiveWriter#writeResourceInst instId={}",
instId);
}
if (instId > MAX_BYTE_RESOURCE_INST_ID) {
if (instId > MAX_SHORT_RESOURCE_INST_ID) {
this.dataOut.writeByte(INT_RESOURCE_INST_ID_TOKEN);
this.dataOut.writeInt(instId);
if (this.trace && (traceResourceInstId == -1 || traceResourceInstId == instId)) {
this.traceDataOut.println("writeResourceInst#writeByte INT_RESOURCE_INST_ID_TOKEN: "
+ INT_RESOURCE_INST_ID_TOKEN);
this.traceDataOut.println("writeResourceInst#writeInt instId: " + instId);
}
} else {
this.dataOut.writeByte(SHORT_RESOURCE_INST_ID_TOKEN);
this.dataOut.writeShort(instId);
if (this.trace && (traceResourceInstId == -1 || traceResourceInstId == instId)) {
this.traceDataOut.println("writeResourceInst#writeByte SHORT_RESOURCE_INST_ID_TOKEN: "
+ SHORT_RESOURCE_INST_ID_TOKEN);
this.traceDataOut.println("writeResourceInst#writeShort instId: " + instId);
}
}
} else {
this.dataOut.writeByte(instId);
if (this.trace && (traceResourceInstId == -1 || traceResourceInstId == instId)) {
if (instId == ILLEGAL_RESOURCE_INST_ID) {
this.traceDataOut.println(
"writeResourceInst#writeByte ILLEGAL_RESOURCE_INST_ID: " + ILLEGAL_RESOURCE_INST_ID);
} else {
this.traceDataOut.println("writeResourceInst#writeByte instId: " + instId);
}
}
}
}
@Override
public void sampled(long nanosTimeStamp, List<ResourceInstance> resourceInstances) {
if (logger.isTraceEnabled(LogMarker.STATISTICS_VERBOSE)) {
logger.trace(LogMarker.STATISTICS_VERBOSE,
"StatArchiveWriter#sampled nanosTimeStamp={}, resourceInstances={}", nanosTimeStamp,
resourceInstances);
}
try {
this.dataOut.writeByte(SAMPLE_TOKEN);
if (this.trace) {
this.traceDataOut.println("sampled#writeByte SAMPLE_TOKEN: " + SAMPLE_TOKEN);
}
writeTimeStamp(nanosTimeStamp);
for (ResourceInstance ri : resourceInstances) {
writeSample(ri);
}
writeResourceInst(ILLEGAL_RESOURCE_INST_ID);
this.dataOut.flush();
if (this.trace) {
this.traceDataOut.flush();
}
} catch (IOException ex) {
throw new GemFireIOException(
"Failed writing sample to statistic archive",
ex);
}
this.sampleCount++; // only inc after sample done w/o an exception thrown
}
public int getSampleCount() {
return this.sampleCount;
}
private void writeSample(ResourceInstance ri) throws IOException {
final boolean isDebugEnabled_STATISTICS = logger.isTraceEnabled(LogMarker.STATISTICS_VERBOSE);
if (isDebugEnabled_STATISTICS) {
logger.trace(LogMarker.STATISTICS_VERBOSE, "StatArchiveWriter#writeSample ri={}", ri);
}
if (this.trace
&& (traceStatisticsName == null
|| traceStatisticsName.equals(ri.getStatistics().getTextId()))
&& (traceStatisticsTypeName == null || traceStatisticsTypeName
.equals(ri.getResourceType().getStatisticsType().getName()))) {
this.traceDataOut.println("writeSample#writeSample for ri=" + ri);
}
if (ri.getStatistics().isClosed()) {
return;
}
StatisticDescriptor[] stats = ri.getResourceType().getStatisticDescriptors();
if (stats.length > 254) {
throw new Error("StatisticsType " + ri.getResourceType().getStatisticsType().getName()
+ " has too many stats: " + stats.length);
}
boolean wroteInstId = false;
boolean checkForChange = true;
if (!this.sampleWrittenForResources.contains(ri)) {
// first time for this instance so all values need to be written
checkForChange = false;
this.sampleWrittenForResources.add(ri);
}
long[] previousStatValues = ri.getPreviousStatValues();
if (isDebugEnabled_STATISTICS) {
logger.trace(LogMarker.STATISTICS_VERBOSE,
"StatArchiveWriter#writeSample checkForChange={}, previousStatValues={}, stats.length={}",
checkForChange, Arrays.toString(previousStatValues), stats.length);
}
if (previousStatValues == null) {
previousStatValues = new long[stats.length];
ri.setPreviousStatValues(previousStatValues);
}
int statsWritten = 0;
try {
for (int i = 0; i < stats.length; i++) {
long value = ri.getLatestStatValues()[i];
if (!checkForChange || value != previousStatValues[i]) {
long delta = checkForChange ? value - previousStatValues[i] : value;
if (!wroteInstId) {
wroteInstId = true;
writeResourceInst(ri.getId());
}
this.dataOut.writeByte(i);
if (this.trace
&& (traceStatisticsName == null
|| traceStatisticsName.equals(ri.getStatistics().getTextId()))
&& (traceStatisticsTypeName == null || traceStatisticsTypeName
.equals(ri.getResourceType().getStatisticsType().getName()))) {
this.traceDataOut.println("writeSample#writeByte i: " + i);
}
if (isDebugEnabled_STATISTICS) {
logger.trace(LogMarker.STATISTICS_VERBOSE,
"StatArchiveWriter#writeStatValue stats[{}]={}, delta={}", i, stats[i], delta);
}
writeStatValue(stats[i], delta, this.dataOut);
if (this.trace
&& (traceStatisticsName == null
|| traceStatisticsName.equals(ri.getStatistics().getTextId()))
&& (traceStatisticsTypeName == null || traceStatisticsTypeName
.equals(ri.getResourceType().getStatisticsType().getName()))) {
byte typeCode = ((StatisticDescriptorImpl) stats[i]).getTypeCode();
switch (typeCode) {
case BYTE_CODE:
this.traceDataOut.println(
"writeStatValue#writeByte " + typeCodeToString(typeCode) + " delta: " + delta);
break;
case SHORT_CODE:
this.traceDataOut.println(
"writeStatValue#writeShort" + typeCodeToString(typeCode) + " delta: " + delta);
break;
case INT_CODE:
case FLOAT_CODE:
case LONG_CODE:
case DOUBLE_CODE:
this.traceDataOut.println("writeStatValue#writeCompactValue "
+ typeCodeToString(typeCode) + " delta: " + delta);
break;
default:
}
}
}
}
} catch (IllegalStateException closedEx) {
// resource was closed causing getStatValue to throw this exception
}
if (wroteInstId) {
this.dataOut.writeByte(ILLEGAL_STAT_OFFSET);
if (this.trace
&& (traceStatisticsName == null
|| traceStatisticsName.equals(ri.getStatistics().getTextId()))
&& (traceStatisticsTypeName == null || traceStatisticsTypeName
.equals(ri.getResourceType().getStatisticsType().getName()))) {
this.traceDataOut
.println("writeSample#writeByte ILLEGAL_STAT_OFFSET: " + ILLEGAL_STAT_OFFSET);
}
}
if (isDebugEnabled_STATISTICS) {
logger.trace(LogMarker.STATISTICS_VERBOSE, "StatArchiveWriter#writeSample statsWritten={}",
statsWritten);
}
}
public static void writeCompactValue(long v, DataOutput dataOut) throws IOException {
if (v <= MAX_1BYTE_COMPACT_VALUE && v >= MIN_1BYTE_COMPACT_VALUE) {
dataOut.writeByte((int) v);
} else if (v <= MAX_2BYTE_COMPACT_VALUE && v >= MIN_2BYTE_COMPACT_VALUE) {
dataOut.writeByte(COMPACT_VALUE_2_TOKEN);
dataOut.writeShort((int) v);
} else {
byte[] buffer = new byte[8];
int idx = 0;
long originalValue = v;
if (v < 0) {
while (v != -1 && v != 0) {
buffer[idx++] = (byte) (v & 0xFF);
v >>= 8;
}
// On windows v goes to zero somtimes; seems like a bug
if (v == 0) {
// when this happens we end up with a bunch of -1 bytes
// so strip off the high order ones
while (buffer[idx - 1] == -1) {
idx--;
}
}
if ((buffer[idx - 1] & 0x80) == 0) {
/*
* If the most significant byte does not have its high order bit set then add a -1 byte so
* we know this is a negative number
*/
buffer[idx++] = -1;
}
} else {
while (v != 0) {
buffer[idx++] = (byte) (v & 0xFF);
v >>= 8;
}
if ((buffer[idx - 1] & 0x80) != 0) {
/*
* If the most significant byte has its high order bit set then add a zero byte so we know
* this is a positive number
*/
buffer[idx++] = 0;
}
}
if (idx <= 2) {
throw new InternalGemFireException(
String.format("Expected idx to be greater than 2. It was %s for the value %s",
new Object[] {Integer.valueOf(idx), Long.valueOf(originalValue)}));
}
int token = COMPACT_VALUE_2_TOKEN + (idx - 2);
dataOut.writeByte(token);
for (int i = idx - 1; i >= 0; i--) {
dataOut.writeByte(buffer[i]);
}
}
}
public static long readCompactValue(DataInput dataIn) throws IOException {
long v = dataIn.readByte();
boolean dump = false;
if (dump) {
System.out.print("compactValue(byte1)=" + v);
}
if (v < MIN_1BYTE_COMPACT_VALUE) {
if (v == COMPACT_VALUE_2_TOKEN) {
v = dataIn.readShort();
if (dump) {
System.out.print("compactValue(short)=" + v);
}
} else {
int bytesToRead = ((byte) v - COMPACT_VALUE_2_TOKEN) + 2;
v = dataIn.readByte(); // note the first byte will be a signed byte.
if (dump) {
System.out.print("compactValue(" + bytesToRead + ")=" + v);
}
bytesToRead--;
while (bytesToRead > 0) {
v <<= 8;
v |= dataIn.readUnsignedByte();
bytesToRead--;
}
}
}
return v;
}
protected static void writeStatValue(StatisticDescriptor f, long v, DataOutput dataOut)
throws IOException {
byte typeCode = ((StatisticDescriptorImpl) f).getTypeCode();
writeStatValue(typeCode, v, dataOut);
}
public static void writeStatValue(byte typeCode, long v, DataOutput dataOut) throws IOException {
switch (typeCode) {
case BYTE_CODE:
dataOut.writeByte((int) v);
break;
case SHORT_CODE:
dataOut.writeShort((int) v);
break;
case INT_CODE:
case FLOAT_CODE:
case LONG_CODE:
case DOUBLE_CODE:
writeCompactValue(v, dataOut);
break;
default:
throw new InternalGemFireException(String.format("Unexpected type code %s",
Byte.valueOf(typeCode)));
}
}
protected static void setTraceFilter(String traceStatisticsName, String traceStatisticsTypeName) {
StatArchiveWriter.traceStatisticsName = traceStatisticsName;
StatArchiveWriter.traceStatisticsTypeName = traceStatisticsTypeName;
StatArchiveWriter.traceResourceInstId = -1;
}
protected static void clearTraceFilter() {
StatArchiveWriter.traceStatisticsName = null;
StatArchiveWriter.traceStatisticsTypeName = null;
StatArchiveWriter.traceResourceInstId = -1;
}
private static String typeCodeToString(byte typeCode) {
switch (typeCode) {
case BYTE_CODE:
return "BYTE_CODE";
case SHORT_CODE:
return "SHORT_CODE";
case INT_CODE:
return "INT_CODE";
case FLOAT_CODE:
return "FLOAT_CODE";
case LONG_CODE:
return "LONG_CODE";
case DOUBLE_CODE:
return "DOUBLE_CODE";
default:
return "unknown typeCode " + typeCode;
}
}
private static class MyDataOutputStream implements DataOutput {
private long bytesWritten = 0;
private final DataOutputStream dataOut;
public MyDataOutputStream(OutputStream out) {
this.dataOut = new DataOutputStream(out);
}
public long getBytesWritten() {
return this.bytesWritten;
}
public void flush() throws IOException {
this.dataOut.flush();
}
@SuppressWarnings("unused")
public void close() throws IOException {
this.dataOut.close();
}
@Override
public void write(int b) throws IOException {
throw new RuntimeException(
"method unimplemented");
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
throw new RuntimeException(
"method unimplemented");
}
@Override
public void write(byte[] b) throws IOException {
throw new RuntimeException(
"method unimplemented");
}
@Override
public void writeBytes(String v) throws IOException {
throw new RuntimeException(
"method unimplemented");
}
@Override
public void writeChar(int v) throws IOException {
throw new RuntimeException(
"method unimplemented");
}
@Override
public void writeChars(String v) throws IOException {
throw new RuntimeException(
"method unimplemented");
}
@Override
public void writeDouble(double v) throws IOException {
throw new RuntimeException(
"method unimplemented");
}
@Override
public void writeFloat(float v) throws IOException {
throw new RuntimeException(
"method unimplemented");
}
@Override
public void writeBoolean(boolean v) throws IOException {
this.dataOut.writeBoolean(v);
this.bytesWritten += 1;
}
@Override
public void writeByte(int v) throws IOException {
this.dataOut.writeByte(v);
this.bytesWritten += 1;
}
@Override
public void writeShort(int v) throws IOException {
this.dataOut.writeShort(v);
this.bytesWritten += 2;
}
@Override
public void writeInt(int v) throws IOException {
this.dataOut.writeInt(v);
this.bytesWritten += 4;
}
@Override
public void writeLong(long v) throws IOException {
this.dataOut.writeLong(v);
this.bytesWritten += 8;
}
@Override
public void writeUTF(String v) throws IOException {
this.dataOut.writeUTF(v);
this.bytesWritten += v.length() + 2; // this is the minimum. The max is v.size()*3 +2
}
}
}