blob: 4e15668f6a3408ec0bd10baf35f5977b704458d0 [file] [log] [blame]
Beverlyc65c4082019-09-23 14:54:33 -04001/*
2 * Copyright (C) 2019 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.systemui.log;
18
19import android.os.Build;
20import android.os.SystemProperties;
21import android.util.Log;
22
Beverly3ae892b2019-12-05 14:33:09 -050023import com.android.internal.annotations.GuardedBy;
Beverlyc65c4082019-09-23 14:54:33 -040024import com.android.internal.annotations.VisibleForTesting;
25import com.android.systemui.DumpController;
26import com.android.systemui.Dumpable;
27
28import java.io.FileDescriptor;
29import java.io.PrintWriter;
30import java.text.SimpleDateFormat;
31import java.util.ArrayDeque;
32import java.util.Locale;
33
34/**
35 * Thread-safe logger in SystemUI which prints logs to logcat and stores logs to be
36 * printed by the DumpController. This is an alternative to printing directly
37 * to avoid logs being deleted by chatty. The number of logs retained is varied based on
38 * whether the build is {@link Build.IS_DEBUGGABLE}.
39 *
40 * To manually view the logs via adb:
41 * adb shell dumpsys activity service com.android.systemui/.SystemUIService \
42 * dependency DumpController <SysuiLogId>
Beverly3ae892b2019-12-05 14:33:09 -050043 *
44 * Logs can be disabled by setting the following SystemProperty and then restarting the device:
45 * adb shell setprop persist.sysui.log.enabled.<id> true/false && adb reboot
46 *
47 * @param <E> Type of event we'll be logging
Beverlyc65c4082019-09-23 14:54:33 -040048 */
Beverly3ae892b2019-12-05 14:33:09 -050049public class SysuiLog<E extends Event> implements Dumpable {
Beverlyc65c4082019-09-23 14:54:33 -040050 public static final SimpleDateFormat DATE_FORMAT =
51 new SimpleDateFormat("MM-dd HH:mm:ss", Locale.US);
52
Beverly3ae892b2019-12-05 14:33:09 -050053 protected final Object mDataLock = new Object();
Beverlyc65c4082019-09-23 14:54:33 -040054 private final String mId;
55 private final int mMaxLogs;
Beverly712a7cd2019-12-10 11:00:51 -050056 protected boolean mEnabled;
57 protected boolean mLogToLogcatEnabled;
Beverlyc65c4082019-09-23 14:54:33 -040058
Beverly3ae892b2019-12-05 14:33:09 -050059 @VisibleForTesting protected ArrayDeque<E> mTimeline;
Beverlyc65c4082019-09-23 14:54:33 -040060
61 /**
62 * Creates a SysuiLog
Beverlyc65c4082019-09-23 14:54:33 -040063 * @param dumpController where to register this logger's dumpsys
64 * @param id user-readable tag for this logger
65 * @param maxDebugLogs maximum number of logs to retain when {@link sDebuggable} is true
66 * @param maxLogs maximum number of logs to retain when {@link sDebuggable} is false
67 */
68 public SysuiLog(DumpController dumpController, String id, int maxDebugLogs, int maxLogs) {
69 this(dumpController, id, sDebuggable ? maxDebugLogs : maxLogs,
Beverly712a7cd2019-12-10 11:00:51 -050070 SystemProperties.getBoolean(SYSPROP_ENABLED_PREFIX + id, DEFAULT_ENABLED),
71 SystemProperties.getBoolean(SYSPROP_LOGCAT_ENABLED_PREFIX + id,
72 DEFAULT_LOGCAT_ENABLED));
Beverlyc65c4082019-09-23 14:54:33 -040073 }
74
75 @VisibleForTesting
Beverly712a7cd2019-12-10 11:00:51 -050076 protected SysuiLog(DumpController dumpController, String id, int maxLogs, boolean enabled,
77 boolean logcatEnabled) {
Beverlyc65c4082019-09-23 14:54:33 -040078 mId = id;
79 mMaxLogs = maxLogs;
80 mEnabled = enabled;
Beverly712a7cd2019-12-10 11:00:51 -050081 mLogToLogcatEnabled = logcatEnabled;
Beverlyc65c4082019-09-23 14:54:33 -040082 mTimeline = mEnabled ? new ArrayDeque<>(mMaxLogs) : null;
83 dumpController.registerDumpable(mId, this);
84 }
85
Beverlyc65c4082019-09-23 14:54:33 -040086 /**
87 * Logs an event to the timeline which can be printed by the dumpsys.
88 * May also log to logcat if enabled.
Beverly3ae892b2019-12-05 14:33:09 -050089 * @return the last event that was discarded from the Timeline (can be recycled)
Beverlyc65c4082019-09-23 14:54:33 -040090 */
Beverly3ae892b2019-12-05 14:33:09 -050091 public E log(E event) {
Beverlyc65c4082019-09-23 14:54:33 -040092 if (!mEnabled) {
Beverly3ae892b2019-12-05 14:33:09 -050093 return null;
Beverlyc65c4082019-09-23 14:54:33 -040094 }
95
Beverly3ae892b2019-12-05 14:33:09 -050096 E recycledEvent = null;
Beverlyc65c4082019-09-23 14:54:33 -040097 synchronized (mDataLock) {
98 if (mTimeline.size() >= mMaxLogs) {
Beverly3ae892b2019-12-05 14:33:09 -050099 recycledEvent = mTimeline.removeFirst();
Beverlyc65c4082019-09-23 14:54:33 -0400100 }
101
102 mTimeline.add(event);
103 }
104
Beverly712a7cd2019-12-10 11:00:51 -0500105 if (mLogToLogcatEnabled) {
Beverlyc65c4082019-09-23 14:54:33 -0400106 final String strEvent = eventToString(event);
107 switch (event.getLogLevel()) {
108 case Event.VERBOSE:
109 Log.v(mId, strEvent);
110 break;
111 case Event.DEBUG:
112 Log.d(mId, strEvent);
113 break;
114 case Event.ERROR:
115 Log.e(mId, strEvent);
116 break;
117 case Event.INFO:
118 Log.i(mId, strEvent);
119 break;
120 case Event.WARN:
121 Log.w(mId, strEvent);
122 break;
123 }
124 }
Beverly3ae892b2019-12-05 14:33:09 -0500125
126 if (recycledEvent != null) {
127 recycledEvent.recycle();
128 }
129
130 return recycledEvent;
Beverlyc65c4082019-09-23 14:54:33 -0400131 }
132
133 /**
Beverly264bb452019-10-29 12:43:17 -0400134 * @return user-readable string of the given event with timestamp
Beverlyc65c4082019-09-23 14:54:33 -0400135 */
Beverly3ae892b2019-12-05 14:33:09 -0500136 private String eventToTimestampedString(Event event) {
Beverlyc65c4082019-09-23 14:54:33 -0400137 StringBuilder sb = new StringBuilder();
138 sb.append(SysuiLog.DATE_FORMAT.format(event.getTimestamp()));
139 sb.append(" ");
140 sb.append(event.getMessage());
141 return sb.toString();
142 }
143
144 /**
Beverly264bb452019-10-29 12:43:17 -0400145 * @return user-readable string of the given event without a timestamp
146 */
147 public String eventToString(Event event) {
148 return event.getMessage();
149 }
150
Beverly3ae892b2019-12-05 14:33:09 -0500151 @GuardedBy("mDataLock")
Beverlyc65c4082019-09-23 14:54:33 -0400152 private void dumpTimelineLocked(PrintWriter pw) {
153 pw.println("\tTimeline:");
154
155 for (Event event : mTimeline) {
Beverly264bb452019-10-29 12:43:17 -0400156 pw.println("\t" + eventToTimestampedString(event));
Beverlyc65c4082019-09-23 14:54:33 -0400157 }
158 }
159
160 @Override
161 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
162 pw.println(mId + ":");
163
164 if (mEnabled) {
165 synchronized (mDataLock) {
166 dumpTimelineLocked(pw);
167 }
168 } else {
169 pw.print(" - Logging disabled.");
170 }
171 }
172
173 private static boolean sDebuggable = Build.IS_DEBUGGABLE;
Beverly712a7cd2019-12-10 11:00:51 -0500174 private static final String SYSPROP_ENABLED_PREFIX = "persist.sysui.log.enabled.";
175 private static final String SYSPROP_LOGCAT_ENABLED_PREFIX = "persist.sysui.log.enabled.logcat.";
Beverlyc65c4082019-09-23 14:54:33 -0400176 private static final boolean DEFAULT_ENABLED = sDebuggable;
Beverly712a7cd2019-12-10 11:00:51 -0500177 private static final boolean DEFAULT_LOGCAT_ENABLED = false;
Beverlyc65c4082019-09-23 14:54:33 -0400178 private static final int DEFAULT_MAX_DEBUG_LOGS = 100;
179 private static final int DEFAULT_MAX_LOGS = 50;
180}