Simplify logging interface & add RPC method#121
Conversation
| AddPathStart("VK_DEVICE_LAYERS", "VkGraphicsSpy"). | ||
| AddPathStart("VK_LAYER_PATH", libVkLayerGraphicsSpy.Parent().System()). | ||
| Set("LD_PRELOAD", libgapii.System()), | ||
| }) |
There was a problem hiding this comment.
It's related. There's new logic to handle stdout and stderr, which adds more options to the start function. Given the number of parameters we're passing in, I opted for a struct.
There was a problem hiding this comment.
Yes, I noticed that later. Sorry, I didn't realize it sends out comments right away. Is there no draft comment mode like Critique/Gerrit?
There was a problem hiding this comment.
Yes. Next to the single line comment button there should be a "Start Review" green button.
|
Now also contains GAPIC changes to display the merged logs across GAPIC, GAPIS and GAPIR. |
|
I'm following up with a final CL that avoids the |
pmuetschard
left a comment
There was a problem hiding this comment.
FYI: i've checked in a .settings folder which enables a lot of warnings in Eclipse.
| dirty.set(false); | ||
| } | ||
|
|
||
| private String formatTime(Timestamp time) { |
| case Fatal: | ||
| return theme.fatalBackground(); | ||
| } | ||
| return theme.infoBackground(); |
There was a problem hiding this comment.
Please move this into a default case to make the behavior more explicit.
| case Fatal: | ||
| return theme.fatalForeground(); | ||
| } | ||
| return theme.infoForeground(); |
There was a problem hiding this comment.
Please move this into a default case to make the behavior more explicit.
| private Color severityBackgroundColor(Log.Severity severity) { | ||
| switch (severity) { | ||
| case Verbose: | ||
| return theme.verboseBackground(); |
There was a problem hiding this comment.
nit: since the case statements are so simple, I'd prefer them to be on one line:
case Verbose: return theme.verboseBackground();
case Debug: return theme.debugBackground();
...
| package com.google.gapid.util; | ||
|
|
||
| import com.google.gapid.proto.log.Log; | ||
| import com.google.gapid.rpclib.schema.Method; |
|
|
||
| static LogLevel fromLevel(Level level) { | ||
| for (LogLevel logLevel : LogLevel.values()) { | ||
| if (logLevel.level == level) { |
There was a problem hiding this comment.
This should be range based as it was before.
java.util.logging.Level is not an enum, so you also should not use '==', but .equals.
| } | ||
|
|
||
| /** | ||
| * Adds a {@link Log.Message} to the buffer, bypassing the Java {@link LogManager}. |
There was a problem hiding this comment.
Unfortunately this needs to be {@link com.google.gapid.proto.log.Log.Message}
There was a problem hiding this comment.
Is this Eclipse telling you this? IntelliJ seems fine with it as-is.
There was a problem hiding this comment.
Yes and no. Eclipse does give me the warning, but it comes straight from javadoc:
src/main/com/google/gapid/util/Logging.java:145: warning - Tag @link: reference not found: Log.Message
| private static final String GAPID_PREFIX = "com.google.gapid."; | ||
| private static final String GOOG_PREFIX = "com.google."; | ||
|
|
||
| private static String shorten(String className) { |
There was a problem hiding this comment.
make this protected, as you are using it from multiple inner classes
There was a problem hiding this comment.
I'm sure I've asked this before, but why is this an issue?
Done anyway.
There was a problem hiding this comment.
The JVM has no concept (almost) of inner classes. To the JVM, an inner class and its container outer class are simply two different classes. You cannot call private methods of another class. The compiler adds synthetic methods to make it look like you can call private methods from inner/outer classes. These synthetic methods add unnecessary overhead and pollute stack traces. If you're going to allow access via these synthetic methods, why not give access directly?
| private static class Buffer extends Handler { | ||
| public Runnable listener; | ||
| private final StringBuilder buffer = new StringBuilder(); | ||
| private final LinkedList<Log.Message> buffer = new LinkedList<>(); |
There was a problem hiding this comment.
buffer is shadowing Logging.buffer. I suggest renaming Logging.buffer to Logging.messageBuffer
There was a problem hiding this comment.
Changed in later CL.
| byte[] data = ByteStreams.toByteArray(in); | ||
| SearchingInputStream is = new SearchingInputStream(in); | ||
| if (!is.search(PACKAGE_DATA_MARKER.getBytes())) { | ||
| throw new RuntimeException("The gapit command didn't produce the data marker."); |
There was a problem hiding this comment.
Resource leak: 'is' is not closed at this location.
I'd suggest making the search method static and avoid the wrapping in another input stream. The SearchingInputStream class has no additional state, so it's not needed.
There was a problem hiding this comment.
The SearchingInputStream class has no additional state, so it's not needed.
Well, it has the buffered reader state. I've wrapped it with a try-with-resources,
Thanks, but I use IntelliJ. :) |
| import java.util.logging.Logger; | ||
| import java.util.logging.StreamHandler; | ||
| import java.util.LinkedList; | ||
| import java.util.logging.*; |
There was a problem hiding this comment.
Please no * imports. I've been following the Google style for Java, and I'd like to continue to do so.
There was a problem hiding this comment.
Done. Another IntelliJ default that I continually battle.
| public class Logging { | ||
| /** | ||
| * Possible log level flag values. | ||
| * Possible logMessage level flag values. |
There was a problem hiding this comment.
Refactor gone crazy. Fixed.
| "logLevel", LogLevel.INFO, "Logging level [OFF, ERROR, WARNING, INFO, DEBUG, ALL]."); | ||
| public static final Flag<String> logDir = Flags.value( | ||
| "logDir", System.getProperty("java.io.tmpdir"), "Directory for log files."); | ||
| "logDir", System.getProperty("java.io.tmpdir"), "Directory for logMessage files."); |
| if (!logDir.get().isEmpty()) { | ||
| try { | ||
| FileHandler fileHandler = new FileHandler(logDir.get() + File.separator + "gapic.log"); | ||
| FileHandler fileHandler = new FileHandler(logDir.get() + File.separator + "gapic.logMessage"); |
| } | ||
|
|
||
| /** | ||
| * Adds a {@link Log.Message} to the buffer, bypassing the Java {@link LogManager}. |
There was a problem hiding this comment.
Yes and no. Eclipse does give me the warning, but it comes straight from javadoc:
src/main/com/google/gapid/util/Logging.java:145: warning - Tag @link: reference not found: Log.Message
|
|
||
| public static void setListener(Runnable listener) { | ||
| buffer.listener = (listener == null) ? () -> { /* empty */ } : listener; | ||
| public static void setListener(Listener listener_) { |
There was a problem hiding this comment.
Name shadowing is acceptable on setters, if you're shadowing the variable you're setting.
If you don't like to do that, please use new instead of _.
| @@ -123,6 +135,7 @@ public static void setListener(Runnable listener) { | |||
| */ | |||
| public static void logMessage(Log.Message message) { | |||
There was a problem hiding this comment.
This is now very much round-about: Buffer.publish calls Logging.logMessage, which calls Buffer.add and then fires the event.
Better to keep it they way it was:
logMessage and Buffer.publish call Buffer.add
Buffer.add fires the event
There was a problem hiding this comment.
I've separated Buffer and Handler. The inner class was doing too much - hence the extraction of the event logic. Hopefully this is now cleaner and would be nice and simple to make the classes public if needed in the future.
| * new messages. | ||
| */ | ||
| private static class MessageIterator implements Iterator<Log.Message> { | ||
| private final Buffer buffer; |
There was a problem hiding this comment.
Given that the class is static, does it?
There was a problem hiding this comment.
Yes, because so is Logging.buffer.
| if (thrown != null) { | ||
| for (StackTraceElement el : thrown.getStackTrace()) { | ||
| builder.addCallstack(Log.SourceLocation.newBuilder() | ||
| .setFile(el.getFileName()) |
There was a problem hiding this comment.
el.getFileName can return null (protos don't like nulls).
| public void run() { | ||
| Service.GetLogStreamRequest request = Service.GetLogStreamRequest.newBuilder().build(); | ||
| Iterator<Log.Message> it = GapidGrpc.newBlockingStub(channel).getLogStream(request); | ||
| while (it.hasNext()) { |
There was a problem hiding this comment.
This now causes an exception on clean shutdown. Before closing the connection to gapis, the logger thread should be stopped.
I think you'd be better off using a StreamObserver and the async case rather than the blocking case with an extra thread.
There was a problem hiding this comment.
AFAICT, streamed RPCs aren't exposed in the FutureStub (if that's what you're referring to).
There was a problem hiding this comment.
Now unbinds the LogMonitor on close(). Added warning for unexpected StatusRuntimeExceptions that are thrown by the thread.
Replace all uses of log.Context with context.Context. Squash the functionality of jot, memo, severity, fault into log.
Change-Id: I39df2ad33a3f780216fcbcbdcecbfd905252796e
Change-Id: I8ca1d2b11143352dfb6b8802f17c128d5dc49ee8
Change-Id: I4532f987e77b84b976a4d10adce39b75f1ce6d8e
Turn them into log.Messages. Change-Id: I95153e2a860c6db46e9a78ebc047599685239b0c
Let's you control them on the command line. Change-Id: Ib8b12a830a5a22b9f8c1c2fe2cbe00b7a451f1b0
Change-Id: I486fb73fed83100734fcb21d2bf391d44a862d51
Fixes the package picker when also logging to stdout.
Change-Id: Ic64e73cdf4d9b913594c7d02242c9c5dd06e1980
Change-Id: Ice567770dd74c9b2093c732f80be001a6e9d23c7
Change-Id: Ib103d8f824a7e0d7226584ad73127ad291dc763d
| func (ctx logContext) Alert() Logger { | ||
| return ctx.At(severity.Alert) | ||
| func (s Severity) String() string { | ||
| switch s { |
| return ctx.At(severity.Critical) | ||
| // Short returns the severity string with a single character. | ||
| func (s Severity) Short() string { | ||
| switch s { |
Change-Id: I15a47ec97283efdce403b87af8491295ee9c15f9
|
+1 |
Change-Id: If1619af5063c4e98268a8f0dcf33678970b251dd
Change-Id: I428d36fd26c984a7ffcadc50eeb948378d7b935a
| * A connection to a running Graphics API Server (GAPIS). | ||
| */ | ||
| public abstract class GapisConnection implements Closeable { | ||
| private static final Logger LOG = Logger.getLogger(Client.class.getName()); |
There was a problem hiding this comment.
Client.class -> GapisConnection.class
| import com.google.gapid.proto.pkginfo.PkgInfo.PackageList; | ||
|
|
||
| import java.io.File; | ||
| import java.io.*; |
There was a problem hiding this comment.
Another .* import.
I think you can tell intellij not to do this by setting the minimum number of classes needed before it collapses imports to some very high number.
|
|
||
| private static final RingBuffer buffer = new RingBuffer(BUFFER_SIZE); | ||
|
|
||
| private static final Handler handler = new Handler() {{ |
There was a problem hiding this comment.
You no longer need to hang on to this reference, so just create the handler in the init() method and add it to the root logger.
| * {@link Handler} that handles {@link LogRecord}s converting them to | ||
| * {@link com.google.gapid.proto.log.Log.Message}s and then broadcasting them to listeners. | ||
| */ | ||
| protected static class Handler extends java.util.logging.Handler { |
There was a problem hiding this comment.
Maybe name this class ProtoHandler, or ProtoConverter?
There was a problem hiding this comment.
They both sound like the Proto is the source when it's the target. Opted for LogToMessageHandler.
| } | ||
| Log.Severity severity = message.getSeverity(); | ||
| TreeItem item = newItem(tree, severity); | ||
| String[] lines = message.getText().split("(\n)|(\r\n)"); |
Change-Id: I1f8ac3259cf5b528feedf0a2077c4d966f53e375
Instead of
log.Context, now usecontext.Context.To log something use:
Values are added to the context with:
GAPII and GAPIR log messages are parsed into the structured form so they can be filtered.
New streaming RPC
GetLogStream()monitors all log messages from GAPIS and all subprocesses interleaved. Can be used by GAPIC to display all logs across all processes.