/*
* Password Management Servlets (PWM)
* http://www.pwm-project.org
*
* Copyright (c) 2006-2009 Novell, Inc.
* Copyright (c) 2009-2017 The PWM Project
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package password.pwm.util.logging;
import com.google.gson.annotations.SerializedName;
import password.pwm.bean.SessionLabel;
import password.pwm.util.java.JsonUtil;
import password.pwm.util.java.StringUtil;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Serializable;
import java.io.StringWriter;
import java.time.Instant;
public class PwmLogEvent implements Serializable, Comparable {
// -------------------------- ENUMERATIONS --------------------------
private static final String VERSION = "1";
private static final String KEY_VERSION = "v";
private static final String KEY_LEVEL = "l";
private static final String KEY_TOPIC = "t";
private static final String KEY_MESSAGE = "m";
private static final String KEY_SOURCE = "s";
private static final String KEY_ACTOR = "a";
private static final String KEY_LABEL = "b";
private static final String KEY_THROWABLE = "e";
private static final String KEY_DATE = "d";
// ------------------------------ FIELDS ------------------------------
private final PwmLogLevel level;
@SerializedName("t")
private final String topic;
@SerializedName("m")
private final String message;
@SerializedName("s")
private final String source; //aka network address/host
@SerializedName("a")
private final String actor; //aka principal
@SerializedName("b")
private final String label; //aka session id
@SerializedName("e")
private final Throwable throwable;
@SerializedName("d")
private final Instant date;
// -------------------------- STATIC METHODS --------------------------
public static PwmLogEvent fromEncodedString(final String encodedString)
throws ClassNotFoundException, IOException
{
return JsonUtil.deserialize(encodedString, PwmLogEvent.class);
}
// --------------------------- CONSTRUCTORS ---------------------------
private PwmLogEvent(
final Instant date,
final String topic,
final String message,
final String source,
final String actor,
final String label,
final Throwable throwable,
final PwmLogLevel level
)
{
if (date == null) {
throw new IllegalArgumentException("date may not be null");
}
if (level == null) {
throw new IllegalArgumentException("level may not be null");
}
this.date = date;
this.topic = topic;
this.message = message;
this.source = source;
this.actor = actor;
this.label = label;
this.throwable = throwable;
this.level = level;
}
private static String makeSrcString(final SessionLabel sessionLabel)
{
try {
final StringBuilder from = new StringBuilder();
{
final String srcAddress = sessionLabel.getSrcAddress();
final String srcHostname = sessionLabel.getSrcHostname();
if (srcAddress != null) {
from.append(srcAddress);
if (!srcAddress.equals(srcHostname)) {
from.append("/");
from.append(srcHostname);
}
}
}
return from.toString();
} catch (NullPointerException e) {
return "";
}
}
private static String makeActorString(final SessionLabel sessionLabel)
{
final StringBuilder sb = new StringBuilder();
if (sessionLabel != null) {
if (sessionLabel.getUsername() != null) {
sb.append(sessionLabel.getUsername());
} else if (sessionLabel.getUserIdentity() != null) {
sb.append(sessionLabel.getUserIdentity().toDelimitedKey());
}
}
return sb.toString();
}
public static PwmLogEvent createPwmLogEvent(
final Instant date,
final String topic,
final String message,
final String source,
final String actor,
final String label,
final Throwable throwable,
final PwmLogLevel level
)
{
return new PwmLogEvent(date, topic, message, source, actor, label, throwable, level);
}
public static PwmLogEvent createPwmLogEvent(
final Instant date,
final String topic,
final String message,
final SessionLabel sessionLabel,
final Throwable throwable,
final PwmLogLevel level
)
{
final String source = makeSrcString(sessionLabel);
final String actor = makeActorString(sessionLabel);
final String label = sessionLabel != null ? sessionLabel.getSessionID() : null;
return new PwmLogEvent(date, topic, message, source, actor, label, throwable, level);
}
// --------------------- GETTER / SETTER METHODS ---------------------
public String getActor()
{
return actor;
}
public Instant getDate()
{
return date;
}
public PwmLogLevel getLevel()
{
return level;
}
public String getMessage()
{
return message;
}
public String getSource()
{
return source;
}
public Throwable getThrowable()
{
return throwable;
}
public String getTopic()
{
return topic;
}
public String getLabel()
{
return label;
}
public String getTopTopic()
{
if (topic == null) {
return null;
}
final int lastDot = topic.lastIndexOf(".");
return lastDot != -1 ? topic.substring(lastDot + 1, topic.length()) : topic;
}
public String getEnhancedMessage()
{
final StringBuilder output = new StringBuilder();
output.append(getDebugLabel());
output.append(message);
final String srcAddrString = this.getSource();
if (srcAddrString != null && !srcAddrString.isEmpty()) {
final String srcStr = " [" + srcAddrString + "]";
final int firstCR = output.indexOf("\n");
if (firstCR == -1) {
output.append(srcStr);
} else {
output.insert(firstCR, srcStr);
}
}
if (this.getThrowable() != null) {
output.append(" (stacktrace follows)\n");
final StringWriter sw = new StringWriter();
final PrintWriter pw = new PrintWriter(sw);
this.getThrowable().printStackTrace(pw);
pw.flush();
output.append(sw.toString());
}
return output.toString();
}
public int compareTo(final Object o)
{
if (!(o instanceof PwmLogEvent)) {
throw new IllegalArgumentException("cannot compare with non PwmLogEvent");
}
return this.getDate().compareTo(((PwmLogEvent) o).getDate());
}
public String toEncodedString()
throws IOException
{
return JsonUtil.serialize(this);
/*
final Map<String, String> tempMap = new HashMap<>();
tempMap.put(KEY_VERSION, VERSION);
tempMap.put(KEY_TOPIC, topic);
tempMap.put(KEY_MESSAGE, message);
tempMap.put(KEY_SOURCE, source);
tempMap.put(KEY_ACTOR, actor);
tempMap.put(KEY_LEVEL, level.toString());
tempMap.put(KEY_DATE, String.valueOf(date.getTime()));
if (label != null) {
tempMap.put(KEY_LABEL, label);
}
if (throwable != null) {
tempMap.put(KEY_THROWABLE, Base64.encodeObject(throwable, Base64.NO_OPTIONS));
}
return JsonUtil.serializeMap(tempMap);
*/
}
private String getDebugLabel()
{
final StringBuilder sb = new StringBuilder();
if ((getActor() != null && !getActor().isEmpty()) || ((getLabel() != null && !getLabel().isEmpty()))) {
sb.append("{");
if (getLabel() != null && !getLabel().isEmpty()) {
sb.append(this.getLabel());
}
if (getActor() != null && !getActor().isEmpty()) {
if (sb.length() > 0) {
sb.append(",");
}
sb.append(this.getActor());
}
sb.append("} ");
}
return sb.toString();
}
public String toLogString()
{
return toLogString(true);
}
public String toLogString(final boolean includeTimeStamp)
{
final StringBuilder sb = new StringBuilder();
if (includeTimeStamp) {
sb.append(this.getDate().toString());
sb.append(", ");
}
sb.append(StringUtil.padEndToLength(getLevel().toString(),5,' '));
sb.append(", ");
sb.append(shortenTopic(this.topic));
sb.append(", ");
sb.append(this.getEnhancedMessage());
return sb.toString();
}
@Override
public String toString()
{
return "PwmLogEvent=" + JsonUtil.serialize(this);
}
private static String shortenTopic(final String input)
{
if (input == null || input.isEmpty()) {
return input;
}
final int keepParts = 2;
final String[] parts = input.split("\\.");
final StringBuilder output = new StringBuilder();
int partsAdded = 0;
for (int i = parts.length; i > 0 && partsAdded < keepParts; i--) {
output.insert(0, parts[i - 1]);
partsAdded++;
if (i > 0 && partsAdded < keepParts) {
output.insert(0, ".");
}
}
return output.toString();
}
}