OLD | NEW |
| (Empty) |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "blimp/common/logging.h" | |
6 | |
7 #include <iostream> | |
8 #include <string> | |
9 #include <utility> | |
10 #include <vector> | |
11 | |
12 #include "base/format_macros.h" | |
13 #include "base/json/string_escape.h" | |
14 #include "base/lazy_instance.h" | |
15 #include "base/memory/ptr_util.h" | |
16 #include "base/strings/string_number_conversions.h" | |
17 #include "base/strings/string_util.h" | |
18 #include "base/strings/stringprintf.h" | |
19 #include "blimp/common/create_blimp_message.h" | |
20 #include "blimp/common/proto/blimp_message.pb.h" | |
21 | |
22 namespace blimp { | |
23 namespace { | |
24 | |
25 using LogFields = std::vector<std::pair<std::string, std::string>>; | |
26 | |
27 // The AddField() suite of functions are used to convert KV pairs with | |
28 // arbitrarily typed values into string/string KV pairs for logging. | |
29 | |
30 // Specialization for string values, surrounding them with quotes and escaping | |
31 // characters as necessary. | |
32 void AddField(const std::string& key, | |
33 const std::string& value, | |
34 LogFields* output) { | |
35 std::string escaped_value; | |
36 base::EscapeJSONString(value, true, &escaped_value); | |
37 output->push_back(std::make_pair(key, escaped_value)); | |
38 } | |
39 | |
40 // Specialization for string literal values. | |
41 void AddField(const std::string& key, const char* value, LogFields* output) { | |
42 output->push_back(std::make_pair(key, std::string(value))); | |
43 } | |
44 | |
45 // Specialization for boolean values (serialized as "true" or "false"). | |
46 void AddField(const std::string& key, bool value, LogFields* output) { | |
47 output->push_back(std::make_pair(key, (value ? "true" : "false"))); | |
48 } | |
49 | |
50 // Specialization for SizeMessage values, serializing them as | |
51 // WIDTHxHEIGHT:RATIO. RATIO is rounded to two digits of precision | |
52 // (e.g. 2.123 => 2.12). | |
53 void AddField(const std::string& key, | |
54 const SizeMessage& value, | |
55 LogFields* output) { | |
56 output->push_back(std::make_pair( | |
57 key, base::StringPrintf("%" PRIu64 "x%" PRIu64 ":%.2lf", value.width(), | |
58 value.height(), value.device_pixel_ratio()))); | |
59 } | |
60 | |
61 // Conversion function for all other types. | |
62 // Uses std::to_string() to serialize |value|. | |
63 template <typename T> | |
64 void AddField(const std::string& key, const T& value, LogFields* output) { | |
65 output->push_back(std::make_pair(key, std::to_string(value))); | |
66 } | |
67 | |
68 // The following LogExtractor subclasses contain logic for extracting loggable | |
69 // fields from BlimpMessages. | |
70 | |
71 // Logs fields from COMPOSITOR messages. | |
72 void ExtractCompositorMessageFields(const BlimpMessage& message, | |
73 LogFields* output) { | |
74 AddField("render_widget_id", message.compositor().render_widget_id(), output); | |
75 } | |
76 | |
77 // Logs fields from IME messages. | |
78 void ExtractImeMessageFields(const BlimpMessage& message, LogFields* output) { | |
79 AddField("render_widget_id", message.ime().render_widget_id(), output); | |
80 switch (message.ime().type()) { | |
81 case ImeMessage::SHOW_IME: | |
82 AddField("subtype", "SHOW_IME", output); | |
83 AddField("text_input_type", message.ime().text_input_type(), output); | |
84 break; | |
85 case ImeMessage::HIDE_IME: | |
86 AddField("subtype", "HIDE_IME", output); | |
87 break; | |
88 case ImeMessage::SET_TEXT: | |
89 AddField("subtype", "SET_TEXT", output); | |
90 AddField("ime_text(length)", message.ime().ime_text().size(), output); | |
91 break; | |
92 case ImeMessage::UNKNOWN: | |
93 AddField("subtype", "UNKNOWN", output); | |
94 break; | |
95 } | |
96 } | |
97 | |
98 // Logs fields from INPUT messages. | |
99 void ExtractInputMessageFields(const BlimpMessage& message, LogFields* output) { | |
100 AddField("render_widget_id", message.input().render_widget_id(), output); | |
101 AddField("timestamp_seconds", message.input().timestamp_seconds(), output); | |
102 switch (message.input().type()) { | |
103 case InputMessage::Type_GestureScrollBegin: | |
104 AddField("subtype", "GestureScrollBegin", output); | |
105 break; | |
106 case InputMessage::Type_GestureScrollEnd: | |
107 AddField("subtype", "GestureScrollEnd", output); | |
108 break; | |
109 case InputMessage::Type_GestureScrollUpdate: | |
110 AddField("subtype", "GestureScrollUpdate", output); | |
111 break; | |
112 case InputMessage::Type_GestureFlingStart: | |
113 AddField("subtype", "GestureFlingStart", output); | |
114 break; | |
115 case InputMessage::Type_GestureFlingCancel: | |
116 AddField("subtype", "GestureFlingCancel", output); | |
117 AddField("prevent_boosting", | |
118 message.input().gesture_fling_cancel().prevent_boosting(), | |
119 output); | |
120 break; | |
121 case InputMessage::Type_GestureTap: | |
122 AddField("subtype", "GestureTap", output); | |
123 break; | |
124 case InputMessage::Type_GesturePinchBegin: | |
125 AddField("subtype", "GesturePinchBegin", output); | |
126 break; | |
127 case InputMessage::Type_GesturePinchEnd: | |
128 AddField("subtype", "GesturePinchEnd", output); | |
129 break; | |
130 case InputMessage::Type_GesturePinchUpdate: | |
131 AddField("subtype", "GesturePinchUpdate", output); | |
132 break; | |
133 case InputMessage::Type_GestureTapDown: | |
134 AddField("subtype", "GestureTapDown", output); | |
135 break; | |
136 case InputMessage::Type_GestureTapCancel: | |
137 AddField("subtype", "GestureTapCancel", output); | |
138 break; | |
139 case InputMessage::Type_GestureTapUnconfirmed: | |
140 AddField("subtype", "GestureTapUnconfirmed", output); | |
141 break; | |
142 case InputMessage::Type_GestureShowPress: | |
143 AddField("subtype", "GestureShowPress", output); | |
144 break; | |
145 case InputMessage::UNKNOWN: | |
146 break; | |
147 } | |
148 } | |
149 | |
150 // Logs fields from NAVIGATION messages. | |
151 void ExtractNavigationMessageFields(const BlimpMessage& message, | |
152 LogFields* output) { | |
153 switch (message.navigation().type()) { | |
154 case NavigationMessage::NAVIGATION_STATE_CHANGED: | |
155 AddField("subtype", "NAVIGATION_STATE_CHANGED", output); | |
156 if (message.navigation().navigation_state_changed().has_url()) { | |
157 AddField("url", message.navigation().navigation_state_changed().url(), | |
158 output); | |
159 } | |
160 if (message.navigation().navigation_state_changed().has_favicon()) { | |
161 AddField( | |
162 "favicon_size", | |
163 message.navigation().navigation_state_changed().favicon().size(), | |
164 output); | |
165 } | |
166 if (message.navigation().navigation_state_changed().has_title()) { | |
167 AddField("title", | |
168 message.navigation().navigation_state_changed().title(), | |
169 output); | |
170 } | |
171 if (message.navigation().navigation_state_changed().has_loading()) { | |
172 AddField("loading", | |
173 message.navigation().navigation_state_changed().loading(), | |
174 output); | |
175 } | |
176 break; | |
177 case NavigationMessage::LOAD_URL: | |
178 AddField("subtype", "LOAD_URL", output); | |
179 AddField("url", message.navigation().load_url().url(), output); | |
180 break; | |
181 case NavigationMessage::GO_BACK: | |
182 AddField("subtype", "GO_BACK", output); | |
183 break; | |
184 case NavigationMessage::GO_FORWARD: | |
185 AddField("subtype", "GO_FORWARD", output); | |
186 break; | |
187 case NavigationMessage::RELOAD: | |
188 AddField("subtype", "RELOAD", output); | |
189 break; | |
190 case NavigationMessage::UNKNOWN: | |
191 break; | |
192 } | |
193 } | |
194 | |
195 // Logs fields from PROTOCOL_CONTROL messages. | |
196 void ExtractProtocolControlMessageFields(const BlimpMessage& message, | |
197 LogFields* output) { | |
198 switch (message.protocol_control().connection_message_case()) { | |
199 case ProtocolControlMessage::kStartConnection: | |
200 AddField("subtype", "START_CONNECTION", output); | |
201 AddField( | |
202 "client_token", | |
203 message.protocol_control().start_connection().client_auth_token(), | |
204 output); | |
205 AddField("protocol_version", | |
206 message.protocol_control().start_connection().protocol_version(), | |
207 output); | |
208 break; | |
209 case ProtocolControlMessage::kCheckpointAck: | |
210 AddField("subtype", "CHECKPOINT_ACK", output); | |
211 AddField("checkpoint_id", | |
212 message.protocol_control().checkpoint_ack().checkpoint_id(), | |
213 output); | |
214 break; | |
215 case ProtocolControlMessage::kEndConnection: | |
216 AddField("subtype", "END_CONNECTION", output); | |
217 switch (message.protocol_control().end_connection().reason()) { | |
218 case EndConnectionMessage::AUTHENTICATION_FAILED: | |
219 AddField("reason", "AUTHENTICATION_FAILED", output); | |
220 break; | |
221 case EndConnectionMessage::PROTOCOL_MISMATCH: | |
222 AddField("reason", "PROTOCOL_MISMATCH", output); | |
223 break; | |
224 case EndConnectionMessage::UNKNOWN: | |
225 break; | |
226 } | |
227 break; | |
228 case ProtocolControlMessage::CONNECTION_MESSAGE_NOT_SET: | |
229 break; | |
230 } | |
231 } | |
232 | |
233 // Logs fields from RENDER_WIDGET messages. | |
234 void ExtractRenderWidgetMessageFields(const BlimpMessage& message, | |
235 LogFields* output) { | |
236 switch (message.render_widget().type()) { | |
237 case RenderWidgetMessage::INITIALIZE: | |
238 AddField("subtype", "INITIALIZE", output); | |
239 break; | |
240 case RenderWidgetMessage::CREATED: | |
241 AddField("subtype", "CREATED", output); | |
242 break; | |
243 case RenderWidgetMessage::DELETED: | |
244 AddField("subtype", "DELETED", output); | |
245 break; | |
246 case RenderWidgetMessage::UNKNOWN: | |
247 break; | |
248 } | |
249 AddField("render_widget_id", message.render_widget().render_widget_id(), | |
250 output); | |
251 } | |
252 | |
253 // Logs fields from SETTINGS messages. | |
254 void ExtractSettingsMessageFields(const BlimpMessage& message, | |
255 LogFields* output) { | |
256 if (message.settings().has_engine_settings()) { | |
257 const EngineSettingsMessage& engine_settings = | |
258 message.settings().engine_settings(); | |
259 AddField("subtype", "ENGINE_SETTINGS", output); | |
260 AddField("record_whole_document", engine_settings.record_whole_document(), | |
261 output); | |
262 AddField("client_os_info", engine_settings.client_os_info(), output); | |
263 } | |
264 } | |
265 | |
266 // Logs fields from TAB_CONTROL messages. | |
267 void ExtractTabControlMessageFields(const BlimpMessage& message, | |
268 LogFields* output) { | |
269 switch (message.tab_control().tab_control_case()) { | |
270 case TabControlMessage::kCreateTab: | |
271 AddField("subtype", "CREATE_TAB", output); | |
272 break; | |
273 case TabControlMessage::kCloseTab: | |
274 AddField("subtype", "CLOSE_TAB", output); | |
275 break; | |
276 case TabControlMessage::kSize: | |
277 AddField("subtype", "SIZE", output); | |
278 AddField("size", message.tab_control().size(), output); | |
279 break; | |
280 case TabControlMessage::TAB_CONTROL_NOT_SET: | |
281 break; | |
282 } | |
283 } | |
284 | |
285 // Logs fields from BLOB_CHANNEL messages. | |
286 void ExtractBlobChannelMessageFields(const BlimpMessage& message, | |
287 LogFields* output) { | |
288 switch (message.blob_channel().type_case()) { | |
289 case BlobChannelMessage::TypeCase::kTransferBlob: | |
290 AddField("subtype", "TRANSFER_BLOB", output); | |
291 AddField("id", | |
292 base::HexEncode( | |
293 message.blob_channel().transfer_blob().blob_id().data(), | |
294 message.blob_channel().transfer_blob().blob_id().size()), | |
295 output); | |
296 AddField("payload_size", | |
297 message.blob_channel().transfer_blob().payload().size(), output); | |
298 break; | |
299 case BlobChannelMessage::TypeCase::TYPE_NOT_SET: // unknown | |
300 break; | |
301 } | |
302 } | |
303 | |
304 // Logs fields from GEOLOCATION messages. | |
305 void ExtractGeolocationMessageFields(const BlimpMessage& message, | |
306 LogFields* output) { | |
307 switch (message.geolocation().type_case()) { | |
308 case GeolocationMessage::TypeCase::kSetInterestLevel: | |
309 AddField("subtype", "SET_INTEREST_LEVEL", output); | |
310 AddField("level", message.geolocation().set_interest_level().level(), | |
311 output); | |
312 break; | |
313 case GeolocationMessage::TypeCase::kRequestRefresh: | |
314 AddField("subtype", "REQUEST_REFRESH", output); | |
315 break; | |
316 case GeolocationMessage::TypeCase::kCoordinates: { | |
317 const GeolocationCoordinatesMessage& coordinates = | |
318 message.geolocation().coordinates(); | |
319 AddField("subtype", "COORDINATES", output); | |
320 AddField("latitude", coordinates.latitude(), output); | |
321 AddField("longitude", coordinates.longitude(), output); | |
322 AddField("altitude", coordinates.altitude(), output); | |
323 AddField("accuracy", coordinates.accuracy(), output); | |
324 AddField("altitude_accuracy", coordinates.altitude_accuracy(), output); | |
325 AddField("heading", coordinates.heading(), output); | |
326 AddField("speed", coordinates.speed(), output); | |
327 break; | |
328 } | |
329 case GeolocationMessage::TypeCase::kError: | |
330 AddField("subtype", "ERROR", output); | |
331 AddField("error_code", message.geolocation().error().error_code(), | |
332 output); | |
333 AddField("error_message", message.geolocation().error().error_message(), | |
334 output); | |
335 break; | |
336 case GeolocationMessage::TypeCase::TYPE_NOT_SET: | |
337 break; | |
338 } | |
339 } | |
340 | |
341 } // namespace | |
342 | |
343 std::ostream& operator<<(std::ostream& out, const BlimpMessage& message) { | |
344 LogFields fields; | |
345 | |
346 switch (message.feature_case()) { | |
347 case BlimpMessage::kCompositor: | |
348 AddField("type", "COMPOSITOR", &fields); | |
349 ExtractCompositorMessageFields(message, &fields); | |
350 break; | |
351 case BlimpMessage::kInput: | |
352 AddField("type", "INPUT", &fields); | |
353 ExtractInputMessageFields(message, &fields); | |
354 break; | |
355 case BlimpMessage::kNavigation: | |
356 AddField("type", "NAVIGATION", &fields); | |
357 ExtractNavigationMessageFields(message, &fields); | |
358 break; | |
359 case BlimpMessage::kProtocolControl: | |
360 AddField("type", "PROTOCOL_CONTROL", &fields); | |
361 ExtractProtocolControlMessageFields(message, &fields); | |
362 break; | |
363 case BlimpMessage::kRenderWidget: | |
364 AddField("type", "RENDER_WIDGET", &fields); | |
365 ExtractRenderWidgetMessageFields(message, &fields); | |
366 break; | |
367 case BlimpMessage::kSettings: | |
368 AddField("type", "SETTINGS", &fields); | |
369 ExtractSettingsMessageFields(message, &fields); | |
370 break; | |
371 case BlimpMessage::kTabControl: | |
372 AddField("type", "TAB_CONTROL", &fields); | |
373 ExtractTabControlMessageFields(message, &fields); | |
374 break; | |
375 case BlimpMessage::kBlobChannel: | |
376 AddField("type", "BLOB_CHANNEL", &fields); | |
377 ExtractBlobChannelMessageFields(message, &fields); | |
378 break; | |
379 case BlimpMessage::kIme: | |
380 AddField("type", "IME", &fields); | |
381 ExtractImeMessageFields(message, &fields); | |
382 break; | |
383 case BlimpMessage::kGeolocation: | |
384 AddField("type", "GEOLOCATION", &fields); | |
385 ExtractGeolocationMessageFields(message, &fields); | |
386 break; | |
387 case BlimpMessage::FEATURE_NOT_SET: | |
388 AddField("type", "<UNKNOWN>", &fields); | |
389 break; | |
390 } | |
391 | |
392 // Append "target_tab_id" (if present) and "byte_size" to the field set. | |
393 if (message.has_target_tab_id()) { | |
394 AddField("target_tab_id", message.target_tab_id(), &fields); | |
395 } | |
396 AddField("byte_size", message.ByteSize(), &fields); | |
397 | |
398 // Format message using the syntax: | |
399 // <BlimpMessage field1=value1 field2="value 2"> | |
400 out << "<BlimpMessage "; | |
401 for (size_t i = 0; i < fields.size(); ++i) { | |
402 out << fields[i].first << "=" << fields[i].second | |
403 << (i != fields.size() - 1 ? " " : ""); | |
404 } | |
405 out << ">"; | |
406 | |
407 return out; | |
408 } | |
409 | |
410 } // namespace blimp | |
OLD | NEW |