Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3)

Side by Side Diff: components/metrics/persisted_logs.cc

Issue 2588873002: Delegate PersistedLogs metrics recording (Closed)
Patch Set: Fix tests Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « components/metrics/persisted_logs.h ('k') | components/metrics/persisted_logs_metrics.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "components/metrics/persisted_logs.h" 5 #include "components/metrics/persisted_logs.h"
6 6
7 #include <memory> 7 #include <memory>
8 #include <string> 8 #include <string>
9 #include <utility> 9 #include <utility>
10 10
11 #include "base/base64.h" 11 #include "base/base64.h"
12 #include "base/md5.h" 12 #include "base/md5.h"
13 #include "base/metrics/histogram_macros.h" 13 #include "base/metrics/histogram_macros.h"
14 #include "base/sha1.h" 14 #include "base/sha1.h"
15 #include "base/strings/string_number_conversions.h" 15 #include "base/strings/string_number_conversions.h"
16 #include "base/timer/elapsed_timer.h" 16 #include "base/timer/elapsed_timer.h"
17 #include "components/metrics/persisted_logs_metrics.h"
17 #include "components/prefs/pref_service.h" 18 #include "components/prefs/pref_service.h"
18 #include "components/prefs/scoped_user_pref_update.h" 19 #include "components/prefs/scoped_user_pref_update.h"
19 #include "third_party/zlib/google/compression_utils.h" 20 #include "third_party/zlib/google/compression_utils.h"
20 21
21 namespace metrics { 22 namespace metrics {
22 23
23 namespace { 24 namespace {
24 25
25 const char kLogHashKey[] = "hash"; 26 const char kLogHashKey[] = "hash";
26 const char kLogTimestampKey[] = "timestamp"; 27 const char kLogTimestampKey[] = "timestamp";
27 const char kLogDataKey[] = "data"; 28 const char kLogDataKey[] = "data";
28 29
29 PersistedLogs::LogReadStatus MakeRecallStatusHistogram(
30 PersistedLogs::LogReadStatus status) {
31 UMA_HISTOGRAM_ENUMERATION("PrefService.PersistentLogRecallProtobufs",
32 status, PersistedLogs::END_RECALL_STATUS);
33 return status;
34 }
35
36 // Reads the value at |index| from |list_value| as a string and Base64-decodes 30 // Reads the value at |index| from |list_value| as a string and Base64-decodes
37 // it into |result|. Returns true on success. 31 // it into |result|. Returns true on success.
38 bool ReadBase64String(const base::ListValue& list_value, 32 bool ReadBase64String(const base::ListValue& list_value,
39 size_t index, 33 size_t index,
40 std::string* result) { 34 std::string* result) {
41 std::string base64_result; 35 std::string base64_result;
42 if (!list_value.GetString(index, &base64_result)) 36 if (!list_value.GetString(index, &base64_result))
43 return false; 37 return false;
44 return base::Base64Decode(base64_result, result); 38 return base::Base64Decode(base64_result, result);
45 } 39 }
46 40
47 std::string EncodeToBase64(const std::string& to_convert) { 41 std::string EncodeToBase64(const std::string& to_convert) {
48 std::string base64_result; 42 std::string base64_result;
49 base::Base64Encode(to_convert, &base64_result); 43 base::Base64Encode(to_convert, &base64_result);
50 return base64_result; 44 return base64_result;
51 } 45 }
52 46
53 std::string DecodeFromBase64(const std::string& to_convert) { 47 std::string DecodeFromBase64(const std::string& to_convert) {
54 std::string result; 48 std::string result;
55 base::Base64Decode(to_convert, &result); 49 base::Base64Decode(to_convert, &result);
56 return result; 50 return result;
57 } 51 }
58 52
59 } // namespace 53 } // namespace
60 54
61 void PersistedLogs::LogInfo::Init(const std::string& log_data, 55 void PersistedLogs::LogInfo::Init(PersistedLogsMetrics* metrics,
56 const std::string& log_data,
62 const std::string& log_timestamp) { 57 const std::string& log_timestamp) {
63 DCHECK(!log_data.empty()); 58 DCHECK(!log_data.empty());
64 59
65 if (!compression::GzipCompress(log_data, &compressed_log_data)) { 60 if (!compression::GzipCompress(log_data, &compressed_log_data)) {
66 NOTREACHED(); 61 NOTREACHED();
67 return; 62 return;
68 } 63 }
69 64
70 UMA_HISTOGRAM_PERCENTAGE( 65 metrics->RecordCompressionRatio(compressed_log_data.size(), log_data.size());
71 "UMA.ProtoCompressionRatio",
72 static_cast<int>(100 * compressed_log_data.size() / log_data.size()));
73 66
74 hash = base::SHA1HashString(log_data); 67 hash = base::SHA1HashString(log_data);
75 timestamp = log_timestamp; 68 timestamp = log_timestamp;
76 } 69 }
77 70
78 PersistedLogs::PersistedLogs(PrefService* local_state, 71 PersistedLogs::PersistedLogs(std::unique_ptr<PersistedLogsMetrics> metrics,
72 PrefService* local_state,
79 const char* pref_name, 73 const char* pref_name,
80 const char* outdated_pref_name, 74 const char* outdated_pref_name,
81 size_t min_log_count, 75 size_t min_log_count,
82 size_t min_log_bytes, 76 size_t min_log_bytes,
83 size_t max_log_size) 77 size_t max_log_size)
84 : local_state_(local_state), 78 : metrics_(std::move(metrics)),
79 local_state_(local_state),
85 pref_name_(pref_name), 80 pref_name_(pref_name),
86 outdated_pref_name_(outdated_pref_name), 81 outdated_pref_name_(outdated_pref_name),
87 min_log_count_(min_log_count), 82 min_log_count_(min_log_count),
88 min_log_bytes_(min_log_bytes), 83 min_log_bytes_(min_log_bytes),
89 max_log_size_(max_log_size != 0 ? max_log_size : static_cast<size_t>(-1)), 84 max_log_size_(max_log_size != 0 ? max_log_size : static_cast<size_t>(-1)),
90 staged_log_index_(-1) { 85 staged_log_index_(-1) {
91 DCHECK(local_state_); 86 DCHECK(local_state_);
92 // One of the limit arguments must be non-zero. 87 // One of the limit arguments must be non-zero.
93 DCHECK(min_log_count_ > 0 || min_log_bytes_ > 0); 88 DCHECK(min_log_count_ > 0 || min_log_bytes_ > 0);
94 } 89 }
(...skipping 15 matching lines...) Expand all
110 // users are migrated. crbug.com/649440 105 // users are migrated. crbug.com/649440
111 if (local_state_->HasPrefPath(outdated_pref_name_)) { 106 if (local_state_->HasPrefPath(outdated_pref_name_)) {
112 return ReadLogsFromOldFormatPrefList( 107 return ReadLogsFromOldFormatPrefList(
113 *local_state_->GetList(outdated_pref_name_)); 108 *local_state_->GetList(outdated_pref_name_));
114 } 109 }
115 return ReadLogsFromPrefList(*local_state_->GetList(pref_name_)); 110 return ReadLogsFromPrefList(*local_state_->GetList(pref_name_));
116 } 111 }
117 112
118 void PersistedLogs::StoreLog(const std::string& log_data) { 113 void PersistedLogs::StoreLog(const std::string& log_data) {
119 list_.push_back(LogInfo()); 114 list_.push_back(LogInfo());
120 list_.back().Init(log_data, base::Int64ToString(base::Time::Now().ToTimeT())); 115 list_.back().Init(metrics_.get(),
116 log_data,
117 base::Int64ToString(base::Time::Now().ToTimeT()));
121 } 118 }
122 119
123 void PersistedLogs::StageLog() { 120 void PersistedLogs::StageLog() {
124 // CHECK, rather than DCHECK, because swap()ing with an empty list causes 121 // CHECK, rather than DCHECK, because swap()ing with an empty list causes
125 // hard-to-identify crashes much later. 122 // hard-to-identify crashes much later.
126 CHECK(!list_.empty()); 123 CHECK(!list_.empty());
127 DCHECK(!has_staged_log()); 124 DCHECK(!has_staged_log());
128 staged_log_index_ = list_.size() - 1; 125 staged_log_index_ = list_.size() - 1;
129 DCHECK(has_staged_log()); 126 DCHECK(has_staged_log());
130 } 127 }
131 128
132 void PersistedLogs::DiscardStagedLog() { 129 void PersistedLogs::DiscardStagedLog() {
133 DCHECK(has_staged_log()); 130 DCHECK(has_staged_log());
134 DCHECK_LT(static_cast<size_t>(staged_log_index_), list_.size()); 131 DCHECK_LT(static_cast<size_t>(staged_log_index_), list_.size());
135 list_.erase(list_.begin() + staged_log_index_); 132 list_.erase(list_.begin() + staged_log_index_);
136 staged_log_index_ = -1; 133 staged_log_index_ = -1;
137 } 134 }
138 135
139 PersistedLogs::LogReadStatus PersistedLogs::ReadLogsFromPrefList( 136 PersistedLogs::LogReadStatus PersistedLogs::ReadLogsFromPrefList(
140 const base::ListValue& list_value) { 137 const base::ListValue& list_value) {
141 if (list_value.empty()) 138 if (list_value.empty())
142 return MakeRecallStatusHistogram(LIST_EMPTY); 139 return metrics_->RecordLogReadStatus(LIST_EMPTY);
143 140
144 const size_t log_count = list_value.GetSize(); 141 const size_t log_count = list_value.GetSize();
145 142
146 DCHECK(list_.empty()); 143 DCHECK(list_.empty());
147 list_.resize(log_count); 144 list_.resize(log_count);
148 145
149 for (size_t i = 0; i < log_count; ++i) { 146 for (size_t i = 0; i < log_count; ++i) {
150 const base::DictionaryValue* dict; 147 const base::DictionaryValue* dict;
151 if (!list_value.GetDictionary(i, &dict) || 148 if (!list_value.GetDictionary(i, &dict) ||
152 !dict->GetString(kLogDataKey, &list_[i].compressed_log_data) || 149 !dict->GetString(kLogDataKey, &list_[i].compressed_log_data) ||
153 !dict->GetString(kLogHashKey, &list_[i].hash)) { 150 !dict->GetString(kLogHashKey, &list_[i].hash)) {
154 list_.clear(); 151 list_.clear();
155 return MakeRecallStatusHistogram(LOG_STRING_CORRUPTION); 152 return metrics_->RecordLogReadStatus(LOG_STRING_CORRUPTION);
156 } 153 }
157 154
158 list_[i].compressed_log_data = 155 list_[i].compressed_log_data =
159 DecodeFromBase64(list_[i].compressed_log_data); 156 DecodeFromBase64(list_[i].compressed_log_data);
160 list_[i].hash = DecodeFromBase64(list_[i].hash); 157 list_[i].hash = DecodeFromBase64(list_[i].hash);
161 // Ignoring the success of this step as timestamp might not be there for 158 // Ignoring the success of this step as timestamp might not be there for
162 // older logs. 159 // older logs.
163 // NOTE: Should be added to the check with other fields once migration is 160 // NOTE: Should be added to the check with other fields once migration is
164 // over. 161 // over.
165 dict->GetString(kLogTimestampKey, &list_[i].timestamp); 162 dict->GetString(kLogTimestampKey, &list_[i].timestamp);
166 } 163 }
167 164
168 return MakeRecallStatusHistogram(RECALL_SUCCESS); 165 return metrics_->RecordLogReadStatus(RECALL_SUCCESS);
169 } 166 }
170 167
171 void PersistedLogs::WriteLogsToPrefList(base::ListValue* list_value) const { 168 void PersistedLogs::WriteLogsToPrefList(base::ListValue* list_value) const {
172 list_value->Clear(); 169 list_value->Clear();
173 170
174 // Keep the most recent logs which are smaller than |max_log_size_|. 171 // Keep the most recent logs which are smaller than |max_log_size_|.
175 // We keep at least |min_log_bytes_| and |min_log_count_| of logs before 172 // We keep at least |min_log_bytes_| and |min_log_count_| of logs before
176 // discarding older logs. 173 // discarding older logs.
177 size_t start = list_.size(); 174 size_t start = list_.size();
178 size_t saved_log_count = 0; 175 size_t saved_log_count = 0;
179 size_t bytes_used = 0; 176 size_t bytes_used = 0;
180 for (; start > 0; --start) { 177 for (; start > 0; --start) {
181 size_t log_size = list_[start - 1].compressed_log_data.length(); 178 size_t log_size = list_[start - 1].compressed_log_data.length();
182 if (bytes_used >= min_log_bytes_ && 179 if (bytes_used >= min_log_bytes_ &&
183 saved_log_count >= min_log_count_) { 180 saved_log_count >= min_log_count_) {
184 break; 181 break;
185 } 182 }
186 // Oversized logs won't be persisted, so don't count them. 183 // Oversized logs won't be persisted, so don't count them.
187 if (log_size > max_log_size_) 184 if (log_size > max_log_size_)
188 continue; 185 continue;
189 bytes_used += log_size; 186 bytes_used += log_size;
190 ++saved_log_count; 187 ++saved_log_count;
191 } 188 }
192 int dropped_logs_num = start - 1; 189 int dropped_logs_num = start - 1;
193 190
194 for (size_t i = start; i < list_.size(); ++i) { 191 for (size_t i = start; i < list_.size(); ++i) {
195 size_t log_size = list_[i].compressed_log_data.length(); 192 size_t log_size = list_[i].compressed_log_data.length();
196 if (log_size > max_log_size_) { 193 if (log_size > max_log_size_) {
197 UMA_HISTOGRAM_COUNTS("UMA.Large Accumulated Log Not Persisted", 194 metrics_->RecordDroppedLogSize(log_size);
198 static_cast<int>(log_size));
199 dropped_logs_num++; 195 dropped_logs_num++;
200 continue; 196 continue;
201 } 197 }
202 std::unique_ptr<base::DictionaryValue> dict_value( 198 std::unique_ptr<base::DictionaryValue> dict_value(
203 new base::DictionaryValue); 199 new base::DictionaryValue);
204 dict_value->SetString(kLogHashKey, EncodeToBase64(list_[i].hash)); 200 dict_value->SetString(kLogHashKey, EncodeToBase64(list_[i].hash));
205 dict_value->SetString(kLogDataKey, 201 dict_value->SetString(kLogDataKey,
206 EncodeToBase64(list_[i].compressed_log_data)); 202 EncodeToBase64(list_[i].compressed_log_data));
207 dict_value->SetString(kLogTimestampKey, list_[i].timestamp); 203 dict_value->SetString(kLogTimestampKey, list_[i].timestamp);
208 list_value->Append(std::move(dict_value)); 204 list_value->Append(std::move(dict_value));
209 } 205 }
210 if (dropped_logs_num > 0) 206 if (dropped_logs_num > 0)
211 UMA_HISTOGRAM_COUNTS("UMA.UnsentLogs.Dropped", dropped_logs_num); 207 metrics_->RecordDroppedLogsNum(dropped_logs_num);
212 } 208 }
213 209
214 PersistedLogs::LogReadStatus PersistedLogs::ReadLogsFromOldFormatPrefList( 210 PersistedLogs::LogReadStatus PersistedLogs::ReadLogsFromOldFormatPrefList(
215 const base::ListValue& list_value) { 211 const base::ListValue& list_value) {
216 if (list_value.empty()) 212 if (list_value.empty())
217 return MakeRecallStatusHistogram(LIST_EMPTY); 213 return metrics_->RecordLogReadStatus(LIST_EMPTY);
218 214
219 // For each log, there's two entries in the list (the data and the hash). 215 // For each log, there's two entries in the list (the data and the hash).
220 DCHECK_EQ(0U, list_value.GetSize() % 2); 216 DCHECK_EQ(0U, list_value.GetSize() % 2);
221 const size_t log_count = list_value.GetSize() / 2; 217 const size_t log_count = list_value.GetSize() / 2;
222 218
223 // Resize |list_| ahead of time, so that values can be decoded directly into 219 // Resize |list_| ahead of time, so that values can be decoded directly into
224 // the elements of the list. 220 // the elements of the list.
225 DCHECK(list_.empty()); 221 DCHECK(list_.empty());
226 list_.resize(log_count); 222 list_.resize(log_count);
227 223
228 for (size_t i = 0; i < log_count; ++i) { 224 for (size_t i = 0; i < log_count; ++i) {
229 if (!ReadBase64String(list_value, i * 2, &list_[i].compressed_log_data) || 225 if (!ReadBase64String(list_value, i * 2, &list_[i].compressed_log_data) ||
230 !ReadBase64String(list_value, i * 2 + 1, &list_[i].hash)) { 226 !ReadBase64String(list_value, i * 2 + 1, &list_[i].hash)) {
231 list_.clear(); 227 list_.clear();
232 return MakeRecallStatusHistogram(LOG_STRING_CORRUPTION); 228 return metrics_->RecordLogReadStatus(LOG_STRING_CORRUPTION);
233 } 229 }
234 } 230 }
235 231
236 return MakeRecallStatusHistogram(RECALL_SUCCESS); 232 return metrics_->RecordLogReadStatus(RECALL_SUCCESS);
237 } 233 }
238 234
239 } // namespace metrics 235 } // namespace metrics
OLDNEW
« no previous file with comments | « components/metrics/persisted_logs.h ('k') | components/metrics/persisted_logs_metrics.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698