
com.truward.brikar.common.log.LogUtil Maven / Gradle / Ivy
The newest version!
package com.truward.brikar.common.log;
import com.truward.brikar.common.log.metric.Metrics;
import com.truward.brikar.common.log.metric.MetricsCollection;
import com.truward.brikar.common.log.metric.StandardMetricsCollection;
import org.slf4j.Logger;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
/**
* Common constants for logging.
* These constants should be used to provide more information in a form of structured records in the log files.
*
* Certain arguments should come in a form of {@link org.slf4j.MDC} variables, the others may come in a form of
* metric entries.
*
* The metric entry is a substring in log message with specific format: it should start with a constant
* {@link #METRIC_ENTRY} and then contain comma+space separated entries where key and value are separated by
* equals sign.
*
* Each log record may contain none or exactly one metric entry which should be the last one in log message.
* It is completely up to the developer to define semantics behind metric attributes, but general convention is as
* follows:
* Metrics may contain:
*
* - Operation name
* - Optional time delta (how much time this operation took, in milliseconds)
* - Optional flag, that indicates whether or not operation is failed. Absence of this flag indicates that
* operation succeeded
* - Optional integer value that represents a count of something
*
*
* Example of metric entries:
*
* @metric1 op=MeasureUsedToFreeMemRatio, cnt=32
- it may mean, that an operation to measure a ratio
* of used to free memory has been attempted and its result is 32%
* @metric1 op=UserService.getUserById, tDelta=125
- it may mean, that operation to get user by
* ID has been executed and it took 125 milliseconds to complete
*
*
* @author Alexander Shabanov
*/
@ParametersAreNonnullByDefault
public final class LogUtil {
private LogUtil() {}
/**
* A name, under which a request vector is known as one of the logger attributes.
*
* Request vector is an identifier that uniquely identifies particular service request, where did it come from,
* as well as its place in the bigger inter-service call graph.
*
*
* See also {@link org.slf4j.MDC}.
* See also {@link com.truward.brikar.common.tracking.TrackingHttpHeaderNames#REQUEST_VECTOR}.
*/
public static final String REQUEST_VECTOR = "RV";
private static final ThreadLocal METRICS_COLLECTION = new ThreadLocal<>();
/**
* Maximum size of request vector.
*/
public static final int MAX_REQUEST_VECTOR_LENGTH = 4096;
@Nullable
public static MetricsCollection getLocalMetricsCollection() {
return METRICS_COLLECTION.get();
}
public static MetricsCollection getOrCreateLocalMetricsCollection() {
MetricsCollection result = getLocalMetricsCollection();
if (result == null) {
result = new StandardMetricsCollection();
setLocalMetricsCollection(result);
}
return result;
}
public static void setLocalMetricsCollection(@Nullable MetricsCollection value) {
METRICS_COLLECTION.set(value);
}
public static void logInfo(Metrics metrics, Logger log) {
final MetricsCollection metricsCollection = new StandardMetricsCollection();
metricsCollection.add(metrics);
log.info(metricsCollection.toString());
}
public static void logInfo(MetricsCollection metricsCollection, Logger log) {
log.info(metricsCollection.toString());
}
public static void logAndResetLocalMetricsCollection(Logger log, @Nullable MetricsCollection newMetricsCollection) {
final MetricsCollection metricsCollection = getLocalMetricsCollection();
if (metricsCollection != null) {
logInfo(metricsCollection, log);
}
setLocalMetricsCollection(newMetricsCollection);
}
public static void logAndResetLocalMetricsCollection(Logger log) {
logAndResetLocalMetricsCollection(log, null);
}
/**
* Propagates metrics entry to the local metrics collection or (if unavailable) logs immediately
*
* @param metrics Metrics object to log
* @param log Target logger to use if local metrics collection is not available
*/
public static void propagateOrLogInfo(Metrics metrics, Logger log) {
final MetricsCollection metricsCollection = propagate(metrics);
if (metricsCollection == null) {
logInfo(metrics, log);
}
}
/**
* Propagates metrics entry to the thread local metrics collection, does nothing if local metrics collection
* is missing.
*
* @param metrics Metrics to capture
* @return Local metrics collection or null
*/
@Nullable
public static MetricsCollection propagate(Metrics metrics) {
final MetricsCollection metricsCollection = getLocalMetricsCollection();
if (metricsCollection != null) {
metricsCollection.add(metrics);
}
return metricsCollection;
}
/**
* Validates, that passed request vector is valid. This function is used to prevent the potential attacker to send
* garbage request vectors into the service.
* If passed request vector is invalid, it should be discarded.
*
* @param requestVector Request vector to validate
* @return True, if passed request vector is valid, false otherwise.
*/
public static boolean isValidRequestVector(@Nullable String requestVector) {
if (requestVector == null || requestVector.isEmpty() || requestVector.length() > MAX_REQUEST_VECTOR_LENGTH) {
return false;
}
// verify, that each character is within the allowed bounds
for (int i = 0; i < requestVector.length(); ++i) {
final char ch = requestVector.charAt(i);
if (ch <= 32 || ch >= 127) {
return false;
}
}
return true;
}
//
// Parameters in logging statements.
//
/**
* Preceding keyword, which presence indicates that a special entry will be coded afterwards.
* There should be one and only one metric entry per logging statement.
*
* One identifies record version for possible future extension with the new fields.
*/
public static final String METRIC_ENTRY = "@metric1";
/**
* A name, under which an operation should be known.
* Usually recorded as one of the metric attributes.
*/
public static final String OPERATION = "op";
/**
* A name, under which an integer count of something, related to the operation should be known.
* For example, if operation is to measure used/free memory ratio, this field should contain percentage.
*/
public static final String COUNT = "cnt";
/**
* A name of the attribute, corresponding to operation start time (unix time, in milliseconds).
* Usually recorded as one of the metric attributes.
*/
public static final String START_TIME = "tStart";
/**
* A name of the attribute, corresponding to time spent (in milliseconds).
* Usually recorded as one of the metric attributes.
*/
public static final String TIME_DELTA = "tDelta";
/**
* A name of an optional attribute which indicates whether or not operation failed.
* Usually recorded as one of the metric attributes.
*/
public static final String FAILED = "failed";
/**
* HTTP method (invocation type)
*/
public static final String VERB = "verb";
/**
* Http Response Code (integer), e.g. 200 - 'OK', 404 - 'Not Found', etc.
*/
public static final String RESPONSE_CODE = "responseCode";
/**
* Http URL, e.g. /something/like/that
*/
public static final String URL = "url";
/**
* Request vector, that contains in the service call response.
*/
public static final String RESPONSE_REQUEST_VECTOR = "responseRV";
/**
* Value for entry that should be used if corresponding value is omitted.
*/
public static final String UNKNOWN_VALUE = "?";
//
// Helper methods
//
/**
* Encodes a value, so that it won't contain spaces, commas and equal signs.
*
* @param value Value to be encoded
* @return Encoded value or same value if passed argument does not contain whitespace, comma or equals sign
*/
public static String encodeString(String value) {
int estimatedSize = 0;
final int len = value.length();
// estimate output string size to find out whether encoding is required and avoid reallocations in string builder
for (int i = 0; i < len; ++i) {
final char ch = value.charAt(i);
if (ch <= ' ' || ch == ',') {
estimatedSize += 3;
continue;
}
++estimatedSize;
}
if (value.length() == estimatedSize) {
return value; // return value as is - it does not contain any special characters
}
final StringBuilder builder = new StringBuilder(estimatedSize);
for (int i = 0; i < len; ++i) {
final char ch = value.charAt(i);
if (ch <= ' ') {
builder.append("%20");
continue;
}
if (ch == ',') {
builder.append("%2c");
continue;
}
builder.append(ch);
}
return builder.toString();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy