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 17package com.android.server.hdmi; 18 19import android.annotation.Nullable; 20import android.os.Build; 21import android.os.SystemClock; 22import android.util.Pair; 23import android.util.Slog; 24import android.util.Log; 25 26import 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 */ 41final 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 private static final boolean IS_USER_BUILD = "user".equals(Build.TYPE); 48 49 private static final ThreadLocal<HdmiLogger> sLogger = new ThreadLocal<>(); 50 51 // Key (String): log message. 52 // Value (Pair(Long, Integer)): a pair of last log time millis and the number of logMessage. 53 // Cache for warning. 54 private final HashMap<String, Pair<Long, Integer>> mWarningTimingCache = new HashMap<>(); 55 // Cache for error. 56 private final HashMap<String, Pair<Long, Integer>> mErrorTimingCache = new HashMap<>(); 57 58 private HdmiLogger() { 59 } 60 61 static final void warning(String logMessage, Object... objs) { 62 getLogger().warningInternal(toLogString(logMessage, objs)); 63 } 64 65 private void warningInternal(String logMessage) { 66 String log = updateLog(mWarningTimingCache, logMessage); 67 if (!log.isEmpty()) { 68 Slog.w(TAG, log); 69 } 70 } 71 72 static final void error(String logMessage, Object... objs) { 73 getLogger().errorInternal(toLogString(logMessage, objs)); 74 } 75 76 private void errorInternal(String logMessage) { 77 String log = updateLog(mErrorTimingCache, logMessage); 78 if (!log.isEmpty()) { 79 Slog.e(TAG, log); 80 } 81 } 82 83 static final void debug(String logMessage, Object... objs) { 84 getLogger().debugInternal(toLogString(logMessage, objs)); 85 } 86 87 private void debugInternal(String logMessage) { 88 if (DEBUG) { 89 Slog.d(TAG, logMessage); 90 } 91 } 92 93 private static final String toLogString(String logMessage, Object[] objs) { 94 if (objs.length > 0) { 95 return String.format(logMessage, objs); 96 } else { 97 return logMessage; 98 } 99 } 100 101 private static HdmiLogger getLogger() { 102 HdmiLogger logger = sLogger.get(); 103 if (logger == null) { 104 logger = new HdmiLogger(); 105 sLogger.set(logger); 106 } 107 return logger; 108 } 109 110 private static String updateLog(HashMap<String, Pair<Long, Integer>> cache, String logMessage) { 111 long curTime = SystemClock.uptimeMillis(); 112 Pair<Long, Integer> timing = cache.get(logMessage); 113 if (shouldLogNow(timing, curTime)) { 114 String log = buildMessage(logMessage, timing); 115 cache.put(logMessage, new Pair<>(curTime, 1)); 116 return log; 117 } else { 118 increaseLogCount(cache, logMessage); 119 } 120 return ""; 121 } 122 123 private static String buildMessage(String message, @Nullable Pair<Long, Integer> timing) { 124 return new StringBuilder() 125 .append("[").append(timing == null ? 1 : timing.second).append("]:") 126 .append(message).toString(); 127 } 128 129 private static void increaseLogCount(HashMap<String, Pair<Long, Integer>> cache, 130 String message) { 131 Pair<Long, Integer> timing = cache.get(message); 132 if (timing != null) { 133 cache.put(message, new Pair<>(timing.first, timing.second + 1)); 134 } 135 } 136 137 private static boolean shouldLogNow(@Nullable Pair<Long, Integer> timing, long curTime) { 138 return timing == null || curTime - timing.first > ERROR_LOG_DURATTION_MILLIS; 139 } 140} 141