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.text.TextUtils; 20 import android.util.LocalLog; 21 import android.util.Log; 22 23 import java.io.FileDescriptor; 24 import java.io.PrintWriter; 25 import java.util.StringJoiner; 26 27 28 /** 29 * Class to centralize logging functionality for tethering. 30 * 31 * All access to class methods other than dump() must be on the same thread. 32 * 33 * @hide 34 */ 35 public class SharedLog { 36 private final static int DEFAULT_MAX_RECORDS = 500; 37 private final static String COMPONENT_DELIMITER = "."; 38 39 private enum Category { 40 NONE, 41 ERROR, 42 MARK, 43 WARN, 44 }; 45 46 private final LocalLog mLocalLog; 47 // The tag to use for output to the system log. This is not output to the 48 // LocalLog because that would be redundant. 49 private final String mTag; 50 // The component (or subcomponent) of a system that is sharing this log. 51 // This can grow in depth if components call forSubComponent() to obtain 52 // their SharedLog instance. The tag is not included in the component for 53 // brevity. 54 private final String mComponent; 55 56 public SharedLog(String tag) { 57 this(DEFAULT_MAX_RECORDS, tag); 58 } 59 60 public SharedLog(int maxRecords, String tag) { 61 this(new LocalLog(maxRecords), tag, tag); 62 } 63 64 private SharedLog(LocalLog localLog, String tag, String component) { 65 mLocalLog = localLog; 66 mTag = tag; 67 mComponent = component; 68 } 69 70 public SharedLog forSubComponent(String component) { 71 if (!isRootLogInstance()) { 72 component = mComponent + COMPONENT_DELIMITER + component; 73 } 74 return new SharedLog(mLocalLog, mTag, component); 75 } 76 77 public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { 78 mLocalLog.readOnlyLocalLog().dump(fd, writer, args); 79 } 80 81 ////// 82 // Methods that both log an entry and emit it to the system log. 83 ////// 84 85 public void e(Exception e) { 86 Log.e(mTag, record(Category.ERROR, e.toString())); 87 } 88 89 public void e(String msg) { 90 Log.e(mTag, record(Category.ERROR, msg)); 91 } 92 93 public void i(String msg) { 94 Log.i(mTag, record(Category.NONE, msg)); 95 } 96 97 public void w(String msg) { 98 Log.w(mTag, record(Category.WARN, msg)); 99 } 100 101 ////// 102 // Methods that only log an entry (and do NOT emit to the system log). 103 ////// 104 105 public void log(String msg) { 106 record(Category.NONE, msg); 107 } 108 109 public void logf(String fmt, Object... args) { 110 log(String.format(fmt, args)); 111 } 112 113 public void mark(String msg) { 114 record(Category.MARK, msg); 115 } 116 117 private String record(Category category, String msg) { 118 final String entry = logLine(category, msg); 119 mLocalLog.log(entry); 120 return entry; 121 } 122 123 private String logLine(Category category, String msg) { 124 final StringJoiner sj = new StringJoiner(" "); 125 if (!isRootLogInstance()) sj.add("[" + mComponent + "]"); 126 if (category != Category.NONE) sj.add(category.toString()); 127 return sj.add(msg).toString(); 128 } 129 130 // Check whether this SharedLog instance is nominally the top level in 131 // a potential hierarchy of shared logs (the root of a tree), 132 // or is a subcomponent within the hierarchy. 133 private boolean isRootLogInstance() { 134 return TextUtils.isEmpty(mComponent) || mComponent.equals(mTag); 135 } 136 } 137