Skip to content

core: ChannelLogger#5024

Merged
zhangkun83 merged 11 commits into
grpc:masterfrom
zhangkun83:lb_helper_record_trace
Nov 7, 2018
Merged

core: ChannelLogger#5024
zhangkun83 merged 11 commits into
grpc:masterfrom
zhangkun83:lb_helper_record_trace

Conversation

@zhangkun83

Copy link
Copy Markdown
Contributor

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 FINE level or below so that they are not visible by default. They are logged to ChannelLogger'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 DEBUG level for ChannelLogger, 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.

* 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 {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this threadsafe?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Added annotation.

* +---------------------+-------------------+-------------------+
* | ChannelLogger Level | Channelz Severity | Java Logger Level |
* +---------------------+-------------------+-------------------+
* | DEBUG | N/A | FINEST |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's suffiicient to make this a String instead of a hard ref, and then have a test that assert they all work.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should document the behavior change of maxEvents

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

return super.add(event);
}
};
ChannelTracer(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can description be null?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No one is passing null, so I just added the null check.

* +---------------------+-------------------+-------------------+
* </pre>
*/
public enum Level {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This probably deserves a name like ChannelLevel, since it behaves differently than other "Levels".

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed to ChannelLogLevel.

import io.grpc.Attributes;
import io.grpc.CallOptions;
import io.grpc.ChannelLogger;
import io.grpc.ChannelLogger.Level;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We really want to know whether or not this log message will be used, to avoid the string handling when useless.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

* 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);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should use MessageFormat.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Used MessageFormat. PTAL.

tracer.logOnly(Level.FINEST, MessageFormat.format(messageFormat, args));
}
} else {
reportToTracer(level, MessageFormat.format(messageFormat, args));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will create the event and format the message even when tracing is disabled.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@zhangkun83 zhangkun83 merged commit 6b48eb4 into grpc:master Nov 7, 2018
@zhangkun83 zhangkun83 deleted the lb_helper_record_trace branch November 7, 2018 00:48
@lock lock Bot locked as resolved and limited conversation to collaborators Feb 5, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants