/** * * 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.hadoop.hbase.monitoring; import java.io.PrintWriter; import java.text.SimpleDateFormat; import java.util.Date; import java.util.LinkedList; import java.util.List; import org.apache.hadoop.classification.InterfaceAudience; import com.google.common.base.Charsets; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; /** * A size-bounded repository of alerts, which are kept * in a linked list. Alerts can be added, and they will * automatically be removed one by one when the specified heap * usage is exhausted. */ @InterfaceAudience.Private public class MemoryBoundedLogMessageBuffer { private final long maxSizeBytes; private long usage = 0; private LinkedList<LogMessage> messages; public MemoryBoundedLogMessageBuffer(long maxSizeBytes) { Preconditions.checkArgument( maxSizeBytes > 0); this.maxSizeBytes = maxSizeBytes; this.messages = Lists.newLinkedList(); } /** * Append the given message to this buffer, automatically evicting * older messages until the desired memory limit is achieved. */ public synchronized void add(String messageText) { LogMessage message = new LogMessage(messageText, System.currentTimeMillis()); usage += message.estimateHeapUsage(); messages.add(message); while (usage > maxSizeBytes) { LogMessage removed = messages.remove(); usage -= removed.estimateHeapUsage(); assert usage >= 0; } } /** * Dump the contents of the buffer to the given stream. */ public synchronized void dumpTo(PrintWriter out) { SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); for (LogMessage msg : messages) { out.write(df.format(new Date(msg.timestamp))); out.write(" "); out.println(new String(msg.message, Charsets.UTF_8)); } } synchronized List<LogMessage> getMessages() { // defensive copy return Lists.newArrayList(messages); } /** * Estimate the number of bytes this buffer is currently * using. */ synchronized long estimateHeapUsage() { return usage; } private static class LogMessage { /** the error text, encoded in bytes to save memory */ public final byte[] message; public final long timestamp; /** * Completely non-scientific estimate of how much one of these * objects takes, along with the LinkedList overhead. This doesn't * need to be exact, since we don't expect a ton of these alerts. */ private static final long BASE_USAGE=100; public LogMessage(String message, long timestamp) { this.message = message.getBytes(Charsets.UTF_8); this.timestamp = timestamp; } public long estimateHeapUsage() { return message.length + BASE_USAGE; } } }