Skip to content

Instantly share code, notes, and snippets.

@aerobless
Created March 14, 2018 13:52
Show Gist options
  • Save aerobless/e81c9e0de7f2bb1ed4d99897af16fa29 to your computer and use it in GitHub Desktop.
Save aerobless/e81c9e0de7f2bb1ed4d99897af16fa29 to your computer and use it in GitHub Desktop.
Fluent Key Value Log Fromatter
public class LogEntry {
private LogKey key;
private String value;
public LogEntry(LogKey key, String value) {
this.key = key;
this.value = value;
}
public LogKey getKey() {
return key;
}
public String getValue() {
return value;
}
}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import LogKey.customKey;
/**
* LogFormatter is used to fluidly write a log statement containing key=value entries.
* E.g. "This is a sample, someValue=123, otherThing=HelloWorld"
* <p>
* A LogFormatter chain should always be ended either by .build() to get the string value or by
* .logInfo(), .logError() etc. so that the log statement is written with the specified log level.
*/
public class LogFormatter {
private String description = "";
private List<LogEntry> logEntries = new ArrayList<>();
/**
* Please use newLog() instead.
*
* @return new LogFormatter
*/
@Deprecated
public static LogFormatter newLogStatement() {
return newLog();
}
/**
* Creates a new LogFormatter.
*
* @return new LogFormatter
*/
public static LogFormatter newLog() {
return new LogFormatter();
}
/**
* Creates a new LogFormatter.
*
* @param description a human readable description
* @return new LogFormatter
*/
public static LogFormatter newLog(String description) {
final LogFormatter logFormatter = new LogFormatter();
return logFormatter.description(description);
}
/**
* Creates a new LogFormatter.
*
* @param description a human readable description. Can contain {} which will be replaced by args
* @param args arguments are used to replace {} in the description. Args are nullsafe, if null is provided "null" is logged.
* @return new LogFormatter
*/
public static LogFormatter newLog(String description, Object... args) {
final LogFormatter logFormatter = new LogFormatter();
return logFormatter.description(description, args);
}
/**
* Adds a human readable description. The description may contain {} which will be replaced by the provided args.
* Be sure to also add any values with .add(key, value).
*
* @param description a human readable description. Can contain {} which will be replaced by args
* @return the provided LogFormatter which can be used for further chaining
*/
public LogFormatter description(String description) {
this.description = description + ", ";
return this;
}
/**
* Adds a human readable description. The description may contain {} which will be replaced by the provided args.
* Be sure to also add any values with .add(key, value).
*
* @param description a human readable description. Can contain {} which will be replaced by args
* @param args arguments are used to replace {} in the description. Args are nullsafe, if null is provided "null" is logged.
* @return the provided LogFormatter which can be used for further chaining
*/
public LogFormatter description(String description, Object... args) {
String replacedDescription = description;
Object[] arguments = args;
if (arguments == null) {
arguments = new Object[]{null};
}
for (Object argument : arguments) {
replacedDescription = replacedDescription.replaceFirst("\\{}", nullSafeToString(argument));
}
return description(replacedDescription);
}
/**
* Adds a key value pair. The log will contain "key=value, ".
*
* @param key a LogKey or String
* @param value an Object. Value is nullsafe, if null is provided "null" is logged.
* @return the provided LogFormatter which can be used for further chaining
*/
public LogFormatter add(LogKey key, Object value) {
logEntries.add(new LogEntry(key, nullSafeToString(value)));
return this;
}
/**
* Adds a key value pair. The log will contain "key=value, ".
*
* @param key a LogKey or String
* @param value an Object. Value is nullsafe, if null is provided "null" is logged.
* @return the provided LogFormatter which can be used for further chaining
*/
public LogFormatter add(String key, Object value) {
return add(customKey(key), value);
}
/**
* Ends the LogFormatter chain and returns the log statement as a string.
*
* @return the log statement as a string
*/
public String build() {
return description + logEntries.stream()
.map(entry -> entry.getKey().toString() + "=" + entry.getValue())
.collect(Collectors.joining(", "));
}
/**
* Ends the LogFormatter chain and logs the statement with level info.
*/
public void logInfo() {
getLoggerForCallerClass().info(build());
}
/**
* Ends the LogFormatter chain and logs the statement with level warn.
*/
public void logWarn() {
getLoggerForCallerClass().warn(build());
}
/**
* Ends the LogFormatter chain and logs the statement with level warn.
*
* @param throwable a throwable
*/
public void logWarn(Throwable throwable) {
getLoggerForCallerClass().warn(build(), throwable);
}
/**
* Ends the LogFormatter chain and logs the statement with level error.
*/
public void logError() {
getLoggerForCallerClass().error(build());
}
/**
* Ends the LogFormatter chain and logs the statement with level error.
*
* @param throwable a throwable
*/
public void logError(Throwable throwable) {
getLoggerForCallerClass().error(build(), throwable);
}
/**
* Ends the LogFormatter chain and logs the statement with level trace.
*/
public void logTrace() {
getLoggerForCallerClass().trace(build());
}
/**
* Ends the LogFormatter chain and logs the statement with level debug.
*/
public void logDebug() {
getLoggerForCallerClass().debug(build());
}
//https://stackoverflow.com/questions/421280/how-do-i-find-the-caller-of-a-method-using-stacktrace-or-reflection
private Logger getLoggerForCallerClass() {
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
return LoggerFactory.getLogger(stackTraceElements[3].getClassName());
}
private String nullSafeToString(Object value) {
return Optional.ofNullable(value).orElse("null").toString();
}
}
public class LogKey {
//DATE
public static final LogKey VALID_FROM = new LogKey("validFrom");
public static final LogKey VALID_TO = new LogKey("validTo");
//EVENTS
public static final LogKey EVENT_AGE = new LogKey("eventAge");
public static final LogKey EVENT_TYPE = new LogKey("eventType");
public static final LogKey EVENT_LOCAL_ID = new LogKey("eventLocalId");
public static final LogKey CALL_TIMEOUT = new LogKey("callTimeout");
//JOBS
public static final LogKey JOB_NAME = new LogKey("jobName");
public static final LogKey JOB_STATE = new LogKey("jobState");
//Basic Logging
public static final LogKey DESCRIPTION = new LogKey("description");
//REST / Networking
public static final LogKey CORRELATION_ID = new LogKey("cid");
public static final LogKey ORIGIN_APPLICATION_ID = new LogKey("originApplicationId");
public static final LogKey REQUEST_COUNT = new LogKey("requestCount");
//... others
private String keyName;
private LogKey(String keyName) {
this.keyName = keyName.replace(" ", "");
}
public static LogKey customKey(String keyName) {
return new LogKey(keyName);
}
@Override
public String toString() {
return keyName;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment