View Javadoc

1   /**
2    * Copyright 2011 The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  package org.apache.hadoop.hbase.monitoring;
21  
22  import java.io.PrintWriter;
23  import java.text.SimpleDateFormat;
24  import java.util.Date;
25  import java.util.LinkedList;
26  import java.util.List;
27  
28  import com.google.common.base.Charsets;
29  import com.google.common.base.Preconditions;
30  import com.google.common.collect.Lists;
31  
32  /**
33   * A size-bounded repository of alerts, which are kept
34   * in a linked list. Alerts can be added, and they will
35   * automatically be removed one by one when the specified heap
36   * usage is exhausted.
37   */
38  public class MemoryBoundedLogMessageBuffer {
39    private final long maxSizeBytes;
40    private long usage = 0;
41    private LinkedList<LogMessage> messages;
42    
43    public MemoryBoundedLogMessageBuffer(long maxSizeBytes) {
44      Preconditions.checkArgument(
45          maxSizeBytes > 0);
46      this.maxSizeBytes = maxSizeBytes;
47      this.messages = Lists.newLinkedList();
48    }
49    
50    /**
51     * Append the given message to this buffer, automatically evicting
52     * older messages until the desired memory limit is achieved.
53     */
54    public synchronized void add(String messageText) {
55      LogMessage message = new LogMessage(messageText, System.currentTimeMillis());
56      
57      usage += message.estimateHeapUsage();
58      messages.add(message);
59      while (usage > maxSizeBytes) {
60        LogMessage removed = messages.remove();
61        usage -= removed.estimateHeapUsage();
62        assert usage >= 0;
63      }
64    }
65    
66    /**
67     * Dump the contents of the buffer to the given stream.
68     */
69    public synchronized void dumpTo(PrintWriter out) {
70      SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
71  
72      for (LogMessage msg : messages) {
73        out.write(df.format(new Date(msg.timestamp)));
74        out.write(" ");
75        out.println(new String(msg.message, Charsets.UTF_8));
76      }
77    }
78    
79    synchronized List<LogMessage> getMessages() {
80      // defensive copy
81      return Lists.newArrayList(messages);
82    }
83   
84    /**
85     * Estimate the number of bytes this buffer is currently
86     * using.
87     */
88    synchronized long estimateHeapUsage() {
89      return usage;
90    }
91    
92    private static class LogMessage {
93      /** the error text, encoded in bytes to save memory */
94      public final byte[] message;
95      public final long timestamp;
96      
97      /**
98       * Completely non-scientific estimate of how much one of these
99       * objects takes, along with the LinkedList overhead. This doesn't
100      * need to be exact, since we don't expect a ton of these alerts.
101      */
102     private static final long BASE_USAGE=100;
103     
104     public LogMessage(String message, long timestamp) {
105       this.message = message.getBytes(Charsets.UTF_8);
106       this.timestamp = timestamp;
107     }
108     
109     public long estimateHeapUsage() {
110       return message.length + BASE_USAGE;
111     }
112   }
113 
114 }