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