1 /* 2 * Copyright 2019 The gRPC Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package io.grpc.okhttp; 18 19 import static com.google.common.base.Preconditions.checkNotNull; 20 21 import com.google.common.annotations.VisibleForTesting; 22 import io.grpc.okhttp.internal.framed.ErrorCode; 23 import io.grpc.okhttp.internal.framed.Header; 24 import io.grpc.okhttp.internal.framed.Settings; 25 import java.util.EnumMap; 26 import java.util.List; 27 import java.util.logging.Level; 28 import java.util.logging.Logger; 29 import okio.Buffer; 30 import okio.ByteString; 31 32 class OkHttpFrameLogger { 33 private static final int BUFFER_LENGTH_THRESHOLD = 64; 34 private final Logger logger; 35 private final Level level; 36 OkHttpFrameLogger(Level level, Class<?> clazz)37 OkHttpFrameLogger(Level level, Class<?> clazz) { 38 this(level, Logger.getLogger(clazz.getName())); 39 } 40 41 @VisibleForTesting OkHttpFrameLogger(Level level, Logger logger)42 OkHttpFrameLogger(Level level, Logger logger) { 43 this.level = checkNotNull(level, "level"); 44 this.logger = checkNotNull(logger, "logger"); 45 } 46 toString(Settings settings)47 private static String toString(Settings settings) { 48 EnumMap<SettingParams, Integer> map = new EnumMap<>(SettingParams.class); 49 for (SettingParams p : SettingParams.values()) { 50 // Only log set parameters. 51 if (settings.isSet(p.getBit())) { 52 map.put(p, settings.get(p.getBit())); 53 } 54 } 55 return map.toString(); 56 } 57 toString(Buffer buf)58 private static String toString(Buffer buf) { 59 if (buf.size() <= BUFFER_LENGTH_THRESHOLD) { 60 // Log the entire buffer. 61 return buf.snapshot().hex(); 62 } 63 64 // Otherwise just log the first 64 bytes. 65 int length = (int) Math.min(buf.size(), BUFFER_LENGTH_THRESHOLD); 66 return buf.snapshot(length).hex() + "..."; 67 } 68 isEnabled()69 private boolean isEnabled() { 70 return logger.isLoggable(level); 71 } 72 logData(Direction direction, int streamId, Buffer data, int length, boolean endStream)73 void logData(Direction direction, int streamId, Buffer data, int length, boolean endStream) { 74 if (isEnabled()) { 75 logger.log( 76 level, 77 direction 78 + " DATA: streamId=" 79 + streamId 80 + " endStream=" 81 + endStream 82 + " length=" 83 + length 84 + " bytes=" 85 + toString(data)); 86 } 87 } 88 logHeaders(Direction direction, int streamId, List<Header> headers, boolean endStream)89 void logHeaders(Direction direction, int streamId, List<Header> headers, boolean endStream) { 90 if (isEnabled()) { 91 logger.log( 92 level, 93 direction 94 + " HEADERS: streamId=" 95 + streamId 96 + " headers=" 97 + headers 98 + " endStream=" 99 + endStream); 100 } 101 } 102 logPriority( Direction direction, int streamId, int streamDependency, int weight, boolean exclusive)103 public void logPriority( 104 Direction direction, int streamId, int streamDependency, int weight, boolean exclusive) { 105 if (isEnabled()) { 106 logger.log( 107 level, 108 direction 109 + " PRIORITY: streamId=" 110 + streamId 111 + " streamDependency=" 112 + streamDependency 113 + " weight=" 114 + weight 115 + " exclusive=" 116 + exclusive); 117 } 118 } 119 logRstStream(Direction direction, int streamId, ErrorCode errorCode)120 void logRstStream(Direction direction, int streamId, ErrorCode errorCode) { 121 if (isEnabled()) { 122 logger.log( 123 level, direction + " RST_STREAM: streamId=" + streamId + " errorCode=" + errorCode); 124 } 125 } 126 logSettingsAck(Direction direction)127 void logSettingsAck(Direction direction) { 128 if (isEnabled()) { 129 logger.log(level, direction + " SETTINGS: ack=true"); 130 } 131 } 132 logSettings(Direction direction, Settings settings)133 void logSettings(Direction direction, Settings settings) { 134 if (isEnabled()) { 135 logger.log(level, direction + " SETTINGS: ack=false settings=" + toString(settings)); 136 } 137 } 138 logPing(Direction direction, long data)139 void logPing(Direction direction, long data) { 140 if (isEnabled()) { 141 logger.log(level, direction + " PING: ack=false bytes=" + data); 142 } 143 } 144 logPingAck(Direction direction, long data)145 void logPingAck(Direction direction, long data) { 146 if (isEnabled()) { 147 logger.log(level, direction + " PING: ack=true bytes=" + data); 148 } 149 } 150 logPushPromise( Direction direction, int streamId, int promisedStreamId, List<Header> headers)151 void logPushPromise( 152 Direction direction, int streamId, int promisedStreamId, List<Header> headers) { 153 if (isEnabled()) { 154 logger.log( 155 level, 156 direction 157 + " PUSH_PROMISE: streamId=" 158 + streamId 159 + " promisedStreamId=" 160 + promisedStreamId 161 + " headers=" 162 + headers); 163 } 164 } 165 logGoAway(Direction direction, int lastStreamId, ErrorCode errorCode, ByteString debugData)166 void logGoAway(Direction direction, int lastStreamId, ErrorCode errorCode, ByteString debugData) { 167 if (isEnabled()) { 168 logger.log( 169 level, 170 direction 171 + " GO_AWAY: lastStreamId=" 172 + lastStreamId 173 + " errorCode=" 174 + errorCode 175 + " length=" 176 + debugData.size() 177 + " bytes=" 178 + toString(new Buffer().write(debugData))); 179 } 180 } 181 logWindowsUpdate(Direction direction, int streamId, long windowSizeIncrement)182 void logWindowsUpdate(Direction direction, int streamId, long windowSizeIncrement) { 183 if (isEnabled()) { 184 logger.log( 185 level, 186 direction 187 + " WINDOW_UPDATE: streamId=" 188 + streamId 189 + " windowSizeIncrement=" 190 + windowSizeIncrement); 191 } 192 } 193 194 enum Direction { 195 INBOUND, 196 OUTBOUND 197 } 198 199 // Note the set bits in OkHttp's Settings are different from HTTP2 Specifications. 200 private enum SettingParams { 201 HEADER_TABLE_SIZE(1), 202 ENABLE_PUSH(2), 203 MAX_CONCURRENT_STREAMS(4), 204 MAX_FRAME_SIZE(5), 205 MAX_HEADER_LIST_SIZE(6), 206 INITIAL_WINDOW_SIZE(7); 207 208 private final int bit; 209 SettingParams(int bit)210 SettingParams(int bit) { 211 this.bit = bit; 212 } 213 getBit()214 public int getBit() { 215 return this.bit; 216 } 217 } 218 } 219