/* * 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.accumulo.server.metrics; import static java.nio.charset.StandardCharsets.UTF_8; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Writer; import java.lang.management.ManagementFactory; import java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.ConcurrentHashMap; import javax.management.MBeanServer; import javax.management.ObjectName; import javax.management.StandardMBean; import org.apache.commons.lang.builder.ToStringBuilder; import org.apache.commons.lang.builder.ToStringStyle; import org.apache.commons.lang.time.DateUtils; public abstract class AbstractMetricsImpl implements Metrics { public static class Metric { private long count = 0; private long avg = 0; private long min = 0; private long max = 0; public long getCount() { return count; } public long getAvg() { return avg; } public long getMin() { return min; } public long getMax() { return max; } public void incCount() { count++; } public void addAvg(long a) { if (a < 0) return; avg = (long) ((avg * .8) + (a * .2)); } public void addMin(long a) { if (a < 0) return; min = Math.min(min, a); } public void addMax(long a) { if (a < 0) return; max = Math.max(max, a); } @Override public String toString() { return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).append("count", count).append("average", avg).append("minimum", min) .append("maximum", max).toString(); } } static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AbstractMetricsImpl.class); private static ConcurrentHashMap<String,Metric> registry = new ConcurrentHashMap<>(); private boolean currentlyLogging = false; private File logDir = null; private String metricsPrefix = null; private Date today = new Date(); private File logFile = null; private Writer logWriter = null; private SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd"); private SimpleDateFormat logFormatter = new SimpleDateFormat("yyyyMMddhhmmssz"); private MetricsConfiguration config = null; public AbstractMetricsImpl() { this.metricsPrefix = getMetricsPrefix(); config = new MetricsConfiguration(metricsPrefix); } /** * Registers a StandardMBean with the MBean Server */ public void register(StandardMBean mbean) throws Exception { // Register this object with the MBeanServer MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); if (null == getObjectName()) throw new IllegalArgumentException("MBean object name must be set."); mbs.registerMBean(mbean, getObjectName()); setupLogging(); } /** * Registers this MBean with the MBean Server */ @Override public void register() throws Exception { // Register this object with the MBeanServer MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); if (null == getObjectName()) throw new IllegalArgumentException("MBean object name must be set."); mbs.registerMBean(this, getObjectName()); setupLogging(); } public void createMetric(String name) { registry.put(name, new Metric()); } public long getMetricCount(String name) { return registry.get(name).getCount(); } public long getMetricAvg(String name) { return registry.get(name).getAvg(); } public long getMetricMin(String name) { return registry.get(name).getMin(); } public long getMetricMax(String name) { return registry.get(name).getMax(); } private void setupLogging() throws IOException { if (null == config.getMetricsConfiguration()) return; // If we are already logging, then return if (!currentlyLogging && config.getMetricsConfiguration().getBoolean(metricsPrefix + ".logging", false)) { // Check to see if directory exists, else make it String mDir = config.getMetricsConfiguration().getString("logging.dir"); if (null != mDir) { File dir = new File(mDir); if (!dir.isDirectory()) if (!dir.mkdir()) log.warn("Could not create log directory: " + dir); logDir = dir; // Create new log file startNewLog(); } currentlyLogging = true; } } private void startNewLog() throws IOException { if (null != logWriter) { logWriter.flush(); logWriter.close(); } logFile = new File(logDir, metricsPrefix + "-" + formatter.format(today) + ".log"); if (!logFile.exists()) { if (!logFile.createNewFile()) { log.error("Unable to create new log file"); currentlyLogging = false; return; } } logWriter = new OutputStreamWriter(new FileOutputStream(logFile, true), UTF_8); } private void writeToLog(String name) throws IOException { if (null == logWriter) return; // Increment the date if we have to Date now = new Date(); if (!DateUtils.isSameDay(today, now)) { today = now; startNewLog(); } logWriter.append(logFormatter.format(now)).append(" Metric: ").append(name).append(": ").append(registry.get(name).toString()).append("\n"); } @Override public void add(String name, long time) { if (isEnabled()) { registry.get(name).incCount(); registry.get(name).addAvg(time); registry.get(name).addMin(time); registry.get(name).addMax(time); // If we are not currently logging and should be, then initialize if (!currentlyLogging && config.getMetricsConfiguration().getBoolean(metricsPrefix + ".logging", false)) { try { setupLogging(); } catch (IOException ioe) { log.error("Error setting up log", ioe); } } else if (currentlyLogging && !config.getMetricsConfiguration().getBoolean(metricsPrefix + ".logging", false)) { // if we are currently logging and shouldn't be, then close logs try { logWriter.flush(); logWriter.close(); logWriter = null; logFile = null; } catch (Exception e) { log.error("Error stopping metrics logging", e); } currentlyLogging = false; } if (currentlyLogging) { try { writeToLog(name); } catch (IOException ioe) { log.error("Error writing to metrics log", ioe); } } } } @Override public boolean isEnabled() { return config.isEnabled(); } protected abstract ObjectName getObjectName(); protected abstract String getMetricsPrefix(); @Override protected void finalize() { if (null != logWriter) { try { logWriter.close(); } catch (Exception e) { // do nothing } } } }