Pull out common code from client and host versions of ServerLogEntry.

This also adds unittests for ServerLogEntry client code, with common code
pulled out to a separate file.

This doesn't affect LogToServer classes - they will be refactored in a
separate CL. Also fix some IWYU issues uncovered by the refactor.

TEST=unittests

Review URL: https://codereview.chromium.org/282063005

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@272346 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/remoting/client/jni/chromoting_jni_instance.cc b/remoting/client/jni/chromoting_jni_instance.cc
index 3f9bd22f..a2add665 100644
--- a/remoting/client/jni/chromoting_jni_instance.cc
+++ b/remoting/client/jni/chromoting_jni_instance.cc
@@ -13,11 +13,11 @@
 #include "remoting/client/jni/android_keymap.h"
 #include "remoting/client/jni/chromoting_jni_runtime.h"
 #include "remoting/client/log_to_server.h"
-#include "remoting/client/server_log_entry.h"
 #include "remoting/client/software_video_renderer.h"
 #include "remoting/jingle_glue/chromium_port_allocator.h"
 #include "remoting/jingle_glue/chromium_socket_factory.h"
 #include "remoting/jingle_glue/network_settings.h"
+#include "remoting/jingle_glue/server_log_entry.h"
 #include "remoting/protocol/host_stub.h"
 #include "remoting/protocol/libjingle_transport_factory.h"
 
@@ -336,7 +336,7 @@
       net::ClientSocketFactory::GetDefaultFactory(),
       jni_runtime_->url_requester(), xmpp_config_));
 
-  log_to_server_.reset(new client::LogToServer(client::ServerLogEntry::ME2ME,
+  log_to_server_.reset(new client::LogToServer(ServerLogEntry::ME2ME,
                                                signaling_.get(),
                                                "[email protected]"));
 
diff --git a/remoting/client/log_to_server.cc b/remoting/client/log_to_server.cc
index c45ad386..e29de135 100644
--- a/remoting/client/log_to_server.cc
+++ b/remoting/client/log_to_server.cc
@@ -8,6 +8,7 @@
 #include "base/rand_util.h"
 #include "remoting/base/constants.h"
 #include "remoting/client/chromoting_stats.h"
+#include "remoting/client/server_log_entry_client.h"
 #include "remoting/jingle_glue/iq_sender.h"
 #include "remoting/jingle_glue/signal_strategy.h"
 #include "third_party/libjingle/source/talk/xmllite/xmlelement.h"
@@ -69,8 +70,8 @@
   DCHECK(CalledOnValidThread());
 
   scoped_ptr<ServerLogEntry> entry(
-      ServerLogEntry::MakeForSessionStateChange(state, error));
-  entry->AddClientFields();
+      MakeLogEntryForSessionStateChange(state, error));
+  AddClientFieldsToLogEntry(entry.get());
   entry->AddModeField(mode_);
 
   MaybeExpireSessionId();
@@ -85,12 +86,13 @@
   }
 
   if (!session_id_.empty()) {
-    entry->AddSessionId(session_id_);
+    AddSessionIdToLogEntry(entry.get(), session_id_);
   }
 
   // Maybe clear the session start time and log the session duration.
   if (ShouldAddDuration(state) && !session_start_time_.is_null()) {
-    entry->AddSessionDuration(base::TimeTicks::Now() - session_start_time_);
+    AddSessionDurationToLogEntry(entry.get(),
+                                 base::TimeTicks::Now() - session_start_time_);
   }
 
   if (IsEndOfSession(state)) {
@@ -106,11 +108,10 @@
 
   MaybeExpireSessionId();
 
-  scoped_ptr<ServerLogEntry> entry(
-      ServerLogEntry::MakeForStatistics(statistics));
-  entry->AddClientFields();
+  scoped_ptr<ServerLogEntry> entry(MakeLogEntryForStatistics(statistics));
+  AddClientFieldsToLogEntry(entry.get());
   entry->AddModeField(mode_);
-  entry->AddSessionId(session_id_);
+  AddSessionIdToLogEntry(entry.get(), session_id_);
   Log(*entry.get());
 }
 
@@ -174,8 +175,7 @@
   base::TimeDelta max_age = base::TimeDelta::FromDays(kMaxSessionIdAgeDays);
   if (base::TimeTicks::Now() - session_id_generation_time_ > max_age) {
     // Log the old session ID.
-    scoped_ptr<ServerLogEntry> entry(
-        ServerLogEntry::MakeForSessionIdOld(session_id_));
+    scoped_ptr<ServerLogEntry> entry(MakeLogEntryForSessionIdOld(session_id_));
     entry->AddModeField(mode_);
     Log(*entry.get());
 
@@ -183,7 +183,7 @@
     GenerateSessionId();
 
     // Log the new session ID.
-    entry = ServerLogEntry::MakeForSessionIdNew(session_id_);
+    entry = MakeLogEntryForSessionIdNew(session_id_);
     entry->AddModeField(mode_);
     Log(*entry.get());
   }
diff --git a/remoting/client/log_to_server.h b/remoting/client/log_to_server.h
index 35f3c96..cd70ff5 100644
--- a/remoting/client/log_to_server.h
+++ b/remoting/client/log_to_server.h
@@ -11,7 +11,7 @@
 
 #include "base/threading/non_thread_safe.h"
 #include "base/time/time.h"
-#include "remoting/client/server_log_entry.h"
+#include "remoting/jingle_glue/server_log_entry.h"
 #include "remoting/jingle_glue/signal_strategy.h"
 #include "remoting/protocol/connection_to_host.h"
 #include "remoting/protocol/errors.h"
