package io.nextop.log;
import io.nextop.WireValue;
import rx.functions.Func0;
import javax.annotation.Nullable;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
// common logging interface for all of Nextop
// in this interface, keys are dot-separated strings that form a hierarchy (like Graphite)
// implementations should be lightweight the system can log metrics everywhere
/** thread-safe */
public interface Log {
void count(String keyFormat, Object ... keyArgs);
void count(String keyFormat, long d, Object ... keyArgs);
void count(Level level, String keyFormat, long d, Object ... keyArgs);
void metric(String keyFormat, long value, Object unit, Object ... keyArgs);
/** @param unit a {@link TimeUnit} or general {@link Unit} */
void metric(Level level, String keyFormat, long value, Object unit, Object ... keyArgs);
// duration* methods write as metrics. the metric is the time to run the callback
<R> R duration(String key, Func0<R> eval);
<R> R duration(Level level, String key, Func0<R> eval);
/** does not log on exception */
<R> R durationWithException(String key, Callable<R> eval) throws Exception;
/** does not log on exception */
<R> R durationWithException(Level level, String key, Callable<R> eval) throws Exception;
void message(String key);
void message(String key, @Nullable String messageFormat, Object ... args);
void message(Level level, String key, @Nullable String messageFormat, Object ... args);
void handled(String key, java.lang.Throwable t);
void handled(Level level, String key, Throwable t);
void handled(String key, Throwable t, @Nullable String messageFormat, Object ... args);
void handled(Level level, String key, Throwable t, @Nullable String messageFormat, Object ... args);
void unhandled(String key, Throwable t);
void unhandled(Level level, String key, Throwable t);
void unhandled(String key, Throwable t, @Nullable String messageFormat, Object ... args);
void unhandled(Level level, String key, Throwable t, @Nullable String messageFormat, Object ... args);
class Unit {
public static Unit create(String name) {
try {
return timeUnit(name);
} catch (IllegalArgumentException e) {
return new Unit(name);
}
}
public static Unit valueOf(Object unit) {
if (unit instanceof Unit) {
return (Unit) unit;
}
if (unit instanceof TimeUnit) {
return timeUnit((TimeUnit) unit);
}
return create(String.valueOf(unit));
}
private final String name;
private Unit(String name) {
this.name = name;
}
// convert to this unit from "source"
public long convert(long sourceValue, Unit sourceUnit) {
throw new UnsupportedOperationException();
}
@Override
public int hashCode() {
return name.hashCode();
}
@Override
public String toString() {
return name;
}
private static Unit timeUnit(TimeUnit timeUnit) {
class T extends Unit {
final TimeUnit timeUnit;
T(TimeUnit timeUnit) {
super(timeUnitToName(timeUnit));
this.timeUnit = timeUnit;
}
@Override
public long convert(long sourceValue, Unit sourceUnit) {
if (!(sourceUnit instanceof T)) {
throw new IllegalArgumentException();
}
return timeUnit.convert(sourceValue, ((T) sourceUnit).timeUnit);
}
}
return new T(timeUnit);
}
private static Unit timeUnit(String name) {
return timeUnit(timeUnitFromName(name));
}
private static String timeUnitToName(TimeUnit timeUnit) {
switch (timeUnit) {
case NANOSECONDS:
return NAME_NANOSECONDS;
case MICROSECONDS:
return NAME_MICROSECONDS;
case MILLISECONDS:
return NAME_MILLISECONDS;
case SECONDS:
return NAME_SECONDS;
case MINUTES:
return NAME_MINUTES;
case HOURS:
return NAME_HOURS;
case DAYS:
return NAME_DAYS;
default:
throw new IllegalArgumentException();
}
}
private static TimeUnit timeUnitFromName(String name) {
String n = name.toLowerCase();
if (NAME_NANOSECONDS.equals(n)) {
return TimeUnit.NANOSECONDS;
} else if (NAME_MICROSECONDS.equals(n)) {
return TimeUnit.MICROSECONDS;
} else if (NAME_MILLISECONDS.equals(n)) {
return TimeUnit.MILLISECONDS;
} else if (NAME_SECONDS.equals(n)) {
return TimeUnit.SECONDS;
} else if (NAME_MINUTES.equals(n)) {
return TimeUnit.MINUTES;
} else if (NAME_HOURS.equals(n)) {
return TimeUnit.HOURS;
} else if (NAME_DAYS.equals(n)) {
return TimeUnit.DAYS;
} else {
throw new IllegalArgumentException();
}
}
private static final String NAME_NANOSECONDS = "ns";
private static final String NAME_MICROSECONDS = "us";
private static final String NAME_MILLISECONDS = "ms";
private static final String NAME_SECONDS = "s";
private static final String NAME_MINUTES = "m";
private static final String NAME_HOURS = "h";
private static final String NAME_DAYS = "d";
}
/** thread-safe */
interface Out {
boolean isWrite(Level level, LogEntry.Type type);
/** @return the line width of the log/console, so output can be properly formatted */
int lineWidth();
/** @return the width of keys in the log/console. Must be ≤ {@link #lineWidth} */
int keyWidth();
int valueWidth();
int unitWidth();
void write(Level level, LogEntry.Type type, String ... lines);
boolean isWriteUp(Level level, LogEntry.Type type);
// this can be used to write aggregate statistics or critical logs (e.g. crashes) to an upstream
void writeUp(LogEntry entry);
}
}