xref: /aosp_15_r20/external/cronet/net/docs/net-log.md (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1# NetLog
2
3This document describes the design and use of logging through NetLog.
4
5[TOC]
6
7## Adding new NetLogging code
8
9Adding information to the NetLog helps debugging. However, logging also requires
10careful review as it can impact performance, privacy, and security.
11
12Please add a [net/log/OWNERS](../log/OWNERS) reviewer when adding new NetLog
13parameters, or adding information to existing ones.
14
15The high level objectives when adding net logging code are:
16
17* No performance cost when capturing is off.
18* Logs captured using [`kDefault`](../log/net_log_capture_mode.h) are safe to
19  upload and share publicly.
20* Capturing using [`kDefault`](../log/net_log_capture_mode.h) has a low
21  performance impact.
22* Logs captured using [`kDefault`](../log/net_log_capture_mode.h) are small
23  enough to upload to bug reports.
24* Events that may emit sensitive information have accompanying unit-tests.
25* The event and its possible parameters are documented in
26  [net_log_event_type_list.h](../log/net_log_event_type_list.h)
27
28To avoid doing work when logging is off, logging code should generally be
29conditional on `NetLog::IsCapturing()`. Note that when specifying parameters
30via a lambda, the lambda is already conditional on `IsCapturing()`.
31
32### Binary data and strings
33
34NetLog parameters are specified as a JSON serializable `base::Value`. This has
35some subtle implications:
36
37* Do not use `base::Value::Type::STRING` with non-UTF-8 data.
38* Do not use `base::Value::Type::BINARY` (the JSON serializer can't handle it)
39
40Instead:
41
42* If the string is likely ASCII or UTF-8, use `NetLogStringValue()`.
43* If the string is arbitrary data, use `NetLogBinaryValue()`.
44* If the string is guaranteed to be valid UTF-8, you can use
45  `base::Value::Type::STRING`
46
47Also consider the maximum size of any string parameters:
48
49* If the string could be large, truncate or omit it when using the default
50  capture mode. Large strings should be relegated to the `kEverything`
51  capture mode.
52
53### 64-bit integers
54
55NetLog parameters are specified as a JSON serializable `base::Value` which does
56not support 64-bit integers.
57
58Be careful when using `base::Value::Dict::Set()` as it will truncate 64-bit
59values to 32-bits.
60
61Instead use `NetLogNumberValue()`.
62
63### Backwards compatibility
64
65There is no backwards compatibility requirement for NetLog events and their
66parameters, so you are free to change their structure/value as needed.
67
68That said, changing core events may have consequences for external consumers of
69NetLogs, which rely on the structure and parameters to events for pretty
70printing and log analysis.
71
72The [NetLog viewer](https://netlog-viewer.appspot.com/) for instance pretty
73prints certain parameters based on their names, and the event name that added
74them.
75
76### Example 1
77
78Add an `PROXY_RESOLUTION_SERVICE` event without any parameters, at all capture
79modes.
80
81```
82net_log.BeginEvent(NetLogEventType::PROXY_RESOLUTION_SERVICE);
83```
84
85Analysis:
86
87* Privacy: Logging the event at all capture modes only reveals timing
88  information.
89* Performance: When not logging, has the overhead of an unconditional function
90  call (`BeginEvent`), and then a branch (test on `IsCapturing()`).
91* Size: Minimal data added to NetLog - just one parameterless event per URL
92  request.
93
94### Example 2
95
96Add a `FTP_COMMAND_SENT` event, at all capture modes, along with parameters
97that describe the FTP command.
98
99```
100if (net_log.IsCapturing()) {
101  std::string command = BuildCommandForLog();
102  net_log.AddEventWithStringParams(NetLogEventType::FTP_COMMAND_SENT,
103                                   "command", command);
104}
105```
106
107Analysis:
108
109* Privacy: Low risk given FTP traffic is unencrypted. `BuildCommandForString()`
110  should additionally best-effort strip any identity information, as this is
111  being logged at all capture modes.
112* Performance: Costs one branch when not capturing. The call to
113  `BuildCommandForString()` is only executed when capturing.
114* Size: Cost is proportional to the average FTP command length and frequency of
115  FTP, both of which are low. `BuildCommandForLog()` needn't strictly bound the
116  string length. If a huge FTP command makes it to a NetLog, there is a good
117  chance that is the problem being debugged.
118
119### Example 3
120
121Add a `SSL_CERTIFICATES_RECEIVED` event, along with the full certificate chain,
122at all capture modes.
123
124```
125net_log.AddEvent(NetLogEventType::SSL_CERTIFICATES_RECEIVED, [&] {
126  base::Value::Dict dict;
127  base::Value::List certs;
128  std::vector<std::string> encoded_chain;
129  server_cert_->GetPEMEncodedChain(&encoded_chain);
130  for (auto& pem : encoded_chain)
131    certs.Append(std::move(pem));
132  dict.Set("certificates", std::move(certs));
133  return base::Value(std::move(dict));
134});
135```
136
137Analysis:
138
139* Privacy: Low risk as server certificates are generally public data.
140* Performance: Costs one branch when logging is off (hidden by template
141  expansion). The code in the lambda which builds the `base::Value` parameters is only
142  executed when capturing.
143* Size: On average 8K worth of data per request (average of 2K/certificate,
144  chain length of 3, and the overhead of PEM-encoding). This is heavy-weight
145  for inclusion at `kDefault` capture mode, however justified based on how
146  useful the data is.
147
148### Example 4
149
150Add a `COOKIE_STORE_COOKIE_ADDED` event at all capture modes. Moreover, if the
151capture mode is `kIncludeSensitive` or `kEverything`, also logs the cookie's
152name and value.
153
154```
155net_log.AddEvent(NetLogEventType::COOKIE_STORE_COOKIE_ADDED,
156                 [&](NetLogCaptureMode capture_mode) {
157                   if (!NetLogCaptureIncludesSensitive(capture_mode))
158                     return base::Value();
159                   base::Value::Dict dict;
160                   dict.Set("name", cookie->Name());
161                   dict.Set("value", cookie->Value());
162                   return base::Value(std::move(dict));
163                 });
164```
165
166Analysis:
167
168* Privacy: The cookie name and value are not included at the `kDefault` capture
169  mode, so only cookie counts and timing information is revealed.
170* Performance: Costs one branch when logging is off (hidden by template
171  expansion). The code in the lambda which builds the `base::Value` parameters is only
172  executed when capturing.
173* Size: For default captured logs, has a file size cost proportional to the
174  number of cookies added. This is borderline justifiable. It would be better
175  in this case to simply omit the event all together at `kDefault` than to log
176  a parameterless event, as the parameterless event is not broadly useful.
177