blob: 8c8243c90217420b73aa9df7d53eba4e897ba719 [file] [log] [blame]
/*
* Copyright 2018 The gRPC Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.grpc.internal;
import static com.google.common.base.Preconditions.checkNotNull;
import io.grpc.ChannelLogger;
import io.grpc.InternalChannelz.ChannelStats;
import io.grpc.InternalChannelz.ChannelTrace;
import io.grpc.InternalChannelz.ChannelTrace.Event;
import io.grpc.InternalLogId;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;
/**
* Tracks a collections of channel tracing events for a channel/subchannel.
*/
final class ChannelTracer {
// The logs go to ChannelLogger's logger so that user can control the logging level on that public
// class rather than on this internal class.
static final Logger logger = Logger.getLogger(ChannelLogger.class.getName());
private final Object lock = new Object();
private final InternalLogId logId;
@GuardedBy("lock")
@Nullable
private final Collection<Event> events;
private final long channelCreationTimeNanos;
@GuardedBy("lock")
private int eventsLogged;
/**
* Creates a channel tracer and log the creation event of the underlying channel.
*
* @param logId logId will be prepended to the logs logged to Java logger
* @param maxEvents maximum number of events that are retained in memory. If not a positive
* number no events will be retained, but they will still be sent to the Java logger.
* @param channelCreationTimeNanos the creation time of the entity being traced
* @param description a description of the entity being traced
*/
ChannelTracer(
InternalLogId logId, final int maxEvents, long channelCreationTimeNanos, String description) {
checkNotNull(description, "description");
this.logId = checkNotNull(logId, "logId");
if (maxEvents > 0) {
events = new ArrayDeque<Event>() {
@GuardedBy("lock")
@Override
public boolean add(Event event) {
if (size() == maxEvents) {
removeFirst();
}
eventsLogged++;
return super.add(event);
}
};
} else {
events = null;
}
this.channelCreationTimeNanos = channelCreationTimeNanos;
reportEvent(new ChannelTrace.Event.Builder()
.setDescription(description + " created")
.setSeverity(ChannelTrace.Event.Severity.CT_INFO)
// passing the timestamp in as a parameter instead of computing it right here because when
// parent channel and subchannel both report the same event of the subchannel (e.g. creation
// event of the subchannel) we want the timestamps to be exactly the same.
.setTimestampNanos(channelCreationTimeNanos)
.build());
}
void reportEvent(Event event) {
Level logLevel;
switch (event.severity) {
case CT_ERROR:
logLevel = Level.FINE;
break;
case CT_WARNING:
logLevel = Level.FINER;
break;
default:
logLevel = Level.FINEST;
}
traceOnly(event);
logOnly(logId, logLevel, event.description);
}
boolean isTraceEnabled() {
synchronized (lock) {
return events != null;
}
}
void traceOnly(Event event) {
synchronized (lock) {
if (events != null) {
events.add(event);
}
}
}
static void logOnly(InternalLogId logId, Level logLevel, String msg) {
if (logger.isLoggable(logLevel)) {
LogRecord lr = new LogRecord(logLevel, "[" + logId + "] " + msg);
// No resource bundle as gRPC is not localized.
lr.setLoggerName(logger.getName());
lr.setSourceClassName(logger.getName());
// Both logger methods are called log in ChannelLogger.
lr.setSourceMethodName("log");
logger.log(lr);
}
}
InternalLogId getLogId() {
return logId;
}
void updateBuilder(ChannelStats.Builder builder) {
List<Event> eventsSnapshot;
int eventsLoggedSnapshot;
synchronized (lock) {
if (events == null) {
return;
}
eventsLoggedSnapshot = eventsLogged;
eventsSnapshot = new ArrayList<>(events);
}
builder.setChannelTrace(new ChannelTrace.Builder()
.setNumEventsLogged(eventsLoggedSnapshot)
.setCreationTimeNanos(channelCreationTimeNanos)
.setEvents(eventsSnapshot)
.build());
}
}