diff --git a/remoting/client/server_log_entry.cc b/remoting/client/server_log_entry.cc
deleted file mode 100644
index cd580d8..0000000
--- a/remoting/client/server_log_entry.cc
+++ /dev/null
@@ -1,238 +0,0 @@
-// Copyright 2014 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/client/server_log_entry.h"
-
-#include "base/logging.h"
-#include "base/macros.h"
-#include "base/rand_util.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/stringize_macros.h"
-#include "base/strings/stringprintf.h"
-#include "base/sys_info.h"
-#include "remoting/base/constants.h"
-#include "remoting/client/chromoting_stats.h"
-#include "remoting/protocol/connection_to_host.h"
-#include "remoting/protocol/errors.h"
-#include "third_party/libjingle/source/talk/xmllite/xmlelement.h"
-
-using base::StringPrintf;
-using base::SysInfo;
-using buzz::QName;
-using buzz::XmlElement;
-using remoting::protocol::ConnectionToHost;
-
-namespace remoting {
-
-namespace client {
-
-namespace {
-const char kLogCommand[] = "log";
-
-const char kLogEntry[] = "entry";
-
-const char kKeyEventName[] = "event-name";
-const char kValueEventNameSessionState[] = "session-state";
-const char kValueEventNameStatistics[] = "connection-statistics";
-const char kValueEventNameSessionIdOld[] = "session-id-old";
-const char kValueEventNameSessionIdNew[] = "session-id-new";
-
-const char kKeyRole[] = "role";
-const char kValueRoleClient[] = "client";
-
-const char kKeyMode[] = "mode";
-const char kValueModeIt2Me[] = "it2me";
-const char kValueModeMe2Me[] = "me2me";
-
-const char kKeySessionState[] = "session-state";
-const char kValueSessionStateConnected[] = "connected";
-const char kValueSessionStateClosed[] = "closed";
-
-const char kKeyOsName[] = "os-name";
-const char kKeyOsVersion[] = "os-version";
-const char kKeyAppVersion[] = "app-version";
-
-const char kKeyCpu[] = "cpu";
-
-}  // namespace
-
-ServerLogEntry::ServerLogEntry() {
-}
-
-ServerLogEntry::~ServerLogEntry() {
-}
-
-// static
-scoped_ptr<buzz::XmlElement> ServerLogEntry::MakeStanza() {
-  return scoped_ptr<buzz::XmlElement>(
-      new XmlElement(QName(kChromotingXmlNamespace, kLogCommand)));
-}
-
-// static
-scoped_ptr<ServerLogEntry> ServerLogEntry::MakeForSessionStateChange(
-    protocol::ConnectionToHost::State state,
-    protocol::ErrorCode error) {
-  scoped_ptr<ServerLogEntry> entry(new ServerLogEntry());
-  entry->Set(kKeyRole, kValueRoleClient);
-  entry->Set(kKeyEventName, kValueEventNameSessionState);
-
-  entry->Set(kKeySessionState, GetValueSessionState(state));
-  if (error != protocol::OK) {
-    entry->Set("connection-error", GetValueError(error));
-  }
-
-  return entry.Pass();
-}
-
-// static
-scoped_ptr<ServerLogEntry> ServerLogEntry::MakeForStatistics(
-    ChromotingStats* statistics) {
-  scoped_ptr<ServerLogEntry> entry(new ServerLogEntry());
-  entry->Set(kKeyRole, kValueRoleClient);
-  entry->Set(kKeyEventName, kValueEventNameStatistics);
-
-  entry->Set("video-bandwidth",
-             StringPrintf("%.2f", statistics->video_bandwidth()->Rate()));
-  entry->Set("capture-latency",
-             StringPrintf("%.2f", statistics->video_capture_ms()->Average()));
-  entry->Set("encode-latency",
-             StringPrintf("%.2f", statistics->video_encode_ms()->Average()));
-  entry->Set("decode-latency",
-             StringPrintf("%.2f", statistics->video_decode_ms()->Average()));
-  entry->Set("render-latency",
-             StringPrintf("%.2f", statistics->video_frame_rate()->Rate()));
-  entry->Set("roundtrip-latency",
-             StringPrintf("%.2f", statistics->round_trip_ms()->Average()));
-
-  return entry.Pass();
-}
-
-// static
-scoped_ptr<ServerLogEntry> ServerLogEntry::MakeForSessionIdOld(
-    const std::string& session_id) {
-  scoped_ptr<ServerLogEntry> entry(new ServerLogEntry());
-  entry->Set(kKeyRole, kValueRoleClient);
-  entry->Set(kKeyEventName, kValueEventNameSessionIdOld);
-  entry->AddSessionId(session_id);
-  return entry.Pass();
-}
-
-// static
-scoped_ptr<ServerLogEntry> ServerLogEntry::MakeForSessionIdNew(
-    const std::string& session_id) {
-  scoped_ptr<ServerLogEntry> entry(new ServerLogEntry());
-  entry->Set(kKeyRole, kValueRoleClient);
-  entry->Set(kKeyEventName, kValueEventNameSessionIdNew);
-  entry->AddSessionId(session_id);
-  return entry.Pass();
-}
-
-void ServerLogEntry::AddClientFields() {
-  Set(kKeyOsName, SysInfo::OperatingSystemName());
-  Set(kKeyOsVersion, SysInfo::OperatingSystemVersion());
-  Set(kKeyAppVersion, STRINGIZE(VERSION));
-  Set(kKeyCpu, SysInfo::OperatingSystemArchitecture());
-};
-
-void ServerLogEntry::AddModeField(ServerLogEntry::Mode mode) {
-  Set(kKeyMode, GetValueMode(mode));
-}
-
-void ServerLogEntry::AddSessionId(const std::string& session_id) {
-  Set("session-id", session_id);
-}
-
-void ServerLogEntry::AddSessionDuration(base::TimeDelta duration) {
-  Set("session-duration", base::Int64ToString(duration.InSeconds()));
-}
-
-// static
-const char* ServerLogEntry::GetValueMode(ServerLogEntry::Mode mode) {
-  switch (mode) {
-    case IT2ME:
-      return kValueModeIt2Me;
-    case ME2ME:
-      return kValueModeMe2Me;
-    default:
-      NOTREACHED();
-      return NULL;
-  }
-}
-
-scoped_ptr<XmlElement> ServerLogEntry::ToStanza() const {
-  scoped_ptr<XmlElement> stanza(new XmlElement(QName(
-      kChromotingXmlNamespace, kLogEntry)));
-  ValuesMap::const_iterator iter;
-  for (iter = values_map_.begin(); iter != values_map_.end(); ++iter) {
-    stanza->AddAttr(QName(std::string(), iter->first), iter->second);
-  }
-  return stanza.Pass();
-}
-
-// static
-const char* ServerLogEntry::GetValueSessionState(
-    ConnectionToHost::State state) {
-  switch (state) {
-    // Where possible, these are the same strings that the webapp sends for the
-    // corresponding state - see remoting/webapp/server_log_entry.js.
-    case ConnectionToHost::INITIALIZING:
-      return "initializing";
-    case ConnectionToHost::CONNECTING:
-      return "connecting";
-    case ConnectionToHost::AUTHENTICATED:
-      return "authenticated";
-    case ConnectionToHost::CONNECTED:
-      return kValueSessionStateConnected;
-    case ConnectionToHost::FAILED:
-      return "connection-failed";
-    case ConnectionToHost::CLOSED:
-      return kValueSessionStateClosed;
-    default:
-      NOTREACHED();
-      return NULL;
-  }
-}
-
-// static
-const char* ServerLogEntry::GetValueError(protocol::ErrorCode error) {
-  switch (error) {
-    // Where possible, these are the same strings that the webapp sends for the
-    // corresponding error - see remoting/webapp/server_log_entry.js.
-    case protocol::OK:
-      return "none";
-    case protocol::PEER_IS_OFFLINE:
-      return "host-is-offline";
-    case protocol::SESSION_REJECTED:
-      return "session-rejected";
-    case protocol::INCOMPATIBLE_PROTOCOL:
-      return "incompatible-protocol";
-    case protocol::AUTHENTICATION_FAILED:
-      return "authentication-failed";
-    case protocol::CHANNEL_CONNECTION_ERROR:
-      return "channel-connection-error";
-    case protocol::SIGNALING_ERROR:
-      return "signaling-error";
-    case protocol::SIGNALING_TIMEOUT:
-      return "signaling-timeout";
-    case protocol::HOST_OVERLOAD:
-      return "host-overload";
-    case protocol::UNKNOWN_ERROR:
-      return "unknown-error";
-    default:
-      NOTREACHED();
-      return NULL;
-  }
-}
-
-void ServerLogEntry::AddEventName(const std::string& event_name) {
-  Set("event-name", event_name);
-}
-
-void ServerLogEntry::Set(const std::string& key, const std::string& value) {
-  values_map_[key] = value;
-}
-
-}  // namespace client
-
-}  // namespace remoting
diff --git a/remoting/client/server_log_entry.h b/remoting/client/server_log_entry.h
deleted file mode 100644
index 9f861d2..0000000
--- a/remoting/client/server_log_entry.h
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright 2014 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.
-
-#ifndef REMOTING_CLIENT_SERVER_LOG_ENTRY_H_
-#define REMOTING_CLIENT_SERVER_LOG_ENTRY_H_
-
-#include <map>
-#include <string>
-
-#include "base/memory/scoped_ptr.h"
-#include "base/time/time.h"
-#include "remoting/protocol/connection_to_host.h"
-#include "remoting/protocol/errors.h"
-
-namespace buzz {
-class XmlElement;
-}  // namespace buzz
-
-namespace remoting {
-
-class ChromotingStats;
-
-// Temporary namespace to prevent conflict with the same-named class in
-// remoting/host when linking unittests.
-//
-// TODO(lambroslambrou): Remove this and factor out any shared code.
-namespace client {
-
-class ServerLogEntry {
- public:
-  // The mode of a connection.
-  enum Mode {
-    IT2ME,
-    ME2ME
-  };
-
-  // Constructs a log stanza. The caller should add one or more log entry
-  // stanzas as children of this stanza, before sending the log stanza to
-  // the remoting bot.
-  static scoped_ptr<buzz::XmlElement> MakeStanza();
-
-  // Constructs a log entry for a session state change.
-  static scoped_ptr<ServerLogEntry> MakeForSessionStateChange(
-      remoting::protocol::ConnectionToHost::State state,
-      remoting::protocol::ErrorCode error);
-
-  // Constructs a log entry for reporting statistics.
-  static scoped_ptr<ServerLogEntry> MakeForStatistics(
-      remoting::ChromotingStats* statistics);
-
-  // Constructs a log entry for reporting session ID is old.
-  static scoped_ptr<ServerLogEntry> MakeForSessionIdOld(
-      const std::string& session_id);
-
-  // Constructs a log entry for reporting session ID is old.
-  static scoped_ptr<ServerLogEntry> MakeForSessionIdNew(
-      const std::string& session_id);
-
-  ~ServerLogEntry();
-
-  // Adds fields describing the client to this log entry.
-  void AddClientFields();
-
-  // Adds a field describing the mode of a connection to this log entry.
-  void AddModeField(Mode mode);
-
-  void AddEventName(const std::string& event_name);
-  void AddSessionId(const std::string& session_id);
-  void AddSessionDuration(base::TimeDelta duration);
-
-  // Converts this object to an XML stanza.
-  scoped_ptr<buzz::XmlElement> ToStanza() const;
-
- private:
-  typedef std::map<std::string, std::string> ValuesMap;
-
-  ServerLogEntry();
-  void Set(const std::string& key, const std::string& value);
-
-  static const char* GetValueSessionState(
-      remoting::protocol::ConnectionToHost::State state);
-  static const char* GetValueError(remoting::protocol::ErrorCode error);
-  static const char* GetValueMode(Mode mode);
-
-  ValuesMap values_map_;
-};
-
-}  // namespace client
-
-}  // namespace remoting
-
-#endif  // REMOTING_CLIENT_SERVER_LOG_ENTRY_H_
diff --git a/remoting/client/server_log_entry_client.cc b/remoting/client/server_log_entry_client.cc
new file mode 100644
index 0000000..f664f8f
--- /dev/null
+++ b/remoting/client/server_log_entry_client.cc
@@ -0,0 +1,166 @@
+// Copyright 2014 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/client/server_log_entry_client.h"
+
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringize_macros.h"
+#include "base/strings/stringprintf.h"
+#include "base/sys_info.h"
+#include "remoting/client/chromoting_stats.h"
+#include "remoting/jingle_glue/server_log_entry.h"
+
+using base::StringPrintf;
+using base::SysInfo;
+using remoting::protocol::ConnectionToHost;
+using remoting::protocol::ErrorCode;
+
+namespace remoting {
+
+namespace {
+const char kValueRoleClient[] = "client";
+
+const char kValueEventNameSessionState[] = "session-state";
+const char kValueEventNameStatistics[] = "connection-statistics";
+const char kValueEventNameSessionIdOld[] = "session-id-old";
+const char kValueEventNameSessionIdNew[] = "session-id-new";
+
+const char kKeySessionId[] = "session-id";
+const char kKeySessionDuration[] = "session-duration";
+
+const char kKeySessionState[] = "session-state";
+const char kKeyConnectionError[] = "connection-error";
+const char kValueSessionStateConnected[] = "connected";
+const char kValueSessionStateClosed[] = "closed";
+
+const char kKeyOsName[] = "os-name";
+const char kKeyOsVersion[] = "os-version";
+const char kKeyAppVersion[] = "app-version";
+
+const char* GetValueSessionState(ConnectionToHost::State state) {
+  switch (state) {
+    // Where possible, these are the same strings that the webapp sends for the
+    // corresponding state - see remoting/webapp/server_log_entry.js.
+    case ConnectionToHost::INITIALIZING:
+      return "initializing";
+    case ConnectionToHost::CONNECTING:
+      return "connecting";
+    case ConnectionToHost::AUTHENTICATED:
+      return "authenticated";
+    case ConnectionToHost::CONNECTED:
+      return kValueSessionStateConnected;
+    case ConnectionToHost::FAILED:
+      return "connection-failed";
+    case ConnectionToHost::CLOSED:
+      return kValueSessionStateClosed;
+    default:
+      NOTREACHED();
+      return NULL;
+  }
+}
+
+const char* GetValueError(ErrorCode error) {
+  switch (error) {
+    // Where possible, these are the same strings that the webapp sends for the
+    // corresponding error - see remoting/webapp/server_log_entry.js.
+    case protocol::OK:
+      return "none";
+    case protocol::PEER_IS_OFFLINE:
+      return "host-is-offline";
+    case protocol::SESSION_REJECTED:
+      return "session-rejected";
+    case protocol::INCOMPATIBLE_PROTOCOL:
+      return "incompatible-protocol";
+    case protocol::AUTHENTICATION_FAILED:
+      return "authentication-failed";
+    case protocol::CHANNEL_CONNECTION_ERROR:
+      return "channel-connection-error";
+    case protocol::SIGNALING_ERROR:
+      return "signaling-error";
+    case protocol::SIGNALING_TIMEOUT:
+      return "signaling-timeout";
+    case protocol::HOST_OVERLOAD:
+      return "host-overload";
+    case protocol::UNKNOWN_ERROR:
+      return "unknown-error";
+    default:
+      NOTREACHED();
+      return NULL;
+  }
+}
+
+}  // namespace
+
+scoped_ptr<ServerLogEntry> MakeLogEntryForSessionStateChange(
+    ConnectionToHost::State state,
+    ErrorCode error) {
+  scoped_ptr<ServerLogEntry> entry(new ServerLogEntry());
+  entry->AddRoleField(kValueRoleClient);
+  entry->AddEventNameField(kValueEventNameSessionState);
+
+  entry->Set(kKeySessionState, GetValueSessionState(state));
+  if (error != protocol::OK) {
+    entry->Set(kKeyConnectionError, GetValueError(error));
+  }
+
+  return entry.Pass();
+}
+
+scoped_ptr<ServerLogEntry> MakeLogEntryForStatistics(
+    ChromotingStats* statistics) {
+  scoped_ptr<ServerLogEntry> entry(new ServerLogEntry());
+  entry->AddRoleField(kValueRoleClient);
+  entry->AddEventNameField(kValueEventNameStatistics);
+
+  entry->Set("video-bandwidth",
+             StringPrintf("%.2f", statistics->video_bandwidth()->Rate()));
+  entry->Set("capture-latency",
+             StringPrintf("%.2f", statistics->video_capture_ms()->Average()));
+  entry->Set("encode-latency",
+             StringPrintf("%.2f", statistics->video_encode_ms()->Average()));
+  entry->Set("decode-latency",
+             StringPrintf("%.2f", statistics->video_decode_ms()->Average()));
+  entry->Set("render-latency",
+             StringPrintf("%.2f", statistics->video_frame_rate()->Rate()));
+  entry->Set("roundtrip-latency",
+             StringPrintf("%.2f", statistics->round_trip_ms()->Average()));
+
+  return entry.Pass();
+}
+
+scoped_ptr<ServerLogEntry> MakeLogEntryForSessionIdOld(
+    const std::string& session_id) {
+  scoped_ptr<ServerLogEntry> entry(new ServerLogEntry());
+  entry->AddRoleField(kValueRoleClient);
+  entry->AddEventNameField(kValueEventNameSessionIdOld);
+  AddSessionIdToLogEntry(entry.get(), session_id);
+  return entry.Pass();
+}
+
+scoped_ptr<ServerLogEntry> MakeLogEntryForSessionIdNew(
+    const std::string& session_id) {
+  scoped_ptr<ServerLogEntry> entry(new ServerLogEntry());
+  entry->AddRoleField(kValueRoleClient);
+  entry->AddEventNameField(kValueEventNameSessionIdNew);
+  AddSessionIdToLogEntry(entry.get(), session_id);
+  return entry.Pass();
+}
+
+void AddClientFieldsToLogEntry(ServerLogEntry* entry) {
+  entry->Set(kKeyOsName, SysInfo::OperatingSystemName());
+  entry->Set(kKeyOsVersion, SysInfo::OperatingSystemVersion());
+  entry->Set(kKeyAppVersion, STRINGIZE(VERSION));
+  entry->AddCpuField();
+}
+
+void AddSessionIdToLogEntry(ServerLogEntry* entry, const std::string& id) {
+  entry->Set(kKeySessionId, id);
+}
+
+void AddSessionDurationToLogEntry(ServerLogEntry* entry,
+                                  base::TimeDelta duration) {
+  entry->Set(kKeySessionDuration, base::Int64ToString(duration.InSeconds()));
+}
+
+}  // namespace remoting
diff --git a/remoting/client/server_log_entry_client.h b/remoting/client/server_log_entry_client.h
new file mode 100644
index 0000000..b2ef262
--- /dev/null
+++ b/remoting/client/server_log_entry_client.h
@@ -0,0 +1,41 @@
+// Copyright 2014 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.
+
+#ifndef REMOTING_CLIENT_SERVER_LOG_ENTRY_CLIENT_H_
+#define REMOTING_CLIENT_SERVER_LOG_ENTRY_CLIENT_H_
+
+#include "base/time/time.h"
+#include "remoting/protocol/connection_to_host.h"
+#include "remoting/protocol/errors.h"
+
+namespace remoting {
+
+class ChromotingStats;
+class ServerLogEntry;
+
+// Constructs a log entry for a session state change.
+scoped_ptr<ServerLogEntry> MakeLogEntryForSessionStateChange(
+    protocol::ConnectionToHost::State state,
+    protocol::ErrorCode error);
+
+// Constructs a log entry for reporting statistics.
+scoped_ptr<ServerLogEntry> MakeLogEntryForStatistics(
+    ChromotingStats* statistics);
+
+// Constructs a log entry for reporting session ID is old.
+scoped_ptr<ServerLogEntry> MakeLogEntryForSessionIdOld(
+    const std::string& session_id);
+
+// Constructs a log entry for reporting session ID is old.
+scoped_ptr<ServerLogEntry> MakeLogEntryForSessionIdNew(
+    const std::string& session_id);
+
+void AddClientFieldsToLogEntry(ServerLogEntry* entry);
+void AddSessionIdToLogEntry(ServerLogEntry* entry, const std::string& id);
+void AddSessionDurationToLogEntry(ServerLogEntry* entry,
+                                  base::TimeDelta duration);
+
+}  // namespace remoting
+
+#endif  // REMOTING_CLIENT_SERVER_LOG_ENTRY_CLIENT_H_
diff --git a/remoting/client/server_log_entry_client_unittest.cc b/remoting/client/server_log_entry_client_unittest.cc
new file mode 100644
index 0000000..9861b180
--- /dev/null
+++ b/remoting/client/server_log_entry_client_unittest.cc
@@ -0,0 +1,125 @@
+// Copyright 2014 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 "base/memory/scoped_ptr.h"
+#include "base/strings/stringize_macros.h"
+#include "base/sys_info.h"
+#include "remoting/client/chromoting_stats.h"
+#include "remoting/client/server_log_entry_client.h"
+#include "remoting/jingle_glue/server_log_entry.h"
+#include "remoting/jingle_glue/server_log_entry_unittest.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/libjingle/source/talk/xmllite/xmlelement.h"
+
+using base::SysInfo;
+using buzz::XmlAttr;
+using buzz::XmlElement;
+using remoting::protocol::ConnectionToHost;
+
+namespace remoting {
+
+TEST(ServerLogEntryClientTest, SessionStateChange) {
+  scoped_ptr<ServerLogEntry> entry(MakeLogEntryForSessionStateChange(
+      ConnectionToHost::CONNECTED, remoting::protocol::OK));
+  scoped_ptr<XmlElement> stanza = entry->ToStanza();
+  std::string error;
+  std::map<std::string, std::string> key_value_pairs;
+  key_value_pairs["role"] = "client";
+  key_value_pairs["event-name"] = "session-state";
+  key_value_pairs["session-state"] = "connected";
+  std::set<std::string> keys;
+  ASSERT_TRUE(VerifyStanza(key_value_pairs, keys, stanza.get(), &error))
+      << error;
+}
+
+TEST(ServerLogEntryClientTest, SessionStateChangeWithError) {
+  scoped_ptr<ServerLogEntry> entry(MakeLogEntryForSessionStateChange(
+      ConnectionToHost::FAILED, remoting::protocol::PEER_IS_OFFLINE));
+  scoped_ptr<XmlElement> stanza = entry->ToStanza();
+  std::string error;
+  std::map<std::string, std::string> key_value_pairs;
+  key_value_pairs["role"] = "client";
+  key_value_pairs["event-name"] = "session-state";
+  key_value_pairs["session-state"] = "connection-failed";
+  key_value_pairs["connection-error"] = "host-is-offline";
+  std::set<std::string> keys;
+  ASSERT_TRUE(VerifyStanza(key_value_pairs, keys, stanza.get(), &error))
+      << error;
+}
+
+TEST(ServerLogEntryClientTest, Statistics) {
+  ChromotingStats statistics;
+  scoped_ptr<ServerLogEntry> entry(MakeLogEntryForStatistics(&statistics));
+  scoped_ptr<XmlElement> stanza = entry->ToStanza();
+  std::string error;
+  std::map<std::string, std::string> key_value_pairs;
+  key_value_pairs["role"] = "client";
+  key_value_pairs["event-name"] = "connection-statistics";
+  std::set<std::string> keys;
+  keys.insert("video-bandwidth");
+  keys.insert("capture-latency");
+  keys.insert("encode-latency");
+  keys.insert("decode-latency");
+  keys.insert("render-latency");
+  keys.insert("roundtrip-latency");
+  ASSERT_TRUE(VerifyStanza(key_value_pairs, keys, stanza.get(), &error))
+      << error;
+}
+
+TEST(ServerLogEntryClientTest, SessionIdChanged) {
+  scoped_ptr<ServerLogEntry> entry(MakeLogEntryForSessionIdOld("abc"));
+  scoped_ptr<XmlElement> stanza = entry->ToStanza();
+  std::string error;
+  std::map<std::string, std::string> key_value_pairs;
+  key_value_pairs["role"] = "client";
+  key_value_pairs["event-name"] = "session-id-old";
+  key_value_pairs["session-id"] = "abc";
+  std::set<std::string> keys;
+  ASSERT_TRUE(VerifyStanza(key_value_pairs, keys, stanza.get(), &error))
+      << error;
+
+  entry = MakeLogEntryForSessionIdNew("def");
+  stanza = entry->ToStanza();
+  key_value_pairs["event-name"] = "session-id-new";
+  key_value_pairs["session-id"] = "def";
+  ASSERT_TRUE(VerifyStanza(key_value_pairs, keys, stanza.get(), &error))
+      << error;
+}
+
+TEST(ServerLogEntryClientTest, AddClientFields) {
+  scoped_ptr<ServerLogEntry> entry(MakeLogEntryForSessionStateChange(
+      ConnectionToHost::CONNECTED, remoting::protocol::OK));
+  AddClientFieldsToLogEntry(entry.get());
+  scoped_ptr<XmlElement> stanza = entry->ToStanza();
+  std::string error;
+  std::map<std::string, std::string> key_value_pairs;
+  key_value_pairs["role"] = "client";
+  key_value_pairs["event-name"] = "session-state";
+  key_value_pairs["session-state"] = "connected";
+  key_value_pairs["os-name"] = SysInfo::OperatingSystemName();
+  key_value_pairs["os-version"] = SysInfo::OperatingSystemVersion();
+  key_value_pairs["app-version"] = STRINGIZE(VERSION);
+  key_value_pairs["cpu"] = SysInfo::OperatingSystemArchitecture();
+  std::set<std::string> keys;
+  ASSERT_TRUE(VerifyStanza(key_value_pairs, keys, stanza.get(), &error)) <<
+      error;
+}
+
+TEST(ServerLogEntryClientTest, AddSessionDuration) {
+  scoped_ptr<ServerLogEntry> entry(MakeLogEntryForSessionStateChange(
+      ConnectionToHost::CONNECTED, remoting::protocol::OK));
+  AddSessionDurationToLogEntry(entry.get(), base::TimeDelta::FromSeconds(123));
+  scoped_ptr<XmlElement> stanza = entry->ToStanza();
+  std::string error;
+  std::map<std::string, std::string> key_value_pairs;
+  key_value_pairs["role"] = "client";
+  key_value_pairs["event-name"] = "session-state";
+  key_value_pairs["session-state"] = "connected";
+  key_value_pairs["session-duration"] = "123";
+  std::set<std::string> keys;
+  ASSERT_TRUE(VerifyStanza(key_value_pairs, keys, stanza.get(), &error))
+      << error;
+}
+
+}  // namespace remoting
diff --git a/remoting/host/heartbeat_sender.cc b/remoting/host/heartbeat_sender.cc
index b489cbe..61b9370 100644
--- a/remoting/host/heartbeat_sender.cc
+++ b/remoting/host/heartbeat_sender.cc
@@ -14,8 +14,9 @@
 #include "base/time/time.h"
 #include "remoting/base/constants.h"
 #include "remoting/base/logging.h"
