-
Notifications
You must be signed in to change notification settings - Fork 0
/
Logging.java
255 lines (201 loc) · 8.06 KB
/
Logging.java
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
package frc.robot;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.TimeZone;
import java.util.logging.ConsoleHandler;
import java.util.logging.Handler;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.logging.Formatter;
import java.util.logging.LogRecord;
import edu.wpi.first.wpilibj.DriverStation;
public class Logging
{
/**
* Open print stream that writes to the log file. Example of use:
* exception.printStackTrace(Util.logPrintStream);
*/
public static final PrintStream logPrintStream = new PrintStream(new LoggingOutputStream());
/**
* Logging class for use by other classes to log though this custom logging scheme. All
* logging should be done by calls to methods on this class instance or with the
* convenience methods of the Logging class.
*/
public final static Logger logger = Logger.getGlobal();
// Private constructor means this class cannot be instantiated. All access is static.
private Logging()
{
}
/**
* Configures and holds (static) classes for our custom logging system.
* Call setup() method to initialize logging.
*/
public static class CustomLogger
{
static private FileHandler fileTxt;
static private LogFormatter logFormatter;
/**
* Initializes our logging system.
* Call before using any logging methods.
*/
static public void setup() throws IOException
{
// 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 hanlder 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 roboRio "disk".
//if (true) throw new IOException("Test Exception");
if (new File("/home/lvuser/Logging.txt.99").exists() != true)
fileTxt = new FileHandler("/home/lvuser/Logging.txt");
else
throw new IOException("Max number of log files reached.");
fileTxt.setFormatter(logFormatter);
logger.addHandler(fileTxt);
}
}
// Our custom formatter for logging output.
private static class LogFormatter extends Formatter
{
SimpleDateFormat dateFormat = new SimpleDateFormat("hh:mm:ss:S");
public LogFormatter()
{
dateFormat.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles"));
}
public String format(LogRecord rec)
{
StringBuffer buf = new StringBuffer(1024);
buf.append(String.format("<%d>", rec.getThreadID()));
buf.append(dateFormat.format(new Date(rec.getMillis())));
buf.append(" ");
buf.append(formatMessage(rec));
buf.append("\n");
return buf.toString();
}
}
// 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 (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);
consoleLog(str);
count = 0;
}
public void close()
{
flush();
hasBeenClosed = true;
}
}
/**
* 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();
// This scheme depends on having one level in the package name between
// frc and the class name, ie: frc.robot.Logging.method. New levels
// will require rewrite.
try
{
String method = stackTrace[level].toString().split("frc.")[1];
int startPos = method.indexOf(".") + 1;
return method.substring(startPos);
}
catch (Throwable e)
{
return "method not found";
}
}
/**
* Write message to console log 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 consoleLog(String message, Object... parms)
{
// logs to the console as well as our log file on RR disk.
logger.log(Level.INFO, String.format("robot: %s: %s", currentMethod(2), String.format(message, parms)));
}
/**
* Write blank line with program location to the console log.
*/
public static void consoleLog()
{
// logs to the console as well as our log file on RR disk.
logger.log(Level.INFO, String.format("robot: %s", currentMethod(2)));
}
/**
* Write exception message to DS console window and exception stack trace to
* log file.
* @param e The exception to log.
*/
public static void logException(Throwable e)
{
DriverStation.reportError(e.toString(), false);
e.printStackTrace(Logging.logPrintStream);
}
}