001/**
002 *
003 * Licensed to the Apache Software Foundation (ASF) under one
004 * or more contributor license agreements.  See the NOTICE file
005 * distributed with this work for additional information
006 * regarding copyright ownership.  The ASF licenses this file
007 * to you under the Apache License, Version 2.0 (the
008 * "License"); you may not use this file except in compliance
009 * with the License.  You may obtain a copy of the License at
010 *
011 *     http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 */
019package org.apache.hadoop.hbase.monitoring;
020
021import java.io.PrintWriter;
022import java.text.SimpleDateFormat;
023import java.util.Date;
024import java.util.LinkedList;
025import java.util.List;
026
027import org.apache.yetus.audience.InterfaceAudience;
028
029import org.apache.hbase.thirdparty.com.google.common.base.Charsets;
030import org.apache.hbase.thirdparty.com.google.common.base.Preconditions;
031import org.apache.hbase.thirdparty.com.google.common.collect.Lists;
032
033/**
034 * A size-bounded repository of alerts, which are kept
035 * in a linked list. Alerts can be added, and they will
036 * automatically be removed one by one when the specified heap
037 * usage is exhausted.
038 */
039@InterfaceAudience.Private
040public class MemoryBoundedLogMessageBuffer {
041  private final long maxSizeBytes;
042  private long usage = 0;
043  private LinkedList<LogMessage> messages;
044  
045  public MemoryBoundedLogMessageBuffer(long maxSizeBytes) {
046    Preconditions.checkArgument(
047        maxSizeBytes > 0);
048    this.maxSizeBytes = maxSizeBytes;
049    this.messages = Lists.newLinkedList();
050  }
051  
052  /**
053   * Append the given message to this buffer, automatically evicting
054   * older messages until the desired memory limit is achieved.
055   */
056  public synchronized void add(String messageText) {
057    LogMessage message = new LogMessage(messageText, System.currentTimeMillis());
058    
059    usage += message.estimateHeapUsage();
060    messages.add(message);
061    while (usage > maxSizeBytes) {
062      LogMessage removed = messages.remove();
063      usage -= removed.estimateHeapUsage();
064      assert usage >= 0;
065    }
066  }
067  
068  /**
069   * Dump the contents of the buffer to the given stream.
070   */
071  public synchronized void dumpTo(PrintWriter out) {
072    SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
073
074    for (LogMessage msg : messages) {
075      out.write(df.format(new Date(msg.timestamp)));
076      out.write(" ");
077      out.println(new String(msg.message, Charsets.UTF_8));
078    }
079  }
080  
081  synchronized List<LogMessage> getMessages() {
082    // defensive copy
083    return Lists.newArrayList(messages);
084  }
085 
086  /**
087   * Estimate the number of bytes this buffer is currently
088   * using.
089   */
090  synchronized long estimateHeapUsage() {
091    return usage;
092  }
093  
094  private static class LogMessage {
095    /** the error text, encoded in bytes to save memory */
096    public final byte[] message;
097    public final long timestamp;
098    
099    /**
100     * Completely non-scientific estimate of how much one of these
101     * objects takes, along with the LinkedList overhead. This doesn't
102     * need to be exact, since we don't expect a ton of these alerts.
103     */
104    private static final long BASE_USAGE=100;
105    
106    public LogMessage(String message, long timestamp) {
107      this.message = message.getBytes(Charsets.UTF_8);
108      this.timestamp = timestamp;
109    }
110    
111    public long estimateHeapUsage() {
112      return message.length + BASE_USAGE;
113    }
114  }
115
116}