blob: b91324688a283e139b1ea62faa6d5fec9e5dd603 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "remoting/base/telemetry_log_writer.h"
#include <utility>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/json/json_string_value_serializer.h"
#include "base/logging.h"
#include "net/http/http_status_code.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "remoting/base/protobuf_http_client.h"
#include "remoting/base/protobuf_http_request.h"
#include "remoting/base/protobuf_http_request_config.h"
#include "remoting/base/service_urls.h"
#include "remoting/proto/remoting/v1/telemetry_messages.pb.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
namespace remoting {
namespace {
const net::BackoffEntry::Policy kBackoffPolicy = {
// Number of initial errors (in sequence) to ignore before applying
// exponential back-off rules.
0,
// Initial delay for exponential back-off in ms.
1000,
// Factor by which the waiting time will be multiplied.
2,
// Fuzzing percentage. ex: 10% will spread requests randomly
// between 90%-100% of the calculated time.
0.5,
// Maximum amount of time we are willing to delay our request in ms.
60000,
// Time to keep an entry from being discarded even when it
// has no significant state, -1 to never discard.
-1,
// Starts with initial delay.
false,
};
constexpr net::NetworkTrafficAnnotationTag kTrafficAnnotation =
net::DefineNetworkTrafficAnnotation("remoting_telemetry_log_writer",
R"(
semantics {
sender: "Chrome Remote Desktop"
description:
"Sends telemetry logs for Chrome Remote Desktop."
trigger:
"These requests are sent periodically by Chrome Remote Desktop "
"(CRD) Android and iOS clients when a session is connected, i.e. "
"CRD app is running and is connected to a host."
data:
"Anonymous usage statistics, which includes CRD host version, "
"connection time, authentication type, connection error, and "
"round trip latency."
destination: GOOGLE_OWNED_SERVICE
}
policy {
cookies_allowed: NO
setting:
"This request cannot be stopped in settings, but will not be sent "
"if the user does not use Chrome Remote Desktop."
policy_exception_justification:
"Not implemented."
})");
constexpr char kCreateEventPath[] = "/v1/telemetry:createevent";
}
const int kMaxSendAttempts = 5;
TelemetryLogWriter::TelemetryLogWriter(
std::unique_ptr<OAuthTokenGetter> token_getter)
: token_getter_(std::move(token_getter)), backoff_(&kBackoffPolicy) {
DETACH_FROM_THREAD(thread_checker_);
DCHECK(token_getter_);
}
TelemetryLogWriter::~TelemetryLogWriter() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
}
void TelemetryLogWriter::Init(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(!http_client_);
http_client_ = std::make_unique<ProtobufHttpClient>(
ServiceUrls::GetInstance()->remoting_server_endpoint(),
token_getter_.get(), url_loader_factory);
}
void TelemetryLogWriter::Log(const ChromotingEvent& entry) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
pending_entries_.push_back(entry);
SendPendingEntries();
}
void TelemetryLogWriter::SendPendingEntries() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (!sending_entries_.empty() || pending_entries_.empty()) {
return;
}
apis::v1::CreateEventRequest request;
while (!pending_entries_.empty()) {
ChromotingEvent& entry = pending_entries_.front();
DCHECK(entry.IsDataValid());
*request.mutable_payload()->mutable_events()->Add() = entry.CreateProto();
entry.IncrementSendAttempts();
sending_entries_.push_back(std::move(entry));
pending_entries_.pop_front();
}
base::TimeDelta time_until_release = backoff_.GetTimeUntilRelease();
backoff_timer_.Start(
FROM_HERE, time_until_release,
base::BindOnce(&TelemetryLogWriter::DoSend, base::Unretained(this),
std::move(request)));
}
void TelemetryLogWriter::DoSend(const apis::v1::CreateEventRequest& request) {
DCHECK(http_client_);
auto request_config =
std::make_unique<ProtobufHttpRequestConfig>(kTrafficAnnotation);
request_config->path = kCreateEventPath;
request_config->request_message =
std::make_unique<apis::v1::CreateEventRequest>(request);
auto http_request =
std::make_unique<ProtobufHttpRequest>(std::move(request_config));
http_request->SetResponseCallback(base::BindOnce(
&TelemetryLogWriter::OnSendLogResult, base::Unretained(this)));
http_client_->ExecuteRequest(std::move(http_request));
}
void TelemetryLogWriter::OnSendLogResult(
const ProtobufHttpStatus& status,
std::unique_ptr<apis::v1::CreateEventResponse> response) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (!status.ok()) {
backoff_.InformOfRequest(false);
LOG(WARNING) << "Error occur when sending logs to the telemetry server, "
<< "status: " << status.error_message();
// Reverse iterating + push_front in order to restore the order of logs.
for (auto i = sending_entries_.rbegin(); i < sending_entries_.rend(); i++) {
if (i->send_attempts() >= kMaxSendAttempts) {
break;
}
pending_entries_.push_front(std::move(*i));
}
} else {
backoff_.InformOfRequest(true);
VLOG(1) << "Successfully sent " << sending_entries_.size()
<< " log(s) to telemetry server.";
}
sending_entries_.clear();
SendPendingEntries();
}
bool TelemetryLogWriter::IsIdleForTesting() {
return sending_entries_.empty() && pending_entries_.empty();
}
} // namespace remoting