core: ChannelLogger#5024
Conversation
| * A Channel-specific logger provided by GRPC library to {@link LoadBalancer} implementations. | ||
| * Information logged here goes to <string>Channelz</strong>, and to Java logger as well. | ||
| */ | ||
| public abstract class ChannelLogger { |
There was a problem hiding this comment.
is this threadsafe?
There was a problem hiding this comment.
Yes. Added annotation.
| * +---------------------+-------------------+-------------------+ | ||
| * | ChannelLogger Level | Channelz Severity | Java Logger Level | | ||
| * +---------------------+-------------------+-------------------+ | ||
| * | DEBUG | N/A | FINEST | |
There was a problem hiding this comment.
This table should be encoded as arguments to the enum valuies:
public enum Level {
DEBUG(null, "CT_INFO", Level.FINEST),
}That way the mapping can be without a switch statement below.
There was a problem hiding this comment.
I actually need to reference the CT_INFO enum to make this useful and robust, but it's internal API and I don't see how I can do it without exposing it.
There was a problem hiding this comment.
It's suffiicient to make this a String instead of a hard ref, and then have a test that assert they all work.
There was a problem hiding this comment.
For these fields to be accessible from ChannelLoggerImpl, they would need to be public, which technically exposes these CT_ enums even not referencing them directly. Doesn't seem enough benefit from doing that.
There was a problem hiding this comment.
These could be exposed via an internal accessor.
The benefit is that we can use hard references to see exactly how they map, rather than relying on switch statements.
| } | ||
| }; | ||
| ChannelTracer( | ||
| InternalLogId logId, final int maxEvents, long channelCreationTimeNanos, String description) { |
There was a problem hiding this comment.
You should document the behavior change of maxEvents
| return super.add(event); | ||
| } | ||
| }; | ||
| ChannelTracer( |
There was a problem hiding this comment.
Can description be null?
There was a problem hiding this comment.
No one is passing null, so I just added the null check.
| * +---------------------+-------------------+-------------------+ | ||
| * </pre> | ||
| */ | ||
| public enum Level { |
There was a problem hiding this comment.
This probably deserves a name like ChannelLevel, since it behaves differently than other "Levels".
There was a problem hiding this comment.
Renamed to ChannelLogLevel.
| import io.grpc.Attributes; | ||
| import io.grpc.CallOptions; | ||
| import io.grpc.ChannelLogger; | ||
| import io.grpc.ChannelLogger.Level; |
There was a problem hiding this comment.
I'd suggest not importing this and always referencing it via ChannelLogger.Level. Otherwise it is pretty confusing and you can't import java.util.logging.Level.
There was a problem hiding this comment.
As suggested by @carl-mastrangelo, I have renamed Level to ChannelLogLevel so there is no collision.
| void propagateError(Status status) { | ||
| logger.log(Level.FINE, "[{0}] Had an error: {1}; dropList={2}; backendList={3}", | ||
| new Object[] {logId, status, dropList, backendList}); | ||
| logger.log(Level.DEBUG, "Error: " + status); |
There was a problem hiding this comment.
We really want to know whether or not this log message will be used, to avoid the string handling when useless.
There was a problem hiding this comment.
Since ChannelLogger is not on data path and is called pretty infrequently, I am not sure it's worth the code clutter to wrap it with a check.
There was a problem hiding this comment.
Things like logger.log(Level.DEBUG, "Got an LB response: " + response); could still be large and run on the network thread. Also, some LBs may do RPCs on cache misses, which could want to log to DEBUG.
I'm a bit worried about how much is getting pulled into this API. There's a lot of data being logged. Like every time the picker changes with logger.log(Level.INFO, state + ": picks=" + picker.pickList + ", drops=" + picker.dropList);. I know that isn't on the critical path, but that still happens frequently. And most of the time nobody will see it.
There was a problem hiding this comment.
What about log(Level level, String template, Object... args)?. For now I can check the loggable level in the implementation and that would save the string generation for DEBUG. We could optimize ChannelTracer even later to generate strings only when Channelz is requested.
There was a problem hiding this comment.
log(Level, String, Object...) seems fine to me. It'd probably be an overload (so log(Level, String) would still exist) to avoid allocating the Object[] when unnecessary.
…neration for DEBUG if not logged.
| * Logs a message, using a message template and a list of arguments used to generate the log | ||
| * message with {@link String#format}. | ||
| */ | ||
| public abstract void log(ChannelLogLevel level, String template, Object... args); |
There was a problem hiding this comment.
Isn't this format different than what normal loggers use? (i.e. {}) ?
I think this should be something that fits in with other, similar log statements. (I.e. it should do what SimpleFormatter does)
There was a problem hiding this comment.
Used MessageFormat. PTAL.
| tracer.logOnly(Level.FINEST, MessageFormat.format(messageFormat, args)); | ||
| } | ||
| } else { | ||
| reportToTracer(level, MessageFormat.format(messageFormat, args)); |
There was a problem hiding this comment.
This will create the event and format the message even when tracing is disabled.
Introduce
ChannelLogger, which is a utility provided to LoadBalancer implementations (potentially NameResolvers too) for recording events to channel trace. This is immediately required by client-side health checking (#4932, https://github.com/grpc/proposal/blob/master/A17-client-side-health-checking.md) to record an error about disabling health checking. It is also useful for any LoadBalancer implementations to record important information.ChannelLogger implementation is backed by the internal ChannelTracer/Channelz. Because Channelz limits the number of retained events, and events are lost once the process ends, I have expanded it to also log Java logger. This would provide a "last resort" in cases where there are too many events or off-line investigation is needed. All logs are prefixed with logId so that they can be easily associated with the involved Channel/Subchannel.
To prevent log spamming, the logs are all at
FINElevel or below so that they are not visible by default. They are logged toChannelLogger's logger, so that user can have precise control.There are also more verbose information that may not fit in ChannelTracer, but can be useful for debugging. It's desirable that these logs are associated with logId, but they currently manually include the logId, which is cumbersome and may result in inconsistency. For this use case, I added the
DEBUGlevel forChannelLogger, which formats the log in the same way as other levels, while not recorded to Channelz.I have converted most logging and channel tracer recording in the Channel implementation and LoadBalancers.