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 25 import java.util.HashMap; 26 27 /** 28 * A logger that prevents spammy log. For the same log message, it logs once every 20seconds. 29 * This class is not thread-safe. 30 * <p> 31 * For convenience, use single character prefix for all messages. 32 * Here are common acronyms 33 * <ul> 34 * <li>[T]: Timout 35 * <li>[R]: Received message 36 * <li>[S]: Sent message 37 * <li>[P]: Device polling result 38 * </ul> 39 */ 40 final class HdmiLogger { 41 private static final String TAG = "HDMI"; 42 // Logging duration for same error message. 43 private static final long ERROR_LOG_DURATTION_MILLIS = 20 * 1000; // 20s 44 45 private static final boolean IS_USER_BUILD = "user".equals(Build.TYPE); 46 47 private static final ThreadLocal<HdmiLogger> sLogger = new ThreadLocal<>(); 48 49 // Key (String): log message. 50 // Value (Pair(Long, Integer)): a pair of last log time millis and the number of logMessage. 51 // Cache for warning. 52 private final HashMap<String, Pair<Long, Integer>> mWarningTimingCache = new HashMap<>(); 53 // Cache for error. 54 private final HashMap<String, Pair<Long, Integer>> mErrorTimingCache = new HashMap<>(); 55 56 private HdmiLogger() { 57 } 58 59 static final void warning(String logMessage, Object... objs) { 60 getLogger().warningInternal(toLogString(logMessage, objs)); 61 } 62 63 private void warningInternal(String logMessage) { 64 String log = updateLog(mWarningTimingCache, logMessage); 65 if (!log.isEmpty()) { 66 Slog.w(TAG, log); 67 } 68 } 69 70 static final void error(String logMessage, Object... objs) { 71 getLogger().errorInternal(toLogString(logMessage, objs)); 72 } 73 74 private void errorInternal(String logMessage) { 75 String log = updateLog(mErrorTimingCache, logMessage); 76 if (!log.isEmpty()) { 77 Slog.e(TAG, log); 78 } 79 } 80 81 static final void debug(String logMessage, Object... objs) { 82 getLogger().debugInternal(toLogString(logMessage, objs)); 83 } 84 85 private void debugInternal(String logMessage) { 86 if (true || IS_USER_BUILD) { 87 return; 88 } 89 Slog.d(TAG, logMessage); 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