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