1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.hdmi; 18 19 import android.annotation.Nullable; 20 import android.os.Build; 21 import android.os.SystemClock; 22 import android.util.Pair; 23 import android.util.Slog; 24 import android.util.Log; 25 26 import java.util.HashMap; 27 28 /** 29 * A logger that prevents spammy log. For the same log message, it logs once every 20seconds. 30 * This class is not thread-safe. 31 * <p> 32 * For convenience, use single character prefix for all messages. 33 * Here are common acronyms 34 * <ul> 35 * <li>[T]: Timout 36 * <li>[R]: Received message 37 * <li>[S]: Sent message 38 * <li>[P]: Device polling result 39 * </ul> 40 */ 41 final class HdmiLogger { 42 private static final String TAG = "HDMI"; 43 // Logging duration for same error message. 44 private static final long ERROR_LOG_DURATTION_MILLIS = 20 * 1000; // 20s 45 46 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 47 48 private static final ThreadLocal<HdmiLogger> sLogger = new ThreadLocal<>(); 49 50 // Key (String): log message. 51 // Value (Pair(Long, Integer)): a pair of last log time millis and the number of logMessage. 52 // Cache for warning. 53 private final HashMap<String, Pair<Long, Integer>> mWarningTimingCache = new HashMap<>(); 54 // Cache for error. 55 private final HashMap<String, Pair<Long, Integer>> mErrorTimingCache = new HashMap<>(); 56 57 private HdmiLogger() { 58 } 59 60 static final void warning(String logMessage, Object... objs) { 61 getLogger().warningInternal(toLogString(logMessage, objs)); 62 } 63 64 private void warningInternal(String logMessage) { 65 String log = updateLog(mWarningTimingCache, logMessage); 66 if (!log.isEmpty()) { 67 Slog.w(TAG, log); 68 } 69 } 70 71 static final void error(String logMessage, Object... objs) { 72 getLogger().errorInternal(toLogString(logMessage, objs)); 73 } 74 75 private void errorInternal(String logMessage) { 76 String log = updateLog(mErrorTimingCache, logMessage); 77 if (!log.isEmpty()) { 78 Slog.e(TAG, log); 79 } 80 } 81 82 static final void debug(String logMessage, Object... objs) { 83 getLogger().debugInternal(toLogString(logMessage, objs)); 84 } 85 86 private void debugInternal(String logMessage) { 87 if (DEBUG) { 88 Slog.d(TAG, logMessage); 89 } 90 } 91 92 private static final String toLogString(String logMessage, Object[] objs) { 93 if (objs.length > 0) { 94 return String.format(logMessage, objs); 95 } else { 96 return logMessage; 97 } 98 } 99 100 private static HdmiLogger getLogger() { 101 HdmiLogger logger = sLogger.get(); 102 if (logger == null) { 103 logger = new HdmiLogger(); 104 sLogger.set(logger); 105 } 106 return logger; 107 } 108 109 private static String updateLog(HashMap<String, Pair<Long, Integer>> cache, String logMessage) { 110 long curTime = SystemClock.uptimeMillis(); 111 Pair<Long, Integer> timing = cache.get(logMessage); 112 if (shouldLogNow(timing, curTime)) { 113 String log = buildMessage(logMessage, timing); 114 cache.put(logMessage, new Pair<>(curTime, 1)); 115 return log; 116 } else { 117 increaseLogCount(cache, logMessage); 118 } 119 return ""; 120 } 121 122 private static String buildMessage(String message, @Nullable Pair<Long, Integer> timing) { 123 return new StringBuilder() 124 .append("[").append(timing == null ? 1 : timing.second).append("]:") 125 .append(message).toString(); 126 } 127 128 private static void increaseLogCount(HashMap<String, Pair<Long, Integer>> cache, 129 String message) { 130 Pair<Long, Integer> timing = cache.get(message); 131 if (timing != null) { 132 cache.put(message, new Pair<>(timing.first, timing.second + 1)); 133 } 134 } 135 136 private static boolean shouldLogNow(@Nullable Pair<Long, Integer> timing, long curTime) { 137 return timing == null || curTime - timing.first > ERROR_LOG_DURATTION_MILLIS; 138 } 139 } 140