-#include "remoting/host/server_log_entry.h"
+#include "remoting/host/server_log_entry_host.h"
 #include "remoting/jingle_glue/iq_sender.h"
+#include "remoting/jingle_glue/server_log_entry.h"
 #include "remoting/jingle_glue/signal_strategy.h"
 #include "third_party/libjingle/source/talk/xmllite/xmlelement.h"
 #include "third_party/libjingle/source/talk/xmpp/constants.h"
@@ -255,8 +256,8 @@
   heartbeat->AddElement(version_tag.release());
   // Append log message (which isn't signed).
   scoped_ptr<XmlElement> log(ServerLogEntry::MakeStanza());
-  scoped_ptr<ServerLogEntry> log_entry(ServerLogEntry::MakeForHeartbeat());
-  log_entry->AddHostFields();
+  scoped_ptr<ServerLogEntry> log_entry(MakeLogEntryForHeartbeat());
+  AddHostFieldsToLogEntry(log_entry.get());
   log->AddElement(log_entry->ToStanza().release());
   heartbeat->AddElement(log.release());
   return heartbeat.Pass();
diff --git a/remoting/host/host_status_sender.cc b/remoting/host/host_status_sender.cc
index 545fce4..44fe98ec2 100644
--- a/remoting/host/host_status_sender.cc
+++ b/remoting/host/host_status_sender.cc
@@ -9,8 +9,9 @@
 #include "base/time/time.h"
 #include "remoting/base/constants.h"
 #include "remoting/base/logging.h"
