All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.gemstone.gemfire.internal.statistics.StatArchiveHandler Maven / Gradle / Ivy

/*
 * Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved.
 *
 * 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. See accompanying
 * LICENSE file.
 */
package com.gemstone.gemfire.internal.statistics;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.List;
import java.util.regex.Pattern;

import com.gemstone.gemfire.GemFireException;
import com.gemstone.gemfire.GemFireIOException;
import com.gemstone.gemfire.i18n.LogWriterI18n;
import com.gemstone.gemfire.internal.ManagerLogWriter;
import com.gemstone.gemfire.internal.StatArchiveWriter;
import com.gemstone.gemfire.internal.i18n.LocalizedStrings;

/**
 * Extracted from {@link com.gemstone.gemfire.internal.HostStatSampler} and 
 * {@link com.gemstone.gemfire.internal.GemFireStatSampler}.
 * 

* The StatArchiveHandler handles statistics samples by archiving them to a * file. This handler provides archive file rolling (file size limit) and * removal (disk space limit). This handler creates and uses an instance of * {@link com.gemstone.gemfire.internal.StatArchiveWriter} for the currently * open archive file (unless archiving is disabled). * * @author Darrel Schneider * @author Kirk Lund * @since 7.0 */ public class StatArchiveHandler implements SampleHandler { /** Enable debug logging if true. */ private final boolean debug = Boolean.getBoolean("gemfire.stats.debug.debugStatArchiveHandler"); /** Configuration used in constructing this handler instance. */ private final StatArchiveHandlerConfig config; /** The collector responsible for sample statistics and notifying handlers. */ private final SampleCollector collector; /** * Indicates if archiving has been disabled by specifying empty string for * the archive file name. Other threads may call in to changeArchiveFile * to manipulate this flag. */ private volatile boolean disabledArchiving = false; /** The currently open writer/file. Protected by synchronization on this handler instance. */ private StatArchiveWriter archiver = null; /** Directory to contain archive files. */ private File archiveDir = null; /** The first of two numbers used within the name of rolling archive files. */ private int mainArchiveId = -1; /** The second of two numbers used within the name of rolling archive files. */ private int archiveId = -1; /** * Constructs a new instance. The {@link StatArchiveHandlerConfig} and * {@link SampleCollector} must not be null. */ public StatArchiveHandler(StatArchiveHandlerConfig config, SampleCollector sampleCollector) { this.config = config; this.collector = sampleCollector; } /** * Initializes the stat archiver with nanosTimeStamp. * @param nanosTimeStamp */ public void initialize(long nanosTimeStamp) { changeArchiveFile(false, nanosTimeStamp); assertInitialized(); } /** * Closes any {@link com.gemstone.gemfire.internal.StatArchiveWriter} * currently in use by this handler. * @throws GemFireException */ public void close() throws GemFireException { synchronized (this) { if (archiver != null) { archiver.close(); } } } private void handleArchiverException(GemFireException ex) { if (this.archiver.getSampleCount() > 0) { StringWriter sw = new StringWriter(); ex.printStackTrace(new PrintWriter(sw, true)); this.collector.getLogWriterI18n().warning(LocalizedStrings.HostStatSampler_STATISTIC_ARCHIVER_SHUTTING_DOWN_BECAUSE__0, sw); } try { this.archiver.close(); } catch (GemFireException ignore) { if (this.archiver.getSampleCount() > 0) { this.collector.getLogWriterI18n().warning(LocalizedStrings.HostStatSampler_STATISIC_ARCHIVER_SHUTDOWN_FAILED_BECAUSE__0, ignore.getMessage()); } } if (this.archiver.getSampleCount() == 0 && this.archiveId != -1) { // dec since we didn't use the file and close deleted it. this.archiveId--; } this.archiver = null; } @Override public void sampled(long nanosTimeStamp, List resourceInstances) { synchronized (this) { if (this.debug) { this.collector.getLogWriter().info("DEBUG StatArchiveHandler#sampled resourceInstances=" + resourceInstances); } if (archiver != null) { try { archiver.sampled(nanosTimeStamp, resourceInstances); if (archiver.getSampleCount() == 1) { LogWriterI18n mainLogger = this.collector.getLogWriterI18n(); mainLogger.info(LocalizedStrings.GemFireStatSampler_ARCHIVING_STATISTICS_TO__0_, archiver.getArchiveName()); } } catch (IllegalArgumentException e) { this.collector.getLogWriter().warning("Use of java.lang.System.nanoTime() resulted in a non-positive timestamp delta. Skipping archival of statistics sample.", e); } catch (GemFireException ex) { handleArchiverException(ex); // this will null out archiver } if (archiver != null) { // fix npe seen in bug 46917 long byteLimit = config.getArchiveFileSizeLimit(); if (byteLimit != 0) { long bytesWritten = archiver.bytesWritten(); if (bytesWritten > byteLimit) { // roll the archive try { changeArchiveFile(true, nanosTimeStamp); } catch (GemFireIOException ignore) { // it has already been logged // We don't want this exception to kill this thread. See 46917 } } } } } else { // Check to see if archiving is enabled. if (!this.config.getArchiveFileName().getPath().equals("")) { // It is enabled so we must not have an archiver due to an exception. // So try to recreate the archiver. See bug 46917. try { changeArchiveFile(true, nanosTimeStamp); } catch (GemFireIOException ignore) { } } } } // sync } void assertInitialized() { if (archiver == null && !this.config.getArchiveFileName().getPath().equals("")) { throw new IllegalStateException("This " + this + " was not initialized"); } } @Override public void allocatedResourceType(ResourceType resourceType) { if (this.debug) { this.collector.getLogWriter().info("DEBUG StatArchiveHandler#allocatedResourceType resourceType=" + resourceType); } if (archiver != null) { try { archiver.allocatedResourceType(resourceType); } catch (GemFireException ex) { handleArchiverException(ex); } } } @Override public void allocatedResourceInstance(ResourceInstance resourceInstance) { if (this.debug) { this.collector.getLogWriter().info("DEBUG StatArchiveHandler#allocatedResourceInstance resourceInstance=" + resourceInstance); } if (archiver != null) { try { archiver.allocatedResourceInstance(resourceInstance); } catch (GemFireException ex) { handleArchiverException(ex); } } } @Override public void destroyedResourceInstance(ResourceInstance resourceInstance) { if (this.debug) { this.collector.getLogWriter().info("DEBUG StatArchiveHandler#destroyedResourceInstance resourceInstance=" + resourceInstance); } if (archiver != null) { try { archiver.destroyedResourceInstance(resourceInstance); } catch (GemFireException ex) { handleArchiverException(ex); } } } /** * Returns the configuration for this handler. */ public StatArchiveHandlerConfig getStatArchiveHandlerConfig() { return this.config; } @Override public String toString() { final StringBuilder sb = new StringBuilder(getClass().getName()); sb.append("@").append(System.identityHashCode(this)).append("{"); sb.append("config=").append(this.config); sb.append(", archiveDir=").append(this.archiveDir); sb.append(", mainArchiveId=").append(this.mainArchiveId); sb.append(", archiveId=").append(this.archiveId); sb.append(", archiver=").append(this.archiver); sb.append("}"); return sb.toString(); } /** * Changes the archive file to the new file or disables archiving if an * empty string is specified. This may be invoked by any thread other than * the stat sampler. *

* If the file name matches any archive file(s) already in {@link #archiveDir} * then this may trigger rolling and/or removal if appropriate based on {@link * StatArchiveHandlerConfig#getArchiveFileSizeLimit() file size limit} and * {@link StatArchiveHandlerConfig#getArchiveDiskSpaceLimit() disk space * limit}. * * @param newFile the new archive file to use or "" to disable archiving * @param nanosTimeStamp */ protected void changeArchiveFile(File newFile, long nanosTimeStamp) { changeArchiveFile(newFile, true, nanosTimeStamp); } protected boolean isArchiving() { return this.archiver != null && this.archiver.bytesWritten() > 0; } /** * Changes the archive file using the same configured archive file name. *

* If the file name matches any archive file(s) already in {@link #archiveDir} * then this may trigger rolling and/or removal if appropriate based on {@link * StatArchiveHandlerConfig#getArchiveFileSizeLimit() file size limit} and * {@link StatArchiveHandlerConfig#getArchiveDiskSpaceLimit() disk space * limit}. *

* If resetHandler is true, then this handler will reset itself with the * SampleCollector by removing and re-adding itself in order to receive * allocation notifications about all resource types and instances. * * @param resetHandler true if the handler should reset itself with the * SampleCollector in order to receive allocation notifications about all * resource types and instances * * @param nanosTimeStamp */ private void changeArchiveFile(boolean resetHandler, long nanosTimeStamp) { changeArchiveFile(this.config.getArchiveFileName(), resetHandler, nanosTimeStamp); } /** * Changes the archive file to the new file or disables archiving if an * empty string is specified. *

* If the file name matches any archive file(s) already in {@link #archiveDir} * then this may trigger rolling and/or removal if appropriate based on {@link * StatArchiveHandlerConfig#getArchiveFileSizeLimit() file size limit} and * {@link StatArchiveHandlerConfig#getArchiveDiskSpaceLimit() disk space * limit}. *

* If resetHandler is true, then this handler will reset itself with the * SampleCollector by removing and re-adding itself in order to receive * allocation notifications about all resource types and instances. * * @param newFile * @param resetHandler * @param nanosTimeStamp */ private void changeArchiveFile(File newFile, boolean resetHandler, long nanosTimeStamp) { if (this.debug) { this.collector.getLogWriter().info("DEBUG StatArchiveHandler#changeArchiveFile newFile=" + newFile + ", nanosTimeStamp=" + nanosTimeStamp); } LogWriterI18n mainLogger = this.collector.getLogWriterI18n(); StatArchiveWriter newArchiver = null; boolean archiveClosed = false; if (newFile.getPath().equals("")) { // disable archiving if (!this.disabledArchiving) { this.disabledArchiving = true; mainLogger.info(LocalizedStrings.GemFireStatSampler_DISABLING_STATISTIC_ARCHIVAL); } } else { this.disabledArchiving = false; if (this.config.getArchiveFileSizeLimit() != 0) { // To fix bug 51133 need to always write to newFile. // Need to close any existing archive and then rename it // to getRollingArchiveName(newFile). if (archiver != null) { archiveClosed = true; synchronized (this) { if (resetHandler) { if (this.debug) { this.collector.getLogWriter().info("DEBUG StatArchiveHandler#changeArchiveFile removing handler"); } this.collector.removeSampleHandler(this); } try { archiver.close(); } catch (GemFireException ignore) { this.collector.getLogWriterI18n().warning(LocalizedStrings.GemFireStatSampler_STATISTIC_ARCHIVE_CLOSE_FAILED_BECAUSE__0, ignore.getMessage()); } } } } if (newFile.exists()) { File oldFile; if (this.config.getArchiveFileSizeLimit() != 0) { oldFile = getRollingArchiveName(newFile, archiveClosed); } else { oldFile = getRenameArchiveName(newFile); } if (!newFile.renameTo(oldFile)) { mainLogger.warning( LocalizedStrings.GemFireStatSampler_COULD_NOT_RENAME_0_TO_1, new Object[] {newFile, oldFile}); } else { mainLogger.info(LocalizedStrings.GemFireStatSampler_RENAMED_OLD_EXISTING_ARCHIVE_TO__0_, oldFile); } } else { if (!newFile.getAbsoluteFile().getParentFile().equals(archiveDir)) { this.archiveDir = newFile.getAbsoluteFile().getParentFile(); if (!this.archiveDir.exists()) { this.archiveDir.mkdirs(); } } if (this.config.getArchiveFileSizeLimit() != 0) { initMainArchiveId(newFile); } } try { StatArchiveDescriptor archiveDescriptor = new StatArchiveDescriptor.Builder() .setArchiveName(newFile.getPath()) .setSystemId(this.config.getSystemId()) .setSystemStartTime(this.config.getSystemStartTime()) .setSystemDirectoryPath(this.config.getSystemDirectoryPath()) .setProductDescription(this.config.getProductDescription()) .build(); newArchiver = new StatArchiveWriter(archiveDescriptor, mainLogger); newArchiver.initialize(nanosTimeStamp); } catch (GemFireIOException ex) { mainLogger.warning( LocalizedStrings.GemFireStatSampler_COULD_NOT_OPEN_STATISTIC_ARCHIVE_0_CAUSE_1, new Object[] {newFile, ex.getLocalizedMessage()}); throw ex; } } synchronized (this) { if (archiveClosed) { if (archiver != null) { removeOldArchives( newFile, mainLogger, this.config.getArchiveDiskSpaceLimit()); } } else { if (resetHandler) { if (this.debug) { this.collector.getLogWriter().info("DEBUG StatArchiveHandler#changeArchiveFile removing handler"); } this.collector.removeSampleHandler(this); } if (archiver != null) { try { archiver.close(); } catch (GemFireException ignore) { this.collector.getLogWriterI18n().warning(LocalizedStrings.GemFireStatSampler_STATISTIC_ARCHIVE_CLOSE_FAILED_BECAUSE__0, ignore.getMessage()); } removeOldArchives( newFile, mainLogger, this.config.getArchiveDiskSpaceLimit()); } } archiver = newArchiver; if (resetHandler && newArchiver != null) { if (this.debug) { this.collector.getLogWriter().info("DEBUG StatArchiveHandler#changeArchiveFile adding handler"); } this.collector.addSampleHandler(this); } } } /** * Returns the modified archive file name to use after incrementing {@link * #mainArchiveId} and {@link #archiveId} based on existing files * {@link #archiveDir}. This is only used if {@link * StatArchiveHandlerConfig#getArchiveFileSizeLimit() file size limit} has * been specified as non-zero (which enables file rolling). * * @param archive the archive file name to modify * @param archiveClosed true if archive was just being written by us; false if it was written by the previous process. * * @return the modified archive file name to use; it is modified by applying * mainArchiveId and archiveId to the name for supporting file rolling */ private File getRollingArchiveName(File archive, boolean archiveClosed) { if (mainArchiveId != -1) { // leave mainArchiveId as is. Bump archiveId. } else { archiveDir = archive.getAbsoluteFile().getParentFile(); LogWriterI18n log = this.collector.getLogWriterI18n(); boolean mainArchiveIdCalculated = false; if (log instanceof ManagerLogWriter) { ManagerLogWriter mlw = (ManagerLogWriter)log; File logDir = mlw.getLogDir(); if (archiveDir.equals(logDir)) { mainArchiveId = mlw.getMainLogId(); if (mainArchiveId > 1 && mlw.useChildLogging()) { mainArchiveId--; } mainArchiveIdCalculated = true; } } if (!mainArchiveIdCalculated) { if (!archiveDir.exists()) { if (!archiveDir.mkdirs()) { // We ignore that we couldn't create the directory. // This will be caught later when we try to create a file in this directory. } } mainArchiveId = ManagerLogWriter.calcNextMainId(archiveDir, false); mainArchiveIdCalculated = true; } if (mainArchiveId == 0) { mainArchiveId = 1; } archiveId = ManagerLogWriter.calcNextChildId(archive, mainArchiveId); if (archiveId > 0) { archiveId--; } } File result = null; do { archiveId++; StringBuffer buf = new StringBuffer(archive.getPath()); int insertIdx = buf.lastIndexOf("."); if (insertIdx == -1) { buf .append(ManagerLogWriter.formatId(mainArchiveId)) .append(ManagerLogWriter.formatId(archiveId)); } else { buf.insert(insertIdx, ManagerLogWriter.formatId(archiveId)); buf.insert(insertIdx, ManagerLogWriter.formatId(mainArchiveId)); } result = new File(buf.toString()); } while (result.exists()); if (archiveId == 1) { // see if a marker file exists. If so delete it. String markerName = archive.getPath(); int dotIdx = markerName.lastIndexOf("."); if (dotIdx != -1) { // strip the extension off markerName = markerName.substring(0, dotIdx); } StringBuffer buf = new StringBuffer(markerName); buf.append(ManagerLogWriter.formatId(mainArchiveId)) .append(ManagerLogWriter.formatId(0)) .append(".marker"); File marker = new File(buf.toString()); if (marker.exists()) { if (!marker.delete()) { // could not delete it; nothing to be done } } } if (!archiveClosed) { mainArchiveId++; archiveId = 0; // create an empty file which we can use on startup when we don't roll // to correctly rename the old archive that did not roll. String markerName = archive.getPath(); int dotIdx = markerName.lastIndexOf("."); if (dotIdx != -1) { // strip the extension off markerName = markerName.substring(0, dotIdx); } StringBuffer buf = new StringBuffer(markerName); buf.append(ManagerLogWriter.formatId(mainArchiveId)) .append(ManagerLogWriter.formatId(0)) .append(".marker"); File marker = new File(buf.toString()); if (!marker.exists()) { try { if (!marker.createNewFile()) { // could not create it; that is ok } } catch (IOException ignore) { // If we can't create the marker that is ok } } } return result; } private void initMainArchiveId(File archive) { if (mainArchiveId != -1) { // already initialized return; } archiveDir = archive.getAbsoluteFile().getParentFile(); LogWriterI18n log = this.collector.getLogWriterI18n(); boolean mainArchiveIdCalculated = false; if (log instanceof ManagerLogWriter) { ManagerLogWriter mlw = (ManagerLogWriter)log; File logDir = mlw.getLogDir(); if (archiveDir.equals(logDir)) { mainArchiveId = mlw.getMainLogId(); mainArchiveIdCalculated = true; } } if (!mainArchiveIdCalculated) { if (!archiveDir.exists()) { archiveDir.mkdirs(); } mainArchiveId = ManagerLogWriter.calcNextMainId(archiveDir, false); mainArchiveId++; mainArchiveIdCalculated = true; } if (mainArchiveId == 0) { mainArchiveId = 1; } archiveId = 0; // create an empty file which we can use on startup when we don't roll // to correctly rename the old archive that did not roll. String markerName = archive.getPath(); int dotIdx = markerName.lastIndexOf("."); if (dotIdx != -1) { // strip the extension off markerName = markerName.substring(0, dotIdx); } StringBuffer buf = new StringBuffer(markerName); buf.append(ManagerLogWriter.formatId(mainArchiveId)) .append(ManagerLogWriter.formatId(0)) .append(".marker"); File marker = new File(buf.toString()); if (!marker.exists()) { try { if (!marker.createNewFile()) { // could not create it; that is ok } } catch (IOException ignore) { // If we can't create the marker that is ok } } } /** * Modifies the desired archive file name with a main id (similar to {@link * #mainArchiveId} if the archive file's dir already contains GemFire * stat archive or log files containing a main id in the file name. * * @param archive the archive file name to modify * * @return the modified archive file name to use; it is modified by applying * the next main id if any files in the dir already have a main id in the file * name */ private static File getRenameArchiveName(File archive) { File dir = archive.getAbsoluteFile().getParentFile(); int previousMainId = ManagerLogWriter.calcNextMainId(dir, false); if (previousMainId==0) { previousMainId = 1; } previousMainId--; File result = null; do { previousMainId++; StringBuffer buf = new StringBuffer(archive.getPath()); int insertIdx = buf.lastIndexOf("."); if (insertIdx == -1) { buf .append(ManagerLogWriter.formatId(previousMainId)) .append(ManagerLogWriter.formatId(1)); } else { buf.insert(insertIdx, ManagerLogWriter.formatId(1)); buf.insert(insertIdx, ManagerLogWriter.formatId(previousMainId)); } result = new File(buf.toString()); } while (result.exists()); return result; } /** * Remove old versions of the specified archive file name in order to stay * under the specified disk space limit. Old versions of the archive file * are those that match based on using {@link #getArchivePattern(String)} * which ignores mainArchiveId and archiveId. * * @param archiveFile the archive file to remove old versions of * @param log an open log writer to use for logging * @param spaceLimit the disk space limit */ private static void removeOldArchives(File archiveFile, LogWriterI18n log, long spaceLimit) { if (spaceLimit == 0 || archiveFile == null || archiveFile.getPath().equals("")) { return; } File archiveDir = archiveFile.getAbsoluteFile().getParentFile(); ManagerLogWriter.checkDiskSpace("archive", archiveFile, spaceLimit, archiveDir, getArchivePattern(archiveFile.getName()), log); } /** * Create a regex pattern which will match the specified archive file name * even if it has a mainArchiveId and/or archiveId. * * @param name archive file name to create a regex pattern for * @return regex pattern to use in finding matching file names */ private static Pattern getArchivePattern(String name) { String ext = ""; int extIdx = name.lastIndexOf('.'); if (extIdx != -1) { ext = "\\Q" + name.substring(extIdx) + "\\E"; name = name.substring(0, extIdx); } /* name may have -DD-DD on the end of it. Trim that part off. */ int dashIdx = name.indexOf('-'); if (dashIdx != -1) { name = name.substring(0, dashIdx); } name = "\\Q" + name + "\\E" + "-\\d+-\\d+" + ext; return Pattern.compile(name); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy