blob: 7d10731d5627922397f4a09f276aacdb8cb599da [file] [log] [blame]
// ***************************************************************************************************************************
// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file *
// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file *
// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance *
// * with the License. You may obtain a copy of the License at *
// * *
// * http://www.apache.org/licenses/LICENSE-2.0 *
// * *
// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an *
// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the *
// * specific language governing permissions and limitations under the License. *
// ***************************************************************************************************************************
package org.apache.juneau.microservice.resources;
import java.io.*;
import java.nio.charset.*;
import java.text.*;
import java.util.*;
import java.util.regex.*;
/**
* Utility class for reading log files.
*
* <p>
* Provides the capability of returning splices of log files based on dates and filtering based on thread and logger
* names.
*/
public final class LogParser implements Iterable<LogParser.Entry>, Iterator<LogParser.Entry>, Closeable {
private BufferedReader br;
LogEntryFormatter formatter;
Date start, end;
Set<String> loggerFilter, severityFilter;
String threadFilter;
private Entry next;
/**
* Constructor.
*
* @param formatter The log entry formatter.
* @param f The log file.
* @param start Don't return rows before this date. If <jk>null</jk>, start from the beginning of the file.
* @param end Don't return rows after this date. If <jk>null</jk>, go to the end of the file.
* @param thread Only return log entries with this thread name.
* @param loggers Only return log entries produced by these loggers (simple class names).
* @param severity Only return log entries with the specified severity.
* @throws IOException Thrown by underlying stream.
*/
public LogParser(LogEntryFormatter formatter, File f, Date start, Date end, String thread, String[] loggers, String[] severity) throws IOException {
br = new BufferedReader(new InputStreamReader(new FileInputStream(f), Charset.defaultCharset()));
this.formatter = formatter;
this.start = start;
this.end = end;
this.threadFilter = thread;
if (loggers != null)
this.loggerFilter = new HashSet<>(Arrays.asList(loggers));
if (severity != null)
this.severityFilter = new HashSet<>(Arrays.asList(severity));
// Find the first line.
String line;
while (next == null && (line = br.readLine()) != null) {
Entry e = new Entry(line);
if (e.matches())
next = e;
}
}
@Override /* Iterator */
public boolean hasNext() {
return next != null;
}
@Override /* Iterator */
public Entry next() {
Entry current = next;
Entry prev = next;
try {
next = null;
String line = null;
while (next == null && (line = br.readLine()) != null) {
Entry e = new Entry(line);
if (e.isRecord) {
if (e.matches())
next = e;
prev = null;
} else {
if (prev != null)
prev.addText(e.line);
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
return current;
}
@Override /* Iterator */
public void remove() {
throw new NoSuchMethodError();
}
@Override /* Iterable */
public Iterator<Entry> iterator() {
return this;
}
@Override /* Closeable */
public void close() throws IOException {
br.close();
}
/**
* Serializes the contents of the parsed log file to the specified writer and then closes the underlying reader.
*
* @param w The writer to write the log file to.
* @throws IOException Thrown by underlying stream.
*/
public void writeTo(Writer w) throws IOException {
try {
if (! hasNext())
w.append("[EMPTY]");
else for (LogParser.Entry le : this)
le.append(w);
} finally {
close();
}
}
/**
* Represents a single line from the log file.
*/
@SuppressWarnings("javadoc")
public final class Entry {
public Date date;
public String severity, logger;
protected String line, text;
protected String thread;
protected List<String> additionalText;
protected boolean isRecord;
Entry(String line) throws IOException {
try {
this.line = line;
Matcher m = formatter.getLogEntryPattern().matcher(line);
if (m.matches()) {
isRecord = true;
String s = formatter.getField("date", m);
if (s != null)
date = formatter.getDateFormat().parse(s);
thread = formatter.getField("thread", m);
severity = formatter.getField("level", m);
logger = formatter.getField("logger", m);
text = formatter.getField("msg", m);
if (logger != null && logger.indexOf('.') > -1)
logger = logger.substring(logger.lastIndexOf('.')+1);
}
} catch (ParseException e) {
throw new IOException(e);
}
}
void addText(String t) {
if (additionalText == null)
additionalText = new LinkedList<>();
additionalText.add(t);
}
public String getText() {
if (additionalText == null)
return text;
int i = text.length();
for (String s : additionalText)
i += s.length() + 1;
StringBuilder sb = new StringBuilder(i);
sb.append(text);
for (String s : additionalText)
sb.append('\n').append(s);
return sb.toString();
}
public String getThread() {
return thread;
}
public Writer appendHtml(Writer w) throws IOException {
w.append(toHtml(line)).append("<br>");
if (additionalText != null)
for (String t : additionalText)
w.append(toHtml(t)).append("<br>");
return w;
}
protected Writer append(Writer w) throws IOException {
w.append(line).append('\n');
if (additionalText != null)
for (String t : additionalText)
w.append(t).append('\n');
return w;
}
boolean matches() {
if (! isRecord)
return false;
if (start != null && date.before(start))
return false;
if (end != null && date.after(end))
return false;
if (threadFilter != null && ! threadFilter.equals(thread))
return false;
if (loggerFilter != null && ! loggerFilter.contains(logger))
return false;
if (severityFilter != null && ! severityFilter.contains(severity))
return false;
return true;
}
}
static final String toHtml(String s) {
if (s.indexOf('<') != -1)
return s.replaceAll("<", "&lt;");//$NON-NLS-2$
return s;
}
}