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
;
58 base::SplitString(metrics_name
, kMetricsNameAppInfoDelimiter
, &tokens
);
59 DCHECK_EQ(tokens
.size(), 4u);
60 // The order of tokens should match EncodeAppInfoIntoMetricsName().
61 *action_name
= tokens
[0];
63 *session_id
= tokens
[2];
64 *sdk_version
= tokens
[3];
69 std::string
CastMetricsHelper::EncodeAppInfoIntoMetricsName(
70 const std::string
& action_name
,
71 const std::string
& app_id
,
72 const std::string
& session_id
,
73 const std::string
& sdk_version
) {
74 std::string
result(action_name
);
75 result
.push_back(kMetricsNameAppInfoDelimiter
);
76 result
.append(app_id
);
77 result
.push_back(kMetricsNameAppInfoDelimiter
);
78 result
.append(session_id
);
79 result
.push_back(kMetricsNameAppInfoDelimiter
);
80 result
.append(sdk_version
);
85 CastMetricsHelper
* CastMetricsHelper::GetInstance() {
90 CastMetricsHelper::CastMetricsHelper(
91 scoped_refptr
<base::SingleThreadTaskRunner
> task_runner
)
92 : task_runner_(task_runner
),
94 record_action_callback_(base::Bind(&base::RecordComputedAction
)) {
95 DCHECK(task_runner_
.get());
100 CastMetricsHelper::CastMetricsHelper()
101 : metrics_sink_(NULL
) {
106 CastMetricsHelper::~CastMetricsHelper() {
107 DCHECK_EQ(g_instance
, this);
111 void CastMetricsHelper::UpdateCurrentAppInfo(const std::string
& app_id
,
112 const std::string
& session_id
) {
113 MAKE_SURE_THREAD(UpdateCurrentAppInfo
, app_id
, session_id
);
115 session_id_
= session_id
;
116 app_start_time_
= base::TimeTicks::Now();
117 TagAppStartForGroupedHistograms(app_id_
);
118 sdk_version_
.clear();
121 void CastMetricsHelper::UpdateSDKInfo(const std::string
& sdk_version
) {
122 MAKE_SURE_THREAD(UpdateSDKInfo
, sdk_version
);
123 sdk_version_
= sdk_version
;
126 void CastMetricsHelper::LogMediaPlay() {
127 MAKE_SURE_THREAD(LogMediaPlay
);
128 RecordSimpleAction(EncodeAppInfoIntoMetricsName(
135 void CastMetricsHelper::LogMediaPause() {
136 MAKE_SURE_THREAD(LogMediaPause
);
137 RecordSimpleAction(EncodeAppInfoIntoMetricsName(
144 void CastMetricsHelper::LogTimeToFirstPaint() {
145 MAKE_SURE_THREAD(LogTimeToFirstPaint
);
148 base::TimeDelta launch_time
= base::TimeTicks::Now() - app_start_time_
;
149 const std::string
uma_name(GetMetricsNameWithAppName("Startup",
150 "TimeToFirstPaint"));
151 LogMediumTimeHistogramEvent(uma_name
, launch_time
);
152 LOG(INFO
) << uma_name
<< " is " << launch_time
.InSecondsF() << " seconds.";
155 void CastMetricsHelper::LogTimeToBufferAv(BufferingType buffering_type
,
156 base::TimeDelta time
) {
157 MAKE_SURE_THREAD(LogTimeToBufferAv
, buffering_type
, time
);
158 if (time
< base::TimeDelta::FromSeconds(0)) {
159 LOG(WARNING
) << "Negative time";
163 const std::string
uma_name(GetMetricsNameWithAppName(
165 (buffering_type
== kInitialBuffering
? "TimeToBufferAv" :
166 buffering_type
== kBufferingAfterUnderrun
?
167 "TimeToBufferAvAfterUnderrun" :
168 buffering_type
== kAbortedBuffering
? "TimeToBufferAvAfterAbort" : "")));
170 // Histogram from 250ms to 30s with 50 buckets.
171 // The ratio between 2 consecutive buckets is:
172 // exp( (ln(30000) - ln(250)) / 50 ) = 1.1
173 LogTimeHistogramEvent(
176 base::TimeDelta::FromMilliseconds(250),
177 base::TimeDelta::FromMilliseconds(30000),
181 std::string
CastMetricsHelper::GetMetricsNameWithAppName(
182 const std::string
& prefix
,
183 const std::string
& suffix
) const {
184 DCHECK(task_runner_
->BelongsToCurrentThread());
185 std::string
metrics_name(prefix
);
186 if (!app_id_
.empty()) {
187 if (!metrics_name
.empty())
188 metrics_name
.push_back('.');
189 metrics_name
.append(app_id_
);
191 if (!suffix
.empty()) {
192 if (!metrics_name
.empty())
193 metrics_name
.push_back('.');
194 metrics_name
.append(suffix
);
199 void CastMetricsHelper::SetMetricsSink(MetricsSink
* delegate
) {
200 MAKE_SURE_THREAD(SetMetricsSink
, delegate
);
201 metrics_sink_
= delegate
;
204 void CastMetricsHelper::SetRecordActionCallback(
205 const RecordActionCallback
& callback
) {
206 DCHECK(task_runner_
->BelongsToCurrentThread());
207 record_action_callback_
= callback
;
210 void CastMetricsHelper::RecordSimpleAction(const std::string
& action
) {
211 MAKE_SURE_THREAD(RecordSimpleAction
, action
);
214 metrics_sink_
->OnAction(action
);
216 record_action_callback_
.Run(action
);
220 void CastMetricsHelper::LogEnumerationHistogramEvent(
221 const std::string
& name
, int value
, int num_buckets
) {
222 MAKE_SURE_THREAD(LogEnumerationHistogramEvent
, name
, value
, num_buckets
);
225 metrics_sink_
->OnEnumerationEvent(name
, value
, num_buckets
);
227 UMA_HISTOGRAM_ENUMERATION_NO_CACHE(name
, value
, num_buckets
);
231 void CastMetricsHelper::LogTimeHistogramEvent(const std::string
& name
,
232 const base::TimeDelta
& value
,
233 const base::TimeDelta
& min
,
234 const base::TimeDelta
& max
,
236 MAKE_SURE_THREAD(LogTimeHistogramEvent
, name
, value
, min
, max
, num_buckets
);
239 metrics_sink_
->OnTimeEvent(name
, value
, min
, max
, num_buckets
);
241 UMA_HISTOGRAM_CUSTOM_TIMES_NO_CACHE(name
, value
, min
, max
, num_buckets
);
245 void CastMetricsHelper::LogMediumTimeHistogramEvent(
246 const std::string
& name
,
247 const base::TimeDelta
& value
) {
248 // Follow UMA_HISTOGRAM_MEDIUM_TIMES definition.
249 LogTimeHistogramEvent(name
, value
,
250 base::TimeDelta::FromMilliseconds(10),
251 base::TimeDelta::FromMinutes(3),
255 } // namespace metrics
256 } // namespace chromecast