1 /* 2 * Copyright (C) 2017 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 android.net.util; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.text.TextUtils; 22 import android.util.LocalLog; 23 import android.util.Log; 24 25 import java.io.FileDescriptor; 26 import java.io.PrintWriter; 27 import java.util.StringJoiner; 28 29 30 /** 31 * Class to centralize logging functionality for tethering. 32 * 33 * All access to class methods other than dump() must be on the same thread. 34 * 35 * @hide 36 */ 37 public class SharedLog { 38 private static final int DEFAULT_MAX_RECORDS = 500; 39 private static final String COMPONENT_DELIMITER = "."; 40 41 private enum Category { 42 NONE, 43 ERROR, 44 MARK, 45 WARN, 46 }; 47 48 private final LocalLog mLocalLog; 49 // The tag to use for output to the system log. This is not output to the 50 // LocalLog because that would be redundant. 51 private final String mTag; 52 // The component (or subcomponent) of a system that is sharing this log. 53 // This can grow in depth if components call forSubComponent() to obtain 54 // their SharedLog instance. The tag is not included in the component for 55 // brevity. 56 private final String mComponent; 57 SharedLog(String tag)58 public SharedLog(String tag) { 59 this(DEFAULT_MAX_RECORDS, tag); 60 } 61 SharedLog(int maxRecords, String tag)62 public SharedLog(int maxRecords, String tag) { 63 this(new LocalLog(maxRecords), tag, tag); 64 } 65 SharedLog(LocalLog localLog, String tag, String component)66 private SharedLog(LocalLog localLog, String tag, String component) { 67 mLocalLog = localLog; 68 mTag = tag; 69 mComponent = component; 70 } 71 getTag()72 public String getTag() { 73 return mTag; 74 } 75 76 /** 77 * Create a SharedLog based on this log with an additional component prefix on each logged line. 78 */ forSubComponent(String component)79 public SharedLog forSubComponent(String component) { 80 if (!isRootLogInstance()) { 81 component = mComponent + COMPONENT_DELIMITER + component; 82 } 83 return new SharedLog(mLocalLog, mTag, component); 84 } 85 86 /** 87 * Dump the contents of this log. 88 * 89 * <p>This method may be called on any thread. 90 */ dump(FileDescriptor fd, PrintWriter writer, String[] args)91 public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { 92 mLocalLog.readOnlyLocalLog().dump(fd, writer, args); 93 } 94 95 ////// 96 // Methods that both log an entry and emit it to the system log. 97 ////// 98 99 /** 100 * Log an error due to an exception. This does not include the exception stacktrace. 101 * 102 * <p>The log entry will be also added to the system log. 103 * @see #e(String, Throwable) 104 */ e(Exception e)105 public void e(Exception e) { 106 Log.e(mTag, record(Category.ERROR, e.toString())); 107 } 108 109 /** 110 * Log an error message. 111 * 112 * <p>The log entry will be also added to the system log. 113 */ e(String msg)114 public void e(String msg) { 115 Log.e(mTag, record(Category.ERROR, msg)); 116 } 117 118 /** 119 * Log an error due to an exception, with the exception stacktrace if provided. 120 * 121 * <p>The error and exception message appear in the shared log, but the stacktrace is only 122 * logged in general log output (logcat). The log entry will be also added to the system log. 123 */ e(@onNull String msg, @Nullable Throwable exception)124 public void e(@NonNull String msg, @Nullable Throwable exception) { 125 if (exception == null) { 126 e(msg); 127 return; 128 } 129 Log.e(mTag, record(Category.ERROR, msg + ": " + exception.getMessage()), exception); 130 } 131 132 /** 133 * Log an informational message. 134 * 135 * <p>The log entry will be also added to the system log. 136 */ i(String msg)137 public void i(String msg) { 138 Log.i(mTag, record(Category.NONE, msg)); 139 } 140 141 /** 142 * Log a warning message. 143 * 144 * <p>The log entry will be also added to the system log. 145 */ w(String msg)146 public void w(String msg) { 147 Log.w(mTag, record(Category.WARN, msg)); 148 } 149 150 ////// 151 // Methods that only log an entry (and do NOT emit to the system log). 152 ////// 153 154 /** 155 * Log a general message to be only included in the in-memory log. 156 * 157 * <p>The log entry will *not* be added to the system log. 158 */ log(String msg)159 public void log(String msg) { 160 record(Category.NONE, msg); 161 } 162 163 /** 164 * Log a general, formatted message to be only included in the in-memory log. 165 * 166 * <p>The log entry will *not* be added to the system log. 167 * @see String#format(String, Object...) 168 */ logf(String fmt, Object... args)169 public void logf(String fmt, Object... args) { 170 log(String.format(fmt, args)); 171 } 172 173 /** 174 * Log a message with MARK level. 175 * 176 * <p>The log entry will *not* be added to the system log. 177 */ mark(String msg)178 public void mark(String msg) { 179 record(Category.MARK, msg); 180 } 181 record(Category category, String msg)182 private String record(Category category, String msg) { 183 final String entry = logLine(category, msg); 184 mLocalLog.log(entry); 185 return entry; 186 } 187 logLine(Category category, String msg)188 private String logLine(Category category, String msg) { 189 final StringJoiner sj = new StringJoiner(" "); 190 if (!isRootLogInstance()) sj.add("[" + mComponent + "]"); 191 if (category != Category.NONE) sj.add(category.toString()); 192 return sj.add(msg).toString(); 193 } 194 195 // Check whether this SharedLog instance is nominally the top level in 196 // a potential hierarchy of shared logs (the root of a tree), 197 // or is a subcomponent within the hierarchy. isRootLogInstance()198 private boolean isRootLogInstance() { 199 return TextUtils.isEmpty(mComponent) || mComponent.equals(mTag); 200 } 201 } 202