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
3 // found in the LICENSE file.
5 #include "chromecast/base/metrics/cast_metrics_helper.h"
8 #include "base/bind_helpers.h"
9 #include "base/location.h"
10 #include "base/metrics/histogram.h"
11 #include "base/metrics/user_metrics.h"
12 #include "base/single_thread_task_runner.h"
13 #include "base/strings/string_split.h"
14 #include "base/strings/string_util.h"
15 #include "chromecast/base/metrics/cast_histograms.h"
16 #include "chromecast/base/metrics/grouped_histogram.h"
18 namespace chromecast
{
21 // A useful macro to make sure current member function runs on the valid thread.
22 #define MAKE_SURE_THREAD(callback, ...) \
23 if (!task_runner_->BelongsToCurrentThread()) { \
24 task_runner_->PostTask(FROM_HERE, \
25 base::Bind(&CastMetricsHelper::callback, \
26 base::Unretained(this), ##__VA_ARGS__)); \
32 CastMetricsHelper
* g_instance
= NULL
;
34 const char kMetricsNameAppInfoDelimiter
= '#';
40 // NOTE(gfhuang): This is a hacky way to encode/decode app infos into a
41 // string. Mainly because it's hard to add another metrics serialization type
42 // into components/metrics/serialization/.
44 bool CastMetricsHelper::DecodeAppInfoFromMetricsName(
45 const std::string
& metrics_name
,
46 std::string
* action_name
,
48 std::string
* session_id
,
49 std::string
* sdk_version
) {
54 if (metrics_name
.find(kMetricsNameAppInfoDelimiter
) == std::string::npos
)
57 std::vector
<std::string
> tokens
= base::SplitString(
58 metrics_name
, std::string(1, kMetricsNameAppInfoDelimiter
),
59 base::TRIM_WHITESPACE
, base::SPLIT_WANT_ALL
);
60 DCHECK_EQ(tokens
.size(), 4u);
61 // The order of tokens should match EncodeAppInfoIntoMetricsName().
62 *action_name
= tokens
[0];
64 *session_id
= tokens
[2];
65 *sdk_version
= tokens
[3];
70 std::string
CastMetricsHelper::EncodeAppInfoIntoMetricsName(
71 const std::string
& action_name
,
72 const std::string
& app_id
,
73 const std::string
& session_id
,
74 const std::string
& sdk_version
) {
75 std::string
result(action_name
);
76 result
.push_back(kMetricsNameAppInfoDelimiter
);
77 result
.append(app_id
);
78 result
.push_back(kMetricsNameAppInfoDelimiter
);
79 result
.append(session_id
);
80 result
.push_back(kMetricsNameAppInfoDelimiter
);
81 result
.append(sdk_version
);
86 CastMetricsHelper
* CastMetricsHelper::GetInstance() {
91 CastMetricsHelper::CastMetricsHelper(
92 scoped_refptr
<base::SingleThreadTaskRunner
> task_runner
)
93 : task_runner_(task_runner
),
95 record_action_callback_(base::Bind(&base::RecordComputedAction
)) {
96 DCHECK(task_runner_
.get());
101 CastMetricsHelper::CastMetricsHelper()
102 : metrics_sink_(NULL
) {
107 CastMetricsHelper::~CastMetricsHelper() {
108 DCHECK_EQ(g_instance
, this);
112 void CastMetricsHelper::UpdateCurrentAppInfo(const std::string
& app_id
,
113 const std::string
& session_id
) {
114 MAKE_SURE_THREAD(UpdateCurrentAppInfo
, app_id
, session_id
);
116 session_id_
= session_id
;
117 app_start_time_
= base::TimeTicks::Now();
118 TagAppStartForGroupedHistograms(app_id_
);
119 sdk_version_
.clear();
122 void CastMetricsHelper::UpdateSDKInfo(const std::string
& sdk_version
) {
123 MAKE_SURE_THREAD(UpdateSDKInfo
, sdk_version
);
124 sdk_version_
= sdk_version
;
127 void CastMetricsHelper::LogMediaPlay() {
128 MAKE_SURE_THREAD(LogMediaPlay
);
129 RecordSimpleAction(EncodeAppInfoIntoMetricsName(
136 void CastMetricsHelper::LogMediaPause() {
137 MAKE_SURE_THREAD(LogMediaPause
);
138 RecordSimpleAction(EncodeAppInfoIntoMetricsName(
145 void CastMetricsHelper::LogTimeToFirstPaint() {
146 MAKE_SURE_THREAD(LogTimeToFirstPaint
);
149 base::TimeDelta launch_time
= base::TimeTicks::Now() - app_start_time_
;
150 const std::string
uma_name(GetMetricsNameWithAppName("Startup",
151 "TimeToFirstPaint"));
152 LogMediumTimeHistogramEvent(uma_name
, launch_time
);
153 LOG(INFO
) << uma_name
<< " is " << launch_time
.InSecondsF() << " seconds.";
156 void CastMetricsHelper::LogTimeToBufferAv(BufferingType buffering_type
,
157 base::TimeDelta time
) {
158 MAKE_SURE_THREAD(LogTimeToBufferAv
, buffering_type
, time
);
159 if (time
< base::TimeDelta::FromSeconds(0)) {
160 LOG(WARNING
) << "Negative time";
164 const std::string
uma_name(GetMetricsNameWithAppName(
166 (buffering_type
== kInitialBuffering
? "TimeToBufferAv" :
167 buffering_type
== kBufferingAfterUnderrun
?
168 "TimeToBufferAvAfterUnderrun" :
169 buffering_type
== kAbortedBuffering
? "TimeToBufferAvAfterAbort" : "")));
171 // Histogram from 250ms to 30s with 50 buckets.
172 // The ratio between 2 consecutive buckets is:
173 // exp( (ln(30000) - ln(250)) / 50 ) = 1.1
174 LogTimeHistogramEvent(
177 base::TimeDelta::FromMilliseconds(250),
178 base::TimeDelta::FromMilliseconds(30000),
182 std::string
CastMetricsHelper::GetMetricsNameWithAppName(
183 const std::string
& prefix
,
184 const std::string
& suffix
) const {
185 DCHECK(task_runner_
->BelongsToCurrentThread());
186 std::string
metrics_name(prefix
);
187 if (!app_id_
.empty()) {
188 if (!metrics_name
.empty())
189 metrics_name
.push_back('.');
190 metrics_name
.append(app_id_
);
192 if (!suffix
.empty()) {
193 if (!metrics_name
.empty())
194 metrics_name
.push_back('.');
195 metrics_name
.append(suffix
);
200 void CastMetricsHelper::SetMetricsSink(MetricsSink
* delegate
) {
201 MAKE_SURE_THREAD(SetMetricsSink
, delegate
);
202 metrics_sink_
= delegate
;
205 void CastMetricsHelper::SetRecordActionCallback(
206 const RecordActionCallback
& callback
) {
207 DCHECK(task_runner_
->BelongsToCurrentThread());
208 record_action_callback_
= callback
;
211 void CastMetricsHelper::RecordSimpleAction(const std::string
& action
) {
212 MAKE_SURE_THREAD(RecordSimpleAction
, action
);
215 metrics_sink_
->OnAction(action
);
217 record_action_callback_
.Run(action
);
221 void CastMetricsHelper::LogEnumerationHistogramEvent(
222 const std::string
& name
, int value
, int num_buckets
) {
223 MAKE_SURE_THREAD(LogEnumerationHistogramEvent
, name
, value
, num_buckets
);
226 metrics_sink_
->OnEnumerationEvent(name
, value
, num_buckets
);
228 UMA_HISTOGRAM_ENUMERATION_NO_CACHE(name
, value
, num_buckets
);
232 void CastMetricsHelper::LogTimeHistogramEvent(const std::string
& name
,
233 const base::TimeDelta
& value
,
234 const base::TimeDelta
& min
,
235 const base::TimeDelta
& max
,
237 MAKE_SURE_THREAD(LogTimeHistogramEvent
, name
, value
, min
, max
, num_buckets
);
240 metrics_sink_
->OnTimeEvent(name
, value
, min
, max
, num_buckets
);
242 UMA_HISTOGRAM_CUSTOM_TIMES_NO_CACHE(name
, value
, min
, max
, num_buckets
);
246 void CastMetricsHelper::LogMediumTimeHistogramEvent(
247 const std::string
& name
,
248 const base::TimeDelta
& value
) {
249 // Follow UMA_HISTOGRAM_MEDIUM_TIMES definition.
250 LogTimeHistogramEvent(name
, value
,
251 base::TimeDelta::FromMilliseconds(10),
252 base::TimeDelta::FromMinutes(3),
256 } // namespace metrics
257 } // namespace chromecast