-#include "remoting/host/server_log_entry.h"
+#include "remoting/host/server_log_entry_host.h"
 #include "remoting/jingle_glue/iq_sender.h"
+#include "remoting/jingle_glue/server_log_entry.h"
 #include "remoting/jingle_glue/signal_strategy.h"
 #include "third_party/libjingle/source/talk/xmllite/xmlelement.h"
 #include "third_party/libjingle/source/talk/xmpp/constants.h"
@@ -123,8 +124,8 @@
   // Append log message (which isn't signed).
   scoped_ptr<XmlElement> log(ServerLogEntry::MakeStanza());
   scoped_ptr<ServerLogEntry> log_entry(
-      ServerLogEntry::MakeForHostStatus(status, exit_code));
-  log_entry->AddHostFields();
+      MakeLogEntryForHostStatus(status, exit_code));
+  AddHostFieldsToLogEntry(log_entry.get());
   log->AddElement(log_entry->ToStanza().release());
   host_status->AddElement(log.release());
   return host_status.Pass();
diff --git a/remoting/host/it2me/it2me_host.cc b/remoting/host/it2me/it2me_host.cc
index 213e782..8081967 100644
--- a/remoting/host/it2me/it2me_host.cc
+++ b/remoting/host/it2me/it2me_host.cc
@@ -21,6 +21,7 @@
 #include "remoting/host/register_support_host_request.h"
 #include "remoting/host/session_manager_factory.h"
 #include "remoting/jingle_glue/network_settings.h"
