-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathLogging.java
More file actions
314 lines (251 loc) · 9.1 KB
/
Copy pathLogging.java
File metadata and controls
314 lines (251 loc) · 9.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
package ev3.exercises.library;
// Logging utility class. Sets a custom logging handler to write log messages to a file on the
// EV3 "disk". You can then pull the file back to your PC with the EV3 Control Center tool.
//
// You normally only need to call the logger.setup function in your class before any logging.
// You can use LogPrintStream to send streams to the log file.
//
// Use the Java logger object in your code to write log messages or use the simpler methods
// in this class. The methods in this class will record the location in your program where
// you call them.
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import java.util.logging.ConsoleHandler;
import java.util.logging.FileHandler;
import java.util.logging.Formatter;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
/**
* Custom logging class. Configures log system (not console) to write to a disk file.
*/
public class Logging
{
/**
* PrintStream that writes to out custom logging location.
*/
public static final PrintStream logPrintStream = new PrintStream(new LoggingOutputStream());
/**
* Used by other classes to implement logging. Other classes should log with methods on this
* object or the simpler methods of this class. The methods in this class will record the
* location in your program where you call them.
*/
public final static Logger logger = Logger.getGlobal();
// The log file can be copied from the ZTE robot controller to your PC for review by using the
// following AndroidDeBugger command in the Android Studio Terminal window:
// adb pull //storage/sdcard0/Logging.txt c:\temp\robot_logging.txt
/**
* Indicates if logging is turned on or off. Can be changed on the fly.
*/
public static boolean enabled = true;
private static FileHandler fileTxt;
//static private SimpleFormatter formatterTxt;
private static LogFormatter logFormatter;
private static boolean isSetup;
private static String packageName;
/**
* Configures our custom logging. If you don't use this custom logging, logging will go to
* the default logging location, typically the console. Call setup to turn on custom logging.
* With custom logging turned on logging goes to the console and also to the file opened in
* setup().
*/
/**
* Call to initialize our custom logging system.
* @param callingPackage The package name of class calling setup(). Use classname.class.getPackage() instead
* of hardcoding the package name.
* @param logToConsole Determines if logging goes to the console as well as the file. Logging
* to the console, which is the EV3 LCD, does work well.
* @throws IOException
*/
static public void setup(Package callingPackage, boolean logToConsole) throws IOException
{
if (isSetup)
{
logger.info("========================================================================");
return;
}
packageName = callingPackage.getName();
// get the global logger to configure it and add a file handler.
Logger logger = Logger.getGlobal();
logger.setLevel(Level.ALL);
// If we decide to redirect system.out to our log handler, then following
// code will delete the default log handler for the console to prevent
// a recursive loop. We would only redirect system.out if we only want to
// log to the file. If we delete the console handler we can skip setting
// the formatter...otherwise we set our formatter on the console logger.
Logger rootLogger = Logger.getLogger("");
Handler[] handlers = rootLogger.getHandlers();
// if (handlers[0] instanceof ConsoleHandler)
// {
// rootLogger.removeHandler(handlers[0]);
// return;
// }
logFormatter = new LogFormatter();
// Set our formatter on the console log handler.
if (handlers[0] instanceof ConsoleHandler) handlers[0].setFormatter(logFormatter);
// Now create a handler to log to a file on EV3 "disk".
//if (true) throw new IOException("Test Exception");
fileTxt = new FileHandler("Logging.txt", 0 , 1);
fileTxt.setFormatter(logFormatter);
logger.addHandler(fileTxt);
logger.setUseParentHandlers(logToConsole);
isSetup = true;
}
/**
* Flush logged data to disk file. Not normally needed.
*/
public static void flushlog()
{
fileTxt.flush();
}
// Our custom formatter for logging output.
private static class LogFormatter extends Formatter
{
public String format(LogRecord rec)
{
StringBuffer buf = new StringBuffer(1024);
buf.append(String.format("<%d>", rec.getThreadID())); //Thread.currentThread().getId()));
buf.append(formatDate(rec.getMillis()));
buf.append(" ");
buf.append(formatMessage(rec));
buf.append("\r\n");
return buf.toString();
}
private String formatDate(long milliseconds)
{
SimpleDateFormat dateFormat = new SimpleDateFormat("hh:mm:ss:S");
dateFormat.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles"));
Date resultDate = new Date(milliseconds);
return dateFormat.format(resultDate);
}
}
/**
* Log blank line with program location.
*/
public static void log()
{
if (!enabled) return;
logger.log(Level.INFO, currentMethod(2));
}
/**
* Log message with optional formatting and program location.
* @param message message with optional format specifiers for listed parameters
* @param parms parameter list matching format specifiers
*/
public static void log(String message, Object... parms)
{
if (!enabled) return;
logger.log(Level.INFO, String.format("%s: %s", currentMethod(2), String.format(message, parms)));
}
/**
* Log message with optional formatting and no program location.
* @param message message with optional format specifiers for listed parameters
* @param parms parameter list matching format specifiers
*/
public static void logNoMethod(String message, Object... parms)
{
if (!enabled) return;
logger.log(Level.INFO, String.format(message, parms));
}
/**
* Log message with no formatting and program location.
* @param message message with optional format specifiers for listed parameters
*/
public static void logNoFormat(String message)
{
if (!enabled) return;
logger.log(Level.INFO, String.format("%s: %s", currentMethod(2), message));
}
/**
* Log message with no formatting and no program location.
* @param message message with optional format specifiers for listed parameters
*/
public static void logNoFormatNoMethod(String message)
{
if (!enabled) return;
logger.log(Level.INFO, message);
}
/**
* Returns program location where call to this method is located.
*/
public static String currentMethod()
{
return currentMethod(2);
}
private static String currentMethod(Integer level)
{
StackTraceElement stackTrace[];
stackTrace = new Throwable().getStackTrace();
try
{
return stackTrace[level].toString().split(packageName + ".")[1];
}
catch (Exception e)
{
try
{
return stackTrace[level].toString().split("library.")[1];
}
catch (Exception e1) {e1.printStackTrace(); return "";}
}
}
// An output stream that writes to our logging system. Writes data with flush on
// flush call or on a newline character in the stream.
private static class LoggingOutputStream extends OutputStream
{
private static final int DEFAULT_BUFFER_LENGTH = 2048;
private boolean hasBeenClosed = false;
private byte[] buf;
private int count, curBufLength;
public LoggingOutputStream()
{
curBufLength = DEFAULT_BUFFER_LENGTH;
buf = new byte[curBufLength];
count = 0;
}
public void write(final int b) throws IOException
{
if (!enabled) return;
if (hasBeenClosed) {throw new IOException("The stream has been closed.");}
// don't log nulls
if (b == 0) return;
// force flush on newline character, dropping the newline.
if ((byte) b == '\n')
{
flush();
return;
}
// would this be writing past the buffer?
if (count == curBufLength)
{
// grow the buffer
final int newBufLength = curBufLength + DEFAULT_BUFFER_LENGTH;
final byte[] newBuf = new byte[newBufLength];
System.arraycopy(buf, 0, newBuf, 0, curBufLength);
buf = newBuf;
curBufLength = newBufLength;
}
buf[count] = (byte) b;
count++;
}
public void flush()
{
if (count == 0) return;
final byte[] bytes = new byte[count];
System.arraycopy(buf, 0, bytes, 0, count);
String str = new String(bytes);
logNoFormatNoMethod(str);
count = 0;
}
public void close()
{
flush();
hasBeenClosed = true;
}
}
}