2
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
6
* The contents of this file are subject to the terms of either the GNU
7
* General Public License Version 2 only ("GPL") or the Common
8
* Development and Distribution License("CDDL") (collectively, the
9
* "License"). You may not use this file except in compliance with the
10
* License. You can obtain a copy of the License at
11
* http://www.netbeans.org/cddl-gplv2.html
12
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13
* specific language governing permissions and limitations under the
14
* License. When distributing the software, include this License Header
15
* Notice in each file and include the License file at
16
* nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
17
* particular file as subject to the "Classpath" exception as provided
18
* by Sun in the GPL Version 2 section of the License file that
19
* accompanied this code. If applicable, add the following below the
20
* License Header, with the fields enclosed by brackets [] replaced by
21
* your own identifying information:
22
* "Portions Copyrighted [year] [name of copyright owner]"
26
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
29
package org.netbeans.lib.uihandler;
31
import java.io.ByteArrayInputStream;
32
import java.io.IOException;
33
import java.io.InputStream;
34
import java.io.OutputStream;
35
import java.io.PushbackInputStream;
36
import java.io.SequenceInputStream;
37
import java.util.ArrayList;
38
import java.util.Collections;
39
import java.util.EnumMap;
40
import java.util.Enumeration;
41
import java.util.LinkedList;
42
import java.util.List;
44
import java.util.MissingResourceException;
45
import java.util.Queue;
46
import java.util.ResourceBundle;
47
import java.util.logging.Formatter;
48
import java.util.logging.Handler;
49
import java.util.logging.Level;
50
import java.util.logging.LogRecord;
51
import java.util.logging.Logger;
52
import java.util.zip.GZIPInputStream;
53
import javax.xml.parsers.ParserConfigurationException;
54
import javax.xml.parsers.SAXParser;
55
import javax.xml.parsers.SAXParserFactory;
56
import org.openide.util.NbBundle;
57
import org.xml.sax.Attributes;
58
import org.xml.sax.Locator;
59
import org.xml.sax.SAXException;
60
import org.xml.sax.SAXParseException;
61
import org.xml.sax.helpers.DefaultHandler;
63
/** Can persist and read log records from streams.
65
* @author Jaroslav Tulach
67
public final class LogRecords {
68
private LogRecords() {
71
private static final Logger LOG = Logger.getLogger(LogRecords.class.getName());
73
private static final Formatter FORMATTER = new LogFormatter();
75
/** Inspects the log record and decorates its content.
76
* @param r the log record
77
* @param d callback to be called with inspected values
79
public static void decorate(LogRecord r, Decorable d) {
80
Decorations.decorate(r, d);
83
public static void write(OutputStream os, LogRecord rec) throws IOException {
84
String formated = FORMATTER.format(rec);
85
byte[] arr = formated.getBytes("utf-8");
89
public static void scan(InputStream is, Handler h) throws IOException {
90
PushbackInputStream wrap = new PushbackInputStream(is, 32);
91
byte[] arr = new byte[5];
92
int len = wrap.read(arr);
96
wrap.unread(arr, 0, len);
97
if (arr[0] == 0x1f && arr[1] == -117) {
98
wrap = new PushbackInputStream(new GZIPInputStream(wrap), 32);
103
wrap.unread(arr, 0, len);
114
ByteArrayInputStream header = new ByteArrayInputStream(
115
"<?xml version='1.0' encoding='UTF-8'?><uigestures version='1.0'>".getBytes()
117
ByteArrayInputStream footer = new ByteArrayInputStream(
118
"</uigestures>".getBytes()
120
is = new SequenceInputStream(
121
new SequenceInputStream(header, wrap),
126
SAXParserFactory f = SAXParserFactory.newInstance();
127
f.setValidating(false);
130
f.setFeature("http://apache.org/xml/features/continue-after-fatal-error", true); // NOI18N
131
p = f.newSAXParser();
132
} catch (ParserConfigurationException ex) {
133
LOG.log(Level.SEVERE, null, ex);
134
throw (IOException)new IOException(ex.getMessage()).initCause(ex);
135
} catch (SAXException ex) {
136
LOG.log(Level.SEVERE, null, ex);
137
throw (IOException)new IOException(ex.getMessage()).initCause(ex);
140
Parser parser = new Parser(h);
143
} catch (SAXException ex) {
144
LOG.log(Level.WARNING, null, ex);
145
throw (IOException)new IOException(ex.getMessage()).initCause(ex);
146
} catch (InternalError error){
147
LOG.log(Level.WARNING, "INPUT FILE CORRUPTION", error);
148
} catch (IOException ex) {
150
} catch (RuntimeException ex) {
151
LOG.log(Level.WARNING, "INPUT FILE CORRUPTION", ex);
155
static Level parseLevel(String lev) {
156
return "USER".equals(lev) ? Level.SEVERE : Level.parse(lev);
159
private static final class Parser extends DefaultHandler {
160
private Handler callback;
161
private static enum Elem {
162
UIGESTURES, RECORD, DATE, MILLIS, SEQUENCE, LEVEL, THREAD,
163
MESSAGE, KEY, PARAM, FRAME, CLASS, METHOD, LOGGER, EXCEPTION, LINE,
166
public String parse(Map<Elem,String> values) {
167
String v = values.get(this);
171
private Map<Elem,String> values = new EnumMap<Elem,String>(Elem.class);
172
private Elem current;
173
private FakeException currentEx;
174
private Queue<FakeException> exceptions;
175
private List<String> params;
176
private StringBuilder chars = new StringBuilder();
177
private int fatalErrors;
179
public Parser(Handler c) {
184
public void setDocumentLocator(Locator locator) {
187
public void startDocument() throws SAXException {
190
public void endDocument() throws SAXException {
194
public void startPrefixMapping(String prefix, String uri) throws SAXException {
197
public void endPrefixMapping(String prefix) throws SAXException {
200
public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
201
if (LOG.isLoggable(Level.FINEST)) {
202
LOG.log(Level.FINEST, "uri: {0} localName: {1} qName: {2} atts: {3}", new Object[] { uri, localName, qName, atts });
206
current = Elem.valueOf(qName.toUpperCase());
207
if (current == Elem.EXCEPTION) {
208
currentEx = new FakeException(new EnumMap<Elem,String>(values));
210
} catch (IllegalArgumentException ex) {
211
LOG.log(Level.FINE, "Uknown tag " + qName, ex);
214
chars = new StringBuilder();
217
public void endElement(String uri, String localName, String qName) throws SAXException {
218
if (current != null) {
219
String v = chars.toString();
220
values.put(current, v);
221
if (current == Elem.PARAM) {
222
if (params == null) {
223
params = new ArrayList<String>();
226
if (params.size() > 1500) {
227
LOG.severe("Too long params when reading a record. Deleting few. Msg: " + Elem.MESSAGE.parse(values)); // NOI18N
228
for (String p : params) {
236
chars = new StringBuilder();
238
if (currentEx != null && currentEx.values != null) {
239
if ("frame".equals(qName)) { // NOI18N
240
String line = Elem.LINE.parse(values);
241
StackTraceElement elem = new StackTraceElement(
242
Elem.CLASS.parse(values),
243
Elem.METHOD.parse(values),
244
Elem.FILE.parse(values),
245
line == null ? -1 : Integer.parseInt(line)
247
currentEx.trace.add(elem);
248
values.remove(Elem.CLASS);
249
values.remove(Elem.METHOD);
250
values.remove(Elem.LINE);
252
if ("exception".equals(qName)) {
253
currentEx.message = values.get(Elem.MESSAGE);
254
String more = values.get(Elem.MORE);
255
if (more != null) currentEx.more = Integer.parseInt(more);
256
if (exceptions == null){
257
exceptions = new LinkedList<FakeException>();
259
exceptions.add(currentEx);
260
values = currentEx.values;
266
if ("record".equals(qName)) { // NOI18N
267
String millis = Elem.MILLIS.parse(values);
268
String seq = Elem.SEQUENCE.parse(values);
269
String lev = Elem.LEVEL.parse(values);
270
String thread = Elem.THREAD.parse(values);
271
String msg = Elem.MESSAGE.parse(values);
272
String key = Elem.KEY.parse(values);
273
String catalog = Elem.CATALOG.parse(values);
276
LogRecord r = new LogRecord(parseLevel(lev), key != null && catalog != null ? key : msg);
278
r.setThreadID(Integer.parseInt(thread));
279
} catch (NumberFormatException ex) {
280
LOG.log(Level.WARNING, ex.getMessage(), ex);
282
r.setSequenceNumber(Long.parseLong(seq));
283
r.setMillis(Long.parseLong(millis));
284
r.setResourceBundleName(key);
285
if (catalog != null && key != null) {
286
r.setResourceBundleName(catalog);
287
if (!"<null>".equals(catalog)) { // NOI18N
289
ResourceBundle b = NbBundle.getBundle(catalog);
291
// ok, the key is there
292
r.setResourceBundle(b);
293
} catch (MissingResourceException e) {
294
LOG.log(Level.CONFIG, "Cannot find resource bundle {0} for key {1}", new Object[] { catalog, key });
295
r.setResourceBundle(new FakeBundle(key, msg));
298
LOG.log(Level.CONFIG, "Cannot find resource bundle <null> for key {1}", key);
301
if (params != null) {
302
r.setParameters(params.toArray());
304
if (exceptions != null) {
305
r.setThrown(createThrown(null));
306
// exceptions = null; should be empty after poll
319
/** set first element of exceptions as a result of this calling and
320
* recursively fill it's cause
322
private FakeException createThrown(FakeException last){
323
if (exceptions.size()==0) {
326
FakeException result = exceptions.poll();
327
if ((result!= null) && (result.getMore()!= 0)){
328
assert last != null : "IF MORE IS NOT 0, LAST MUST BE SET NOT NULL";
329
StackTraceElement[] trace = last.getStackTrace();
330
for (int i = trace.length - result.getMore(); i < trace.length; i++){
331
result.trace.add(trace[i]);// fill the rest of stacktrace
334
FakeException cause = createThrown(result);
335
result.initCause(cause);
339
public void characters(char[] ch, int start, int length) throws SAXException {
340
chars.append(ch, start, length);
343
public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {
346
public void processingInstruction(String target, String data) throws SAXException {
349
public void skippedEntity(String name) throws SAXException {
352
public void fatalError(SAXParseException e) throws SAXException {
353
if (fatalErrors++ > 1000) {
360
private static final class FakeBundle extends ResourceBundle {
362
private String value;
364
public FakeBundle(String key, String value) {
370
protected Object handleGetObject(String arg0) {
371
if (key.equals(arg0)) {
378
public Enumeration<String> getKeys() {
379
return Collections.enumeration(Collections.singleton(key));
381
} // end of FakeBundle
383
private static final class FakeException extends Exception {
384
final List<StackTraceElement> trace = new ArrayList<StackTraceElement>();
385
Map<Parser.Elem,String> values;
389
public FakeException(Map<Parser.Elem,String> values) {
390
this.values = values;
394
public StackTraceElement[] getStackTrace() {
395
return trace.toArray(new StackTraceElement[0]);
398
public String getMessage() {
402
public int getMore(){
407
* org.netbeans.lib.uihandler.LogRecords$FakeException: NullPointerException ...
408
* is not the best message - it's better to suppress FakeException
410
public String toString(){
414
} // end of FakeException