+#include "remoting/jingle_glue/server_log_entry.h"
 #include "remoting/protocol/it2me_host_authenticator_factory.h"
 
 namespace remoting {
diff --git a/remoting/host/it2me/it2me_host.h b/remoting/host/it2me/it2me_host.h
index a5d77a4..4085d55e 100644
--- a/remoting/host/it2me/it2me_host.h
+++ b/remoting/host/it2me/it2me_host.h
@@ -18,12 +18,13 @@
 
 namespace remoting {
 
-class RegisterSupportHostRequest;
-class HostNPScriptObject;
-class DesktopEnvironmentFactory;
-class HostEventLogger;
 class ChromotingHost;
 class ChromotingHostContext;
+class DesktopEnvironmentFactory;
+class HostEventLogger;
+class HostNPScriptObject;
+class RegisterSupportHostRequest;
+class RsaKeyPair;
 
 namespace policy_hack {
 
diff --git a/remoting/host/it2me/it2me_native_messaging_host_main.cc b/remoting/host/it2me/it2me_native_messaging_host_main.cc
index 1483e04..bae18289 100644
--- a/remoting/host/it2me/it2me_native_messaging_host_main.cc
+++ b/remoting/host/it2me/it2me_native_messaging_host_main.cc
@@ -13,6 +13,7 @@
 #include "net/socket/ssl_server_socket.h"
 #include "remoting/base/breakpad.h"
 #include "remoting/base/resources.h"
+#include "remoting/host/host_exit_codes.h"
 #include "remoting/host/it2me/it2me_native_messaging_host.h"
 #include "remoting/host/logging.h"
 #include "remoting/host/usage_stats_consent.h"
diff --git a/remoting/host/log_to_server.cc b/remoting/host/log_to_server.cc
index 6799589..8e0dd6b 100644
--- a/remoting/host/log_to_server.cc
+++ b/remoting/host/log_to_server.cc
@@ -8,8 +8,9 @@
 #include "base/message_loop/message_loop_proxy.h"
 #include "remoting/base/constants.h"
 #include "remoting/host/host_status_monitor.h"
-#include "remoting/host/server_log_entry.h"
+#include "remoting/host/server_log_entry_host.h"
 #include "remoting/jingle_glue/iq_sender.h"
+#include "remoting/jingle_glue/server_log_entry.h"
 #include "remoting/jingle_glue/signal_strategy.h"
 #include "remoting/protocol/transport.h"
 #include "third_party/libjingle/source/talk/xmllite/xmlelement.h"
@@ -43,13 +44,13 @@
   DCHECK(CalledOnValidThread());
 
   scoped_ptr<ServerLogEntry> entry(
-      ServerLogEntry::MakeForSessionStateChange(connected));
-  entry->AddHostFields();
+      MakeLogEntryForSessionStateChange(connected));
+  AddHostFieldsToLogEntry(entry.get());
   entry->AddModeField(mode_);
 
   if (connected) {
     DCHECK(connection_route_type_.count(jid) == 1);
-    entry->AddConnectionTypeField(connection_route_type_[jid]);
+    AddConnectionTypeToLogEntry(entry.get(), connection_route_type_[jid]);
   }
   Log(*entry.get());
 }
diff --git a/remoting/host/log_to_server.h b/remoting/host/log_to_server.h
index bbd920c..38f4edb3 100644
--- a/remoting/host/log_to_server.h
+++ b/remoting/host/log_to_server.h
@@ -13,7 +13,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/threading/non_thread_safe.h"
 #include "remoting/host/host_status_observer.h"
-#include "remoting/host/server_log_entry.h"
+#include "remoting/jingle_glue/server_log_entry.h"
 #include "remoting/jingle_glue/signal_strategy.h"
 #include "remoting/protocol/transport.h"
 
diff --git a/remoting/host/server_log_entry.cc b/remoting/host/server_log_entry.cc
deleted file mode 100644
index 58e24b5c..0000000
--- a/remoting/host/server_log_entry.cc
+++ /dev/null
@@ -1,171 +0,0 @@
-// Copyright (c) 2012 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/host/server_log_entry.h"
-
-#include "base/logging.h"
-#include "base/strings/stringize_macros.h"
-#include "base/sys_info.h"
-#include "remoting/base/constants.h"
-#include "remoting/protocol/session.h"
-#include "third_party/libjingle/source/talk/xmllite/xmlelement.h"
-
-using base::SysInfo;
-using buzz::QName;
-using buzz::XmlElement;
-using remoting::protocol::Session;
-
-namespace remoting {
-
-namespace {
-const char kLogCommand[] = "log";
-
-const char kLogEntry[] = "entry";
-
-const char kKeyEventName[] = "event-name";
-const char kValueEventNameSessionState[] = "session-state";
-const char kValueEventNameHeartbeat[] = "heartbeat";
-const char kValueEventNameHostStatus[] = "host-status";
-
-const char kKeyRole[] = "role";
-const char kValueRoleHost[] = "host";
-
-const char kKeyMode[] = "mode";
-const char kValueModeIt2Me[] = "it2me";
-const char kValueModeMe2Me[] = "me2me";
-
-const char kKeySessionState[] = "session-state";
-const char kValueSessionStateConnected[] = "connected";
-const char kValueSessionStateClosed[] = "closed";
-
-const char kStatusName[] = "status";
-const char kExitCodeName[] = "exit-code";
-
-const char kKeyOsName[] = "os-name";
-
-#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS)
-const char kKeyOsVersion[] = "os-version";
-#endif
-
-const char kKeyHostVersion[] = "host-version";
-
-const char kKeyCpu[] = "cpu";
-
-const char kKeyConnectionType[] = "connection-type";
-
-}  // namespace
-
-ServerLogEntry::ServerLogEntry() {
-}
-
-ServerLogEntry::~ServerLogEntry() {
-}
-
-// static
-scoped_ptr<buzz::XmlElement> ServerLogEntry::MakeStanza() {
-  return scoped_ptr<buzz::XmlElement>(
-      new XmlElement(QName(kChromotingXmlNamespace, kLogCommand)));
-}
-
-// static
-scoped_ptr<ServerLogEntry> ServerLogEntry::MakeForSessionStateChange(
-    bool connected) {
-  scoped_ptr<ServerLogEntry> entry(new ServerLogEntry());
-  entry->Set(kKeyRole, kValueRoleHost);
-  entry->Set(kKeyEventName, kValueEventNameSessionState);
-  entry->Set(kKeySessionState, GetValueSessionState(connected));
-  return entry.Pass();
-}
-
-// static
-scoped_ptr<ServerLogEntry> ServerLogEntry::MakeForHeartbeat() {
-  scoped_ptr<ServerLogEntry> entry(new ServerLogEntry());
-  entry->Set(kKeyRole, kValueRoleHost);
-  entry->Set(kKeyEventName, kValueEventNameHeartbeat);
-  return entry.Pass();
-}
-
-// static
-scoped_ptr<ServerLogEntry> ServerLogEntry::MakeForHostStatus(
-    HostStatusSender::HostStatus host_status, HostExitCodes exit_code) {
-  scoped_ptr<ServerLogEntry> entry(new ServerLogEntry());
-  entry->Set(kKeyRole, kValueRoleHost);
-  entry->Set(kKeyEventName, kValueEventNameHostStatus);
-  entry->Set(kStatusName, HostStatusSender::HostStatusToString(host_status));
-  if (host_status == HostStatusSender::OFFLINE)
-    entry->Set(kExitCodeName, ExitCodeToString(exit_code));
-  return entry.Pass();
-}
-
-void ServerLogEntry::AddHostFields() {
-#if defined(OS_WIN)
-  Set(kKeyOsName, "Windows");
-#elif defined(OS_MACOSX)
-  Set(kKeyOsName, "Mac");
-#elif defined(OS_CHROMEOS)
-  Set(kKeyOsName, "ChromeOS");
-#elif defined(OS_LINUX)
-  Set(kKeyOsName, "Linux");
-#endif
-
-  // SysInfo::OperatingSystemVersionNumbers is only defined for the following
-  // OSes: see base/sys_info_unittest.cc.
-#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS)
-  std::stringstream os_version;
-  int32 os_major_version = 0;
-  int32 os_minor_version = 0;
-  int32 os_bugfix_version = 0;
-  SysInfo::OperatingSystemVersionNumbers(&os_major_version, &os_minor_version,
-                                         &os_bugfix_version);
-  os_version << os_major_version << "." << os_minor_version << "."
-             << os_bugfix_version;
-  Set(kKeyOsVersion, os_version.str());
-#endif
-
-  Set(kKeyHostVersion, STRINGIZE(VERSION));
-  Set(kKeyCpu, SysInfo::OperatingSystemArchitecture());
-};
-
-void ServerLogEntry::AddModeField(ServerLogEntry::Mode mode) {
-  Set(kKeyMode, GetValueMode(mode));
-}
-
-void ServerLogEntry::AddConnectionTypeField(
-    protocol::TransportRoute::RouteType type) {
-  Set(kKeyConnectionType, protocol::TransportRoute::GetTypeString(type));
-}
-
-// static
-const char* ServerLogEntry::GetValueMode(ServerLogEntry::Mode mode) {
-  switch (mode) {
-    case IT2ME:
-      return kValueModeIt2Me;
-    case ME2ME:
-      return kValueModeMe2Me;
-    default:
-      NOTREACHED();
-      return NULL;
-  }
-}
-
-scoped_ptr<XmlElement> ServerLogEntry::ToStanza() const {
-  scoped_ptr<XmlElement> stanza(new XmlElement(QName(
-      kChromotingXmlNamespace, kLogEntry)));
-  ValuesMap::const_iterator iter;
-  for (iter = values_map_.begin(); iter != values_map_.end(); ++iter) {
-    stanza->AddAttr(QName(std::string(), iter->first), iter->second);
-  }
-  return stanza.Pass();
-}
-
-// static
-const char* ServerLogEntry::GetValueSessionState(bool connected) {
-  return connected ? kValueSessionStateConnected : kValueSessionStateClosed;
-}
-
-void ServerLogEntry::Set(const std::string& key, const std::string& value) {
-  values_map_[key] = value;
-}
-
-}  // namespace remoting
diff --git a/remoting/host/server_log_entry.h b/remoting/host/server_log_entry.h
deleted file mode 100644
index ccaa8501b..0000000
--- a/remoting/host/server_log_entry.h
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright (c) 2012 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.
-
-#ifndef REMOTING_HOST_SERVER_LOG_ENTRY_H_
-#define REMOTING_HOST_SERVER_LOG_ENTRY_H_
-
-#include <map>
-#include <string>
-
-#include "base/memory/scoped_ptr.h"
-#include "remoting/host/host_exit_codes.h"
-#include "remoting/host/host_status_sender.h"
-#include "remoting/protocol/transport.h"
-
-namespace buzz {
-class XmlElement;
-}  // namespace buzz
-
-namespace remoting {
-
-class ServerLogEntry {
- public:
-  // The mode of a connection.
-  enum Mode {
-    IT2ME,
-    ME2ME
-  };
-
-  // Constructs a log stanza. The caller should add one or more log entry
-  // stanzas as children of this stanza, before sending the log stanza to
-  // the remoting bot.
-  static scoped_ptr<buzz::XmlElement> MakeStanza();
-
-  // Constructs a log entry for a session state change.
-  // Currently this is either connection or disconnection.
-  static scoped_ptr<ServerLogEntry> MakeForSessionStateChange(bool connection);
-
-  // Constructs a log entry for a heartbeat.
-  static scoped_ptr<ServerLogEntry> MakeForHeartbeat();
-
-  // Constructs a log entry for a host status message.
-  static scoped_ptr<ServerLogEntry> MakeForHostStatus(
-      HostStatusSender::HostStatus host_status, HostExitCodes exit_code);
-
-  ~ServerLogEntry();
-
-  // Adds fields describing the host to this log entry.
-  void AddHostFields();
-
-  // Adds a field describing the mode of a connection to this log entry.
-  void AddModeField(Mode mode);
-
-  // Adds a field describing connection type (direct/stun/relay).
-  void AddConnectionTypeField(protocol::TransportRoute::RouteType type);
-
-  // Converts this object to an XML stanza.
-  scoped_ptr<buzz::XmlElement> ToStanza() const;
-
- private:
-  typedef std::map<std::string, std::string> ValuesMap;
-
-  ServerLogEntry();
-  void Set(const std::string& key, const std::string& value);
-
-  static const char* GetValueSessionState(bool connected);
-  static const char* GetValueMode(Mode mode);
-
-  ValuesMap values_map_;
-};
-
-}  // namespace remoting
-
-#endif
diff --git a/remoting/host/server_log_entry_host.cc b/remoting/host/server_log_entry_host.cc
new file mode 100644
index 0000000..069dc2d
--- /dev/null
+++ b/remoting/host/server_log_entry_host.cc
@@ -0,0 +1,107 @@
+// Copyright 2014 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/host/server_log_entry_host.h"
+
+#include "base/strings/stringize_macros.h"
+#include "base/sys_info.h"
+#include "remoting/jingle_glue/server_log_entry.h"
+
+using base::SysInfo;
+
+namespace remoting {
+
+namespace {
+const char kValueEventNameSessionState[] = "session-state";
+const char kValueEventNameHeartbeat[] = "heartbeat";
+const char kValueEventNameHostStatus[] = "host-status";
+
+const char kValueRoleHost[] = "host";
+
+const char kKeySessionState[] = "session-state";
+const char kValueSessionStateConnected[] = "connected";
+const char kValueSessionStateClosed[] = "closed";
+
+const char kStatusName[] = "status";
+const char kExitCodeName[] = "exit-code";
+
+const char kKeyOsName[] = "os-name";
+
+#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS)
+const char kKeyOsVersion[] = "os-version";
+#endif
+
+const char kKeyHostVersion[] = "host-version";
+
+const char kKeyConnectionType[] = "connection-type";
+
+const char* GetValueSessionState(bool connected) {
+  return connected ? kValueSessionStateConnected : kValueSessionStateClosed;
+}
+
+}  // namespace
+
+scoped_ptr<ServerLogEntry> MakeLogEntryForSessionStateChange(
+    bool connected) {
+  scoped_ptr<ServerLogEntry> entry(new ServerLogEntry());
+  entry->AddRoleField(kValueRoleHost);
+  entry->AddEventNameField(kValueEventNameSessionState);
+  entry->Set(kKeySessionState, GetValueSessionState(connected));
+  return entry.Pass();
+}
+
+scoped_ptr<ServerLogEntry> MakeLogEntryForHeartbeat() {
+  scoped_ptr<ServerLogEntry> entry(new ServerLogEntry());
+  entry->AddRoleField(kValueRoleHost);
+  entry->AddEventNameField(kValueEventNameHeartbeat);
+  return entry.Pass();
+}
+
+// static
+scoped_ptr<ServerLogEntry> MakeLogEntryForHostStatus(
+    HostStatusSender::HostStatus host_status, HostExitCodes exit_code) {
+  scoped_ptr<ServerLogEntry> entry(new ServerLogEntry());
+  entry->AddRoleField(kValueRoleHost);
+  entry->AddEventNameField(kValueEventNameHostStatus);
+  entry->Set(kStatusName, HostStatusSender::HostStatusToString(host_status));
+  if (host_status == HostStatusSender::OFFLINE)
+    entry->Set(kExitCodeName, ExitCodeToString(exit_code));
+  return entry.Pass();
+}
+
+void AddHostFieldsToLogEntry(ServerLogEntry* entry) {
+#if defined(OS_WIN)
+  entry->Set(kKeyOsName, "Windows");
+#elif defined(OS_MACOSX)
+  entry->Set(kKeyOsName, "Mac");
+#elif defined(OS_CHROMEOS)
+  entry->Set(kKeyOsName, "ChromeOS");
+#elif defined(OS_LINUX)
+  entry->Set(kKeyOsName, "Linux");
+#endif
+
+  // SysInfo::OperatingSystemVersionNumbers is only defined for the following
+  // OSes: see base/sys_info_unittest.cc.
+#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS)
+  std::stringstream os_version;
+  int32 os_major_version = 0;
+  int32 os_minor_version = 0;
+  int32 os_bugfix_version = 0;
+  SysInfo::OperatingSystemVersionNumbers(&os_major_version, &os_minor_version,
+                                         &os_bugfix_version);
+  os_version << os_major_version << "." << os_minor_version << "."
+             << os_bugfix_version;
+  entry->Set(kKeyOsVersion, os_version.str());
+#endif
+
+  entry->Set(kKeyHostVersion, STRINGIZE(VERSION));
+  entry->AddCpuField();
+};
+
+void AddConnectionTypeToLogEntry(ServerLogEntry* entry,
+    protocol::TransportRoute::RouteType type) {
+  entry->Set(kKeyConnectionType, protocol::TransportRoute::GetTypeString(type));
+}
+
+}  // namespace remoting
diff --git a/remoting/host/server_log_entry_host.h b/remoting/host/server_log_entry_host.h
new file mode 100644
index 0000000..efe7ad3
--- /dev/null
+++ b/remoting/host/server_log_entry_host.h
@@ -0,0 +1,37 @@
+// Copyright 2014 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.
+
+#ifndef REMOTING_HOST_SERVER_LOG_ENTRY_HOST_H_
+#define REMOTING_HOST_SERVER_LOG_ENTRY_HOST_H_
+
+#include "remoting/host/host_exit_codes.h"
+#include "remoting/host/host_status_sender.h"
+#include "remoting/protocol/transport.h"
+
+namespace remoting {
+
+class ServerLogEntry;
+
+// Constructs a log entry for a session state change.
+// Currently this is either connection or disconnection.
+scoped_ptr<ServerLogEntry> MakeLogEntryForSessionStateChange(
+    bool connected);
+
+// Constructs a log entry for a heartbeat.
+scoped_ptr<ServerLogEntry> MakeLogEntryForHeartbeat();
+
+// Constructs a log entry for a host status message.
+scoped_ptr<ServerLogEntry> MakeLogEntryForHostStatus(
+    HostStatusSender::HostStatus host_status, HostExitCodes exit_code);
+
+// Adds fields describing the host to this log entry.
+void AddHostFieldsToLogEntry(ServerLogEntry* entry);
+
+// Adds a field describing connection type (direct/stun/relay).
+void AddConnectionTypeToLogEntry(ServerLogEntry* entry,
+                                 protocol::TransportRoute::RouteType type);
+
+}  // namespace remoting
+
+#endif  // REMOTING_HOST_SERVER_LOG_ENTRY_HOST_H_
diff --git a/remoting/host/server_log_entry_host_unittest.cc b/remoting/host/server_log_entry_host_unittest.cc
new file mode 100644
index 0000000..603e2ee
--- /dev/null
+++ b/remoting/host/server_log_entry_host_unittest.cc
@@ -0,0 +1,101 @@
+// Copyright 2014 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 "base/memory/scoped_ptr.h"
+#include "base/strings/stringize_macros.h"
+#include "remoting/host/server_log_entry_host.h"
+#include "remoting/jingle_glue/server_log_entry.h"
+#include "remoting/jingle_glue/server_log_entry_unittest.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/libjingle/source/talk/xmllite/xmlelement.h"
+
+using buzz::XmlAttr;
+using buzz::XmlElement;
+
+namespace remoting {
+
+TEST(ServerLogEntryHostTest, MakeForSessionStateChange) {
+  scoped_ptr<ServerLogEntry> entry(MakeLogEntryForSessionStateChange(true));
+  scoped_ptr<XmlElement> stanza = entry->ToStanza();
+  std::string error;
+  std::map<std::string, std::string> key_value_pairs;
+  key_value_pairs["role"] = "host";
+  key_value_pairs["event-name"] = "session-state";
+  key_value_pairs["session-state"] = "connected";
+  std::set<std::string> keys;
+  ASSERT_TRUE(VerifyStanza(key_value_pairs, keys, stanza.get(), &error))
+      << error;
+}
+
+TEST(ServerLogEntryHostTest, MakeForHeartbeat) {
+  scoped_ptr<ServerLogEntry> entry(MakeLogEntryForHeartbeat());
+  scoped_ptr<XmlElement> stanza = entry->ToStanza();
+  std::string error;
+  std::map<std::string, std::string> key_value_pairs;
+  key_value_pairs["role"] = "host";
+  key_value_pairs["event-name"] = "heartbeat";
+  std::set<std::string> keys;
+  ASSERT_TRUE(VerifyStanza(key_value_pairs, keys, stanza.get(), &error))
+      << error;
+}
+
+TEST(ServerLogEntryHostTest, AddHostFields) {
+  scoped_ptr<ServerLogEntry> entry(MakeLogEntryForSessionStateChange(true));
+  AddHostFieldsToLogEntry(entry.get());
+  scoped_ptr<XmlElement> stanza = entry->ToStanza();
+  std::string error;
+  std::map<std::string, std::string> key_value_pairs;
+  key_value_pairs["role"] = "host";
+  key_value_pairs["event-name"] = "session-state";
+  key_value_pairs["session-state"] = "connected";
+  std::set<std::string> keys;
+  keys.insert("cpu");
+#if defined(OS_WIN)
+  key_value_pairs["os-name"] = "Windows";
+  keys.insert("os-version");
+#elif defined(OS_MACOSX)
+  key_value_pairs["os-name"] = "Mac";
+  keys.insert("os-version");
+#elif defined(OS_CHROMEOS)
+  key_value_pairs["os-name"] = "ChromeOS";
+  keys.insert("os-version");
+#elif defined(OS_LINUX)
+  key_value_pairs["os-name"] = "Linux";
+#endif
+  key_value_pairs["host-version"] = STRINGIZE(VERSION);
+  ASSERT_TRUE(VerifyStanza(key_value_pairs, keys, stanza.get(), &error)) <<
+      error;
+}
+
+TEST(ServerLogEntryHostTest, AddModeField1) {
+  scoped_ptr<ServerLogEntry> entry(MakeLogEntryForSessionStateChange(true));
+  entry->AddModeField(ServerLogEntry::IT2ME);
+  scoped_ptr<XmlElement> stanza = entry->ToStanza();
+  std::string error;
+  std::map<std::string, std::string> key_value_pairs;
+  key_value_pairs["role"] = "host";
+  key_value_pairs["event-name"] = "session-state";
+  key_value_pairs["session-state"] = "connected";
+  key_value_pairs["mode"] = "it2me";
+  std::set<std::string> keys;
+  ASSERT_TRUE(VerifyStanza(key_value_pairs, keys, stanza.get(), &error)) <<
+      error;
+}
+
+TEST(ServerLogEntryHostTest, AddModeField2) {
+  scoped_ptr<ServerLogEntry> entry(MakeLogEntryForSessionStateChange(true));
+  entry->AddModeField(ServerLogEntry::ME2ME);
+  scoped_ptr<XmlElement> stanza = entry->ToStanza();
+  std::string error;
+  std::map<std::string, std::string> key_value_pairs;
+  key_value_pairs["role"] = "host";
+  key_value_pairs["event-name"] = "session-state";
+  key_value_pairs["session-state"] = "connected";
+  key_value_pairs["mode"] = "me2me";
+  std::set<std::string> keys;
+  ASSERT_TRUE(VerifyStanza(key_value_pairs, keys, stanza.get(), &error)) <<
+      error;
+}
+
+}  // namespace remoting
diff --git a/remoting/host/server_log_entry_unittest.cc b/remoting/host/server_log_entry_unittest.cc
deleted file mode 100644
index e2e7853..0000000
--- a/remoting/host/server_log_entry_unittest.cc
+++ /dev/null
@@ -1,151 +0,0 @@
-// Copyright (c) 2012 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 "base/memory/scoped_ptr.h"
-#include "base/strings/stringize_macros.h"
-#include "remoting/host/server_log_entry.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/libjingle/source/talk/xmllite/xmlelement.h"
-
-using buzz::XmlAttr;
-using buzz::XmlElement;
-
-namespace remoting {
-
-class ServerLogEntryTest : public testing::Test {
- protected:
-  // Verifies a logging stanza.
-  // |keyValuePairs| lists the keys that must have specified values, and |keys|
-  // lists the keys that must be present, but may have arbitrary values.
-  // There must be no other keys.
-  static bool VerifyStanza(
-      const std::map<std::string, std::string>& key_value_pairs,
-      const std::set<std::string> keys,
-      const XmlElement* elem,
-      std::string* error) {
-    int attrCount = 0;
-    for (const XmlAttr* attr = elem->FirstAttr(); attr != NULL;
-         attr = attr->NextAttr(), attrCount++) {
-      if (attr->Name().Namespace().length() != 0) {
-        *error = "attribute has non-empty namespace " +
-            attr->Name().Namespace();
-        return false;
-      }
-      const std::string& key = attr->Name().LocalPart();
-      const std::string& value = attr->Value();
-      std::map<std::string, std::string>::const_iterator iter =
-          key_value_pairs.find(key);
-      if (iter == key_value_pairs.end()) {
-        if (keys.find(key) == keys.end()) {
-          *error = "unexpected attribute " + key;
-          return false;
-        }
-      } else {
-        if (iter->second != value) {
-          *error = "attribute " + key + " has value " + iter->second +
-              ": expected " + value;
-          return false;
-        }
-      }
-    }
-    int attr_count_expected = key_value_pairs.size() + keys.size();
-    if (attrCount != attr_count_expected) {
-      std::stringstream s;
-      s << "stanza has " << attrCount << " keys: expected "
-        << attr_count_expected;
-      *error = s.str();
-      return false;
-    }
-    return true;
-  }
-};
-
-TEST_F(ServerLogEntryTest, MakeForSessionStateChange) {
-  scoped_ptr<ServerLogEntry> entry(
-      ServerLogEntry::MakeForSessionStateChange(true));
-  scoped_ptr<XmlElement> stanza = entry->ToStanza();
-  std::string error;
-  std::map<std::string, std::string> key_value_pairs;
-  key_value_pairs["role"] = "host";
-  key_value_pairs["event-name"] = "session-state";
-  key_value_pairs["session-state"] = "connected";
-  std::set<std::string> keys;
-  ASSERT_TRUE(VerifyStanza(key_value_pairs, keys, stanza.get(), &error))
-      << error;
-}
-
-TEST_F(ServerLogEntryTest, MakeForHeartbeat) {
-  scoped_ptr<ServerLogEntry> entry(ServerLogEntry::MakeForHeartbeat());
-  scoped_ptr<XmlElement> stanza = entry->ToStanza();
-  std::string error;
-  std::map<std::string, std::string> key_value_pairs;
-  key_value_pairs["role"] = "host";
-  key_value_pairs["event-name"] = "heartbeat";
-  std::set<std::string> keys;
-  ASSERT_TRUE(VerifyStanza(key_value_pairs, keys, stanza.get(), &error))
-      << error;
-}
-
-TEST_F(ServerLogEntryTest, AddHostFields) {
-  scoped_ptr<ServerLogEntry> entry(
-      ServerLogEntry::MakeForSessionStateChange(true));
-  entry->AddHostFields();
-  scoped_ptr<XmlElement> stanza = entry->ToStanza();
-  std::string error;
-  std::map<std::string, std::string> key_value_pairs;
-  key_value_pairs["role"] = "host";
-  key_value_pairs["event-name"] = "session-state";
-  key_value_pairs["session-state"] = "connected";
-  std::set<std::string> keys;
-  keys.insert("cpu");
-#if defined(OS_WIN)
-  key_value_pairs["os-name"] = "Windows";
-  keys.insert("os-version");
-#elif defined(OS_MACOSX)
-  key_value_pairs["os-name"] = "Mac";
-  keys.insert("os-version");
-#elif defined(OS_CHROMEOS)
-  key_value_pairs["os-name"] = "ChromeOS";
-  keys.insert("os-version");
-#elif defined(OS_LINUX)
-  key_value_pairs["os-name"] = "Linux";
-#endif
-  key_value_pairs["host-version"] = STRINGIZE(VERSION);
-  ASSERT_TRUE(VerifyStanza(key_value_pairs, keys, stanza.get(), &error)) <<
-      error;
-}
-
-TEST_F(ServerLogEntryTest, AddModeField1) {
-  scoped_ptr<ServerLogEntry> entry(
-      ServerLogEntry::MakeForSessionStateChange(true));
-  entry->AddModeField(ServerLogEntry::IT2ME);
-  scoped_ptr<XmlElement> stanza = entry->ToStanza();
-  std::string error;
-  std::map<std::string, std::string> key_value_pairs;
-  key_value_pairs["role"] = "host";
-  key_value_pairs["event-name"] = "session-state";
-  key_value_pairs["session-state"] = "connected";
-  key_value_pairs["mode"] = "it2me";
-  std::set<std::string> keys;
-  ASSERT_TRUE(VerifyStanza(key_value_pairs, keys, stanza.get(), &error)) <<
-      error;
-}
-
-TEST_F(ServerLogEntryTest, AddModeField2) {
-  scoped_ptr<ServerLogEntry> entry(
-      ServerLogEntry::MakeForSessionStateChange(true));
-  entry->AddModeField(ServerLogEntry::ME2ME);
-  scoped_ptr<XmlElement> stanza = entry->ToStanza();
-  std::string error;
-  std::map<std::string, std::string> key_value_pairs;
-  key_value_pairs["role"] = "host";
-  key_value_pairs["event-name"] = "session-state";
-  key_value_pairs["session-state"] = "connected";
-  key_value_pairs["mode"] = "me2me";
-  std::set<std::string> keys;
-  ASSERT_TRUE(VerifyStanza(key_value_pairs, keys, stanza.get(), &error)) <<
-      error;
-}
-
-}  // namespace remoting
diff --git a/remoting/jingle_glue/server_log_entry.cc b/remoting/jingle_glue/server_log_entry.cc
new file mode 100644
index 0000000..e90721b
--- /dev/null
+++ b/remoting/jingle_glue/server_log_entry.cc
@@ -0,0 +1,88 @@
+// Copyright 2014 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/jingle_glue/server_log_entry.h"
+
+#include "base/logging.h"
+#include "base/sys_info.h"
+#include "remoting/base/constants.h"
+#include "third_party/libjingle/source/talk/xmllite/xmlelement.h"
+
+using base::SysInfo;
+using buzz::QName;
+using buzz::XmlElement;
+
+namespace remoting {
+
+namespace {
+
+const char kLogCommand[] = "log";
+const char kLogEntry[] = "entry";
+
+const char kKeyEventName[] = "event-name";
+
+const char kKeyRole[] = "role";
+
+const char kKeyMode[] = "mode";
+const char kValueModeIt2Me[] = "it2me";
+const char kValueModeMe2Me[] = "me2me";
+
+const char kKeyCpu[] = "cpu";
+
+}  // namespace
+
+ServerLogEntry::ServerLogEntry() {
+}
+
+ServerLogEntry::~ServerLogEntry() {
+}
+
+void ServerLogEntry::Set(const std::string& key, const std::string& value) {
+  values_map_[key] = value;
+}
+
+void ServerLogEntry::AddCpuField() {
+  Set(kKeyCpu, SysInfo::OperatingSystemArchitecture());
+}
+
+void ServerLogEntry::AddModeField(ServerLogEntry::Mode mode) {
+  const char* mode_value = NULL;
+  switch (mode) {
+    case IT2ME:
+      mode_value = kValueModeIt2Me;
+      break;
+    case ME2ME:
+      mode_value = kValueModeMe2Me;
+      break;
+    default:
+      NOTREACHED();
+  }
+  Set(kKeyMode, mode_value);
+}
+
+void ServerLogEntry::AddRoleField(const char* role) {
+  Set(kKeyRole, role);
+}
+
+void ServerLogEntry::AddEventNameField(const char* name) {
+  Set(kKeyEventName, name);
+}
+
+// static
+scoped_ptr<XmlElement> ServerLogEntry::MakeStanza() {
+  return scoped_ptr<XmlElement>(
+      new XmlElement(QName(kChromotingXmlNamespace, kLogCommand)));
+}
+
+scoped_ptr<XmlElement> ServerLogEntry::ToStanza() const {
+  scoped_ptr<XmlElement> stanza(new XmlElement(QName(
+      kChromotingXmlNamespace, kLogEntry)));
+  ValuesMap::const_iterator iter;
+  for (iter = values_map_.begin(); iter != values_map_.end(); ++iter) {
+    stanza->AddAttr(QName(std::string(), iter->first), iter->second);
+  }
+  return stanza.Pass();
+}
+
+}  // namespace remoting
diff --git a/remoting/jingle_glue/server_log_entry.h b/remoting/jingle_glue/server_log_entry.h
new file mode 100644
index 0000000..ab2314df
--- /dev/null
+++ b/remoting/jingle_glue/server_log_entry.h
@@ -0,0 +1,64 @@
+// Copyright 2014 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.
+
+#ifndef REMOTING_JINGLE_GLUE_SERVER_LOG_ENTRY_H_
+#define REMOTING_JINGLE_GLUE_SERVER_LOG_ENTRY_H_
+
+#include <map>
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+
+namespace buzz {
+class XmlElement;
+}  // namespace buzz
+
+namespace remoting {
+
+// Utility class for building log entries to send to the remoting bot. This is
+// a wrapper around a key/value map and is copyable so it can be used in STL
+// containers.
+class ServerLogEntry {
+ public:
+  // The mode of a connection.
+  enum Mode {
+    IT2ME,
+    ME2ME
+  };
+
+  ServerLogEntry();
+  ~ServerLogEntry();
+
+  // Sets an arbitrary key/value entry.
+  void Set(const std::string& key, const std::string& value);
+
+  // Adds a field describing the CPU type of the platform.
+  void AddCpuField();
+
+  // Adds a field describing the mode of a connection to this log entry.
+  void AddModeField(Mode mode);
+
+  // Adds a field describing the role (client/host).
+  void AddRoleField(const char* role);
+
+  // Adds a field describing the type of log entry.
+  void AddEventNameField(const char* name);
+
+  // Constructs a log stanza. The caller should add one or more log entry
+  // stanzas as children of this stanza, before sending the log stanza to
+  // the remoting bot.
+  static scoped_ptr<buzz::XmlElement> MakeStanza();
+
+  // Converts this object to an XML stanza.
+  scoped_ptr<buzz::XmlElement> ToStanza() const;
+
+ private:
+  typedef std::map<std::string, std::string> ValuesMap;
+
+  ValuesMap values_map_;
+};
+
+}  // namespace remoting
+
+#endif  // REMOTING_JINGLE_GLUE_SERVER_LOG_ENTRY_H_
diff --git a/remoting/jingle_glue/server_log_entry_unittest.cc b/remoting/jingle_glue/server_log_entry_unittest.cc
new file mode 100644
index 0000000..b92feb20
--- /dev/null
+++ b/remoting/jingle_glue/server_log_entry_unittest.cc
@@ -0,0 +1,57 @@
+// Copyright 2014 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/jingle_glue/server_log_entry_unittest.h"
+
+#include <sstream>
+
+#include "third_party/libjingle/source/talk/xmllite/xmlelement.h"
+
+using buzz::XmlAttr;
+using buzz::XmlElement;
+
+namespace remoting {
+
+bool VerifyStanza(
+    const std::map<std::string, std::string>& key_value_pairs,
+    const std::set<std::string> keys,
+    const XmlElement* elem,
+    std::string* error) {
+  int attrCount = 0;
+  for (const XmlAttr* attr = elem->FirstAttr(); attr != NULL;
+       attr = attr->NextAttr(), attrCount++) {
+    if (attr->Name().Namespace().length() != 0) {
+      *error = "attribute has non-empty namespace " +
+          attr->Name().Namespace();
+      return false;
+    }
+    const std::string& key = attr->Name().LocalPart();
+    const std::string& value = attr->Value();
+    std::map<std::string, std::string>::const_iterator iter =
+        key_value_pairs.find(key);
+    if (iter == key_value_pairs.end()) {
+      if (keys.find(key) == keys.end()) {
+        *error = "unexpected attribute " + key;
+        return false;
+      }
+    } else {
+      if (iter->second != value) {
+        *error = "attribute " + key + " has value " + iter->second +
+            ": expected " + value;
+        return false;
+      }
+    }
+  }
+  int attr_count_expected = key_value_pairs.size() + keys.size();
+  if (attrCount != attr_count_expected) {
+    std::stringstream s;
+    s << "stanza has " << attrCount << " keys: expected "
+      << attr_count_expected;
+    *error = s.str();
+    return false;
+  }
+  return true;
+}
+
+}  // namespace remoting
diff --git a/remoting/jingle_glue/server_log_entry_unittest.h b/remoting/jingle_glue/server_log_entry_unittest.h
new file mode 100644
index 0000000..523628b
--- /dev/null
+++ b/remoting/jingle_glue/server_log_entry_unittest.h
@@ -0,0 +1,25 @@
+// Copyright 2014 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 <map>
+#include <set>
+#include <string>
+
+namespace buzz {
+class XmlElement;
+}  // namespace buzz
+
+namespace remoting {
+
+// Verifies a logging stanza.
+// |keyValuePairs| lists the keys that must have specified values, and |keys|
+// lists the keys that must be present, but may have arbitrary values.
+// There must be no other keys.
+bool VerifyStanza(
+    const std::map<std::string, std::string>& key_value_pairs,
+    const std::set<std::string> keys,
+    const buzz::XmlElement* elem,
+    std::string* error);
+
+}  // namespace remoting
diff --git a/remoting/remoting_host.gypi b/remoting/remoting_host.gypi
index ff1996f7..dc4cdc6 100644
--- a/remoting/remoting_host.gypi
+++ b/remoting/remoting_host.gypi
@@ -221,8 +221,8 @@
             'host/screen_controls.h',
             'host/screen_resolution.cc',
             'host/screen_resolution.h',
-            'host/server_log_entry.cc',
-            'host/server_log_entry.h',
+            'host/server_log_entry_host.cc',
+            'host/server_log_entry_host.h',
             'host/service_urls.cc',
             'host/service_urls.h',
             'host/session_manager_factory.cc',
diff --git a/remoting/remoting_srcs.gypi b/remoting/remoting_srcs.gypi
index d4c4a84..25785e20 100644
--- a/remoting/remoting_srcs.gypi
+++ b/remoting/remoting_srcs.gypi
@@ -75,6 +75,8 @@
       'jingle_glue/jingle_info_request.h',
       'jingle_glue/network_settings.cc',
       'jingle_glue/network_settings.h',
+      'jingle_glue/server_log_entry.cc',
+      'jingle_glue/server_log_entry.h',
       'jingle_glue/signal_strategy.h',
       'jingle_glue/xmpp_signal_strategy.cc',
       'jingle_glue/xmpp_signal_strategy.h',
@@ -212,8 +214,8 @@
       'client/key_event_mapper.h',
       'client/log_to_server.cc',
       'client/log_to_server.h',
-      'client/server_log_entry.cc',
-      'client/server_log_entry.h',
+      'client/server_log_entry_client.cc',
+      'client/server_log_entry_client.h',
       'client/software_video_renderer.cc',
       'client/software_video_renderer.h',
       'client/video_renderer.h',
diff --git a/remoting/remoting_test.gypi b/remoting/remoting_test.gypi
index bb09d98..0e8ca025 100644
--- a/remoting/remoting_test.gypi
+++ b/remoting/remoting_test.gypi
@@ -56,6 +56,7 @@
         'base/util_unittest.cc',
         'client/audio_player_unittest.cc',
         'client/key_event_mapper_unittest.cc',
+	'client/server_log_entry_client_unittest.cc',
         'client/plugin/normalizing_input_filter_cros_unittest.cc',
         'client/plugin/normalizing_input_filter_mac_unittest.cc',
         'codec/audio_encoder_opus_unittest.cc',
@@ -100,12 +101,12 @@
         'host/register_support_host_request_unittest.cc',
         'host/remote_input_filter_unittest.cc',
         'host/resizing_host_observer_unittest.cc',
-        'host/setup/me2me_native_messaging_host.cc',
-        'host/setup/me2me_native_messaging_host.h',
         'host/screen_capturer_fake.cc',
         'host/screen_capturer_fake.h',
         'host/screen_resolution_unittest.cc',
-        'host/server_log_entry_unittest.cc',
+	'host/server_log_entry_host_unittest.cc',
+        'host/setup/me2me_native_messaging_host.cc',
+        'host/setup/me2me_native_messaging_host.h',
         'host/setup/me2me_native_messaging_host_unittest.cc',
         'host/setup/oauth_helper_unittest.cc',
         'host/setup/pin_validator_unittest.cc',
@@ -123,6 +124,8 @@
         'jingle_glue/mock_objects.cc',
         'jingle_glue/mock_objects.h',
         'jingle_glue/network_settings_unittest.cc',
+	'jingle_glue/server_log_entry_unittest.cc',
+	'jingle_glue/server_log_entry_unittest.h',
         'protocol/authenticator_test_base.cc',
         'protocol/authenticator_test_base.h',
         'protocol/buffered_socket_writer_unittest.cc',