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}