// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.log;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.yahoo.log.event.Event;
import com.yahoo.log.event.MalformedEventException;
/**
* This class implements the common ground log message used by
* the logserver. A LogMessage is immutable. Note that we have
* chosen the name LogMessage to avoid confusion with LogRecord
* which is used in java.util.logging.
*
* @author Bjorn Borud
*/
public class LogMessage
{
private static Logger log = Logger.getLogger(LogMessage.class.getName());
private static Pattern nativeFormat =
Pattern.compile("^(\\d[^\t]+)\t" + // time
"([^\t]+)\t" + // host
"([^\t]+)\t" + // threadProcess
"([^\t]+)\t" + // service
"([^\t]+)\t" + // component
"([^\t]+)\t" + // level
"(.+)$" // payload
);
private long time;
private String timeStr;
private String host;
private String threadProcess;
private String service;
private String component;
private Level level;
private String payload;
private Event event;
/**
* Private constructor. Log messages should never be instantiated
* directly; only as the result of a static factory method.
*/
private LogMessage (String timeStr, Long time, String host, String threadProcess,
String service, String component, Level level,
String payload)
{
this.timeStr = timeStr;
this.time = time;
this.host = host;
this.threadProcess = threadProcess;
this.service = service;
this.component = component;
this.level = level;
this.payload = payload;
}
public long getTime () {return time;}
public long getTimeInSeconds () {return time / 1000;}
public String getHost () {return host;}
public String getThreadProcess () {return threadProcess;}
public String getService () {return service;}
public String getComponent () {return component;}
public Level getLevel () {return level;}
public String getPayload () {return payload;}
/**
* Make a log message from the native format of the logging
* package.
*
* @param msg The log message
* @return Returns a LogMessage instance
* @throws InvalidLogFormatException if the log message
* can not be parsed, ie. is invalid, we throw this
* exception.
*/
public static LogMessage parseNativeFormat(String msg) throws InvalidLogFormatException {
Matcher m = nativeFormat.matcher(msg);
if (! m.matches()) {
throw new InvalidLogFormatException(msg);
}
Level msgLevel = LogLevel.parse(m.group(6));
Long timestamp = parseTimestamp(m.group(1));
return new LogMessage(m.group(1), timestamp, m.group(2), m.group(3),
m.group(4), m.group(5), msgLevel,
m.group(7));
}
private static long parseTimestamp(String timeStr) throws InvalidLogFormatException {
try {
return (long) (Double.parseDouble(timeStr) * 1000);
} catch (NumberFormatException e) {
throw new InvalidLogFormatException("Invalid time string:" + timeStr);
}
}
/**
* If the LogMessage was an EVENT then this method can
* be used to get the Event instance representing the
* event. The event instance created the first time
* this method is called and then cached.
*
* TODO: make sure this throws exception!
*
* @return Returns Event instance if this is an event message
* and the payload is correctly formatted. Otherwise
* it will return null
.
*
*/
public Event getEvent () throws MalformedEventException {
if ((level == LogLevel.EVENT) && (event == null)) {
try {
event = Event.parse(getPayload());
event.setTime(time);
}
catch (MalformedEventException e) {
log.log(LogLevel.DEBUG, "Got malformed event: " + getPayload());
throw e;
}
}
return event;
}
/**
* Return valid representation of log message.
*/
public String toString () {
return new StringBuilder(timeStr.length()
+ host.length()
+ threadProcess.length()
+ service.length()
+ component.length()
+ level.toString().length()
+ payload.length()
+ 1)
.append(timeStr).append("\t")
.append(host).append("\t")
.append(threadProcess).append("\t")
.append(service).append("\t")
.append(component).append("\t")
.append(level.toString().toLowerCase()).append("\t")
.append(payload).append("\n")
.toString();
}
}