Last active
July 24, 2020 06:00
-
-
Save ibaca/bcd1fccf931d6eca8ba827c5bd37a1db to your computer and use it in GitHub Desktop.
JAX-RS GWT Logging service
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.intendia.igestion.server.util; | |
import static com.intendia.igestion.shared.util.ToolsRestService.logRecordFromDto; | |
import com.google.gwt.core.server.StackTraceDeobfuscator; | |
import com.intendia.igestion.shared.util.ToolsRestService; | |
import io.reactivex.Completable; | |
import java.io.File; | |
import java.io.FileInputStream; | |
import java.io.FileNotFoundException; | |
import java.io.IOException; | |
import java.io.InputStream; | |
import java.util.logging.LogRecord; | |
import java.util.logging.Logger; | |
import javax.inject.Inject; | |
import javax.inject.Provider; | |
import javax.inject.Singleton; | |
import javax.servlet.ServletContext; | |
@Singleton | |
public class DefaultLoggingRestService implements LoggingRestService { | |
private final StackTraceDeobfuscator deobfuscator = new ClasspathStackTraceDeobfuscator(); | |
@Inject Provider<ServletContext> contextProvider; | |
@Override | |
public Completable sendLog(String permutation, LogRecordDto log) { | |
logMessage(permutation, logRecordFromDto(log)); | |
return Completable.complete(); | |
} | |
public void logMessage(String permutation, LogRecord record) { | |
if (record.getThrown() != null) deobfuscator.deobfuscateStackTrace(record.getThrown(), permutation); | |
Logger.getLogger(record.getLoggerName()).log(record); | |
} | |
private final class ClasspathStackTraceDeobfuscator extends StackTraceDeobfuscator { | |
public static final String SYMBOL_MAPS_DIRECTORY = "/WEB-INF/deploy/web/symbolMaps/"; | |
@Override | |
protected InputStream getSourceMapInputStream(String permutation, int fragmentNumber) throws IOException { | |
return getInputStream(SYMBOL_MAPS_DIRECTORY + permutation + "_sourceMap" + fragmentNumber + ".json"); | |
} | |
@Override | |
protected InputStream getSymbolMapInputStream(String permutation) throws IOException { | |
return getInputStream(SYMBOL_MAPS_DIRECTORY + permutation + ".symbolMap"); | |
} | |
@Override | |
protected InputStream openInputStream(String fileName) throws IOException { | |
return new FileInputStream(new File(SYMBOL_MAPS_DIRECTORY, fileName)); | |
} | |
private InputStream getInputStream(String path) throws FileNotFoundException { | |
final InputStream stream = contextProvider.get().getResourceAsStream(path); | |
// don't return null which trows a NPE and escapes to the parent. FNFE it's correctly caught | |
if (stream == null) throw new FileNotFoundException(path); | |
return stream; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.intendia.igestion.shared.util; | |
import static com.google.common.base.MoreObjects.firstNonNull; | |
import static com.intendia.igestion.shared.domain.ApplicationConstants.APPLICATION_JSON; | |
import static com.intendia.igestion.shared.domain.ApplicationConstants.RESOURCE_TOOLS; | |
import static jsinterop.annotations.JsPackage.GLOBAL; | |
import com.intendia.gwt.autorest.client.AutoRestGwt; | |
import io.reactivex.Completable; | |
import java.util.logging.Level; | |
import java.util.logging.LogRecord; | |
import java.util.stream.Stream; | |
import javax.annotation.Nullable; | |
import javax.ws.rs.Consumes; | |
import javax.ws.rs.POST; | |
import javax.ws.rs.Path; | |
import javax.ws.rs.PathParam; | |
import javax.ws.rs.Produces; | |
import jsinterop.annotations.JsType; | |
/** Common application utilities like version details, build info or system info. */ | |
@AutoRestGwt | |
@Path(RESOURCE_TOOLS) @Produces(APPLICATION_JSON) @Consumes(APPLICATION_JSON) | |
public interface LoggingRestService { | |
@POST @Path("log/{permutation}") | |
Completable sendLog(@PathParam("permutation") String permutation, LogRecordDto log); | |
@JsType(namespace = GLOBAL, name = "Object", isNative = true)// | |
class LogRecordDto { | |
public String level; | |
public String loggerName; | |
public String msg; | |
public double timestamp; | |
public @Nullable ThrowableDto thrown; | |
} | |
@JsType(namespace = GLOBAL, name = "Object", isNative = true)// | |
class ThrowableDto { | |
public String type; | |
public String message; | |
public @Nullable ThrowableDto cause; | |
public StackTraceElementDto[] stackTrace; | |
} | |
@JsType(namespace = GLOBAL, name = "Object", isNative = true)// | |
class StackTraceElementDto { | |
public String className; | |
public String fileName; | |
public String methodName; | |
public int lineNumber; | |
} | |
static LogRecordDto logRecordToDto(LogRecord record) { | |
LogRecordDto out = new LogRecordDto(); | |
out.level = record.getLevel().toString(); | |
out.loggerName = record.getLoggerName(); | |
out.msg = record.getMessage(); | |
out.timestamp = record.getMillis(); | |
out.thrown = throwableToDto(record.getThrown()); | |
return out; | |
} | |
static @Nullable ThrowableDto throwableToDto(@Nullable Throwable throwable) { | |
if (throwable == null) return null; | |
ThrowableDto out = new ThrowableDto(); | |
out.type = throwable.getClass().getName(); | |
out.message = throwable.getMessage(); | |
out.cause = throwableToDto(throwable.getCause()); | |
out.stackTrace = Stream.of(firstNonNull(throwable.getStackTrace(), new StackTraceElement[0])) | |
.map(eIn -> { | |
StackTraceElementDto eOut = new StackTraceElementDto(); | |
eOut.className = eIn.getClassName(); | |
eOut.fileName = eIn.getFileName(); | |
eOut.methodName = eIn.getMethodName(); | |
eOut.lineNumber = eIn.getLineNumber(); | |
return eOut; | |
}).toArray(StackTraceElementDto[]::new); | |
return out; | |
} | |
static LogRecord logRecordFromDto(LogRecordDto dto) { | |
LogRecord lr = new LogRecord(Level.parse(dto.level), dto.msg); | |
lr.setLoggerName(dto.loggerName); | |
lr.setThrown(JsonLogRecordThrowable.fromJsonString(dto.thrown)); | |
lr.setMillis((long) dto.timestamp); | |
return lr; | |
} | |
@SuppressWarnings("GwtInconsistentSerializableClass")// | |
class JsonLogRecordThrowable extends Throwable { | |
private final String type; | |
private static @Nullable Throwable fromJsonString(@Nullable ThrowableDto dto) { | |
return dto == null ? null : new JsonLogRecordThrowable(dto); | |
} | |
JsonLogRecordThrowable(ThrowableDto dto) { | |
super(dto.message, fromJsonString(dto.cause)); | |
this.type = dto.type; | |
this.setStackTrace(Stream.of(firstNonNull(dto.stackTrace, new StackTraceElementDto[0])) | |
.map(e -> new StackTraceElement(e.className, e.methodName, e.fileName, e.lineNumber)) | |
.toArray(StackTraceElement[]::new)); | |
} | |
public String toString() { return getMessage() != null ? type + ": " + getMessage() : type; } | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment