From 41462b6dc8bb6792e4e46c7d9085b8d45e1ec81d Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sun, 13 Sep 2009 17:09:40 +0000 Subject: [PATCH] Add documentation for UsageDataCollector. git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@4922 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61 --- .../UsageDataCollector/AnalyticsMonitor.cs | 18 +++++-- .../UsageDataCollector/UsageDataMessage.cs | 54 +++++++++++++++++++ .../UsageDataSessionWriter.cs | 49 ++++++++++++++--- .../UsageDataCollector/UsageDataUploader.cs | 10 ++++ 4 files changed, 120 insertions(+), 11 deletions(-) diff --git a/src/AddIns/Misc/UsageDataCollector/AnalyticsMonitor.cs b/src/AddIns/Misc/UsageDataCollector/AnalyticsMonitor.cs index 1a75258a00..b1176c7855 100644 --- a/src/AddIns/Misc/UsageDataCollector/AnalyticsMonitor.cs +++ b/src/AddIns/Misc/UsageDataCollector/AnalyticsMonitor.cs @@ -27,6 +27,9 @@ namespace ICSharpCode.UsageDataCollector } } + /// + /// Allows to enable/disable the usage data monitoring. + /// public static bool Enabled { get { return string.Equals(PropertyService.Get("ICSharpCode.UsageDataCollector.Enabled"), bool.TrueString, StringComparison.OrdinalIgnoreCase); @@ -52,7 +55,7 @@ namespace ICSharpCode.UsageDataCollector readonly object lockObj = new object(); string dbFileName; - AnalyticsSessionWriter session; + UsageDataSessionWriter session; private AnalyticsMonitor() { @@ -63,13 +66,17 @@ namespace ICSharpCode.UsageDataCollector SharpDevelop.Gui.WorkbenchSingleton.WorkbenchUnloaded += delegate { CloseSession(); }; } + /// + /// Opens the database connection, updates the database if required. + /// Will start an upload to the server, if required. + /// public void OpenSession() { bool sessionOpened = false; lock (lockObj) { if (session == null) { try { - session = new AnalyticsSessionWriter(dbFileName); + session = new UsageDataSessionWriter(dbFileName); session.AddEnvironmentData("appVersion", RevisionClass.FullVersion); session.AddEnvironmentData("language", ResourceService.Language); sessionOpened = true; @@ -79,7 +86,7 @@ namespace ICSharpCode.UsageDataCollector // upgrade database by deleting the old one TryDeleteDatabase(); try { - session = new AnalyticsSessionWriter(dbFileName); + session = new UsageDataSessionWriter(dbFileName); } catch (IncompatibleDatabaseException ex2) { LoggingService.Warn("AnalyticsMonitor: Could upgrade database: " + ex2.Message); } @@ -95,6 +102,11 @@ namespace ICSharpCode.UsageDataCollector } } + /// + /// Retrieves all stored data as XML text. + /// + /// The database version is not compatible with this + /// version of the AnalyticsSessionWriter. public string GetTextForStoredData() { UsageDataUploader uploader = new UsageDataUploader(dbFileName); diff --git a/src/AddIns/Misc/UsageDataCollector/UsageDataMessage.cs b/src/AddIns/Misc/UsageDataCollector/UsageDataMessage.cs index a639de2c40..c67520149d 100644 --- a/src/AddIns/Misc/UsageDataCollector/UsageDataMessage.cs +++ b/src/AddIns/Misc/UsageDataCollector/UsageDataMessage.cs @@ -11,9 +11,16 @@ using System.Runtime.Serialization; namespace ICSharpCode.UsageDataCollector { + /// + /// Represents a message used to send data to the server. + /// [DataContract] sealed class UsageDataMessage { + /// + /// Unique identifier per user. + /// We need this to distinguish between 1 user using a feature 100 times, or 100 users using a feature 1 time. + /// [DataMember] public Guid UserID; @@ -33,12 +40,23 @@ namespace ICSharpCode.UsageDataCollector [DataContract] sealed class UsageDataSession { + /// + /// ID of the session, usually unique per user (unless the user restores a backup of the database). + /// Could be used to detect duplicate uploads. + /// [DataMember] public long SessionID; + /// + /// Timestamp when the session was started. + /// [DataMember] public DateTime StartTime; + /// + /// Timestamp when the session finished. + /// Nullable because the end time might not be recorded if the application crashed. + /// [DataMember] public DateTime? EndTime; @@ -52,6 +70,11 @@ namespace ICSharpCode.UsageDataCollector public List Exceptions = new List(); } + /// + /// A property storing a value about the environment (App version, OS Version etc.). + /// These are stored per session because the user might use different versions of an application in parallel, + /// or have multiple OS installations pointing to the same database (e.g. when SharpDevelop is used on an USB stick). + /// [DataContract] sealed class UsageDataEnvironmentProperty { @@ -62,31 +85,62 @@ namespace ICSharpCode.UsageDataCollector public string Value; } + /// + /// Represents a feature being used. + /// [DataContract] sealed class UsageDataFeatureUse { + /// + /// The time when the feature was used. + /// [DataMember] public DateTime Time; + /// + /// End time of the feature use. Nullable because for some features, the no time span will be recorded. + /// Also, for features where the time span usually is recorded, crashes might cause the end time to be missing + /// in some cases. + /// [DataMember] public DateTime? EndTime; + /// + /// Name of the feature. + /// [DataMember] public string FeatureName; + /// + /// How the feature was activated (Menu, Toolbar, Shortcut, etc.) + /// [DataMember] public string ActivationMethod; } + /// + /// Represents an exception that was recorded. + /// Only ExceptionType and StackTrace are available because those don't contain personal information. + /// The exception message might e.g. contain names of the file the user is working on. + /// [DataContract] sealed class UsageDataException { + /// + /// The time when the exception occurred. + /// [DataMember] public DateTime Time; + /// + /// The fully qualified typename of the exception. + /// [DataMember] public string ExceptionType; + /// + /// The stack trace of the exception. The 'at'/'in' words are potentially localized. + /// [DataMember] public string StackTrace; } diff --git a/src/AddIns/Misc/UsageDataCollector/UsageDataSessionWriter.cs b/src/AddIns/Misc/UsageDataCollector/UsageDataSessionWriter.cs index e0d56b4746..6844e59829 100644 --- a/src/AddIns/Misc/UsageDataCollector/UsageDataSessionWriter.cs +++ b/src/AddIns/Misc/UsageDataCollector/UsageDataSessionWriter.cs @@ -12,9 +12,13 @@ using System.Runtime.Serialization; namespace ICSharpCode.UsageDataCollector { /// - /// Creates an analytics session. + /// Creates a usage data session. + /// + /// Instance methods on this class are not thread-safe. If you are using it in a multi-threaded application, + /// you should consider writing your own wrapper class that takes care of the thread-safety. + /// In SharpDevelop, this is done by the AnalyticsMonitor class. /// - public class AnalyticsSessionWriter : IDisposable + public class UsageDataSessionWriter : IDisposable { SQLiteConnection connection; long sessionID; @@ -22,8 +26,9 @@ namespace ICSharpCode.UsageDataCollector /// /// Opens/Creates the database and starts writing a new session to it. /// - /// - public AnalyticsSessionWriter(string databaseFileName) + /// The database version is not compatible with this + /// version of the AnalyticsSessionWriter. + public UsageDataSessionWriter(string databaseFileName) { SQLiteConnectionStringBuilder conn = new SQLiteConnectionStringBuilder(); conn.Add("Data Source", databaseFileName); @@ -129,6 +134,11 @@ namespace ICSharpCode.UsageDataCollector } } + /// + /// Adds environment data to the current session. + /// + /// Name of the data entry. + /// Value of the data entry. public void AddEnvironmentData(string name, string value) { using (SQLiteCommand cmd = this.connection.CreateCommand()) { @@ -141,6 +151,12 @@ namespace ICSharpCode.UsageDataCollector } } + /// + /// Adds a feature use to the session. + /// + /// Name of the feature. + /// How the feature was activated (Menu, Toolbar, Shortcut, etc.) + /// ID that can be used for public long AddFeatureUse(string featureName, string activationMethod) { long featureRowId; @@ -160,15 +176,24 @@ namespace ICSharpCode.UsageDataCollector return featureRowId; } - public void WriteEndTimeForFeature(long featureID) + /// + /// Marks the end time for a feature use. + /// + /// A value returned from . + public void WriteEndTimeForFeature(long featureUseID) { using (SQLiteCommand cmd = this.connection.CreateCommand()) { cmd.CommandText = "UPDATE FeatureUses SET endTime = datetime('now') WHERE id = ?;"; - cmd.Parameters.Add(new SQLiteParameter { Value = featureID }); + cmd.Parameters.Add(new SQLiteParameter { Value = featureUseID }); cmd.ExecuteNonQuery(); } } + /// + /// Adds an exception report to the session. + /// + /// Full .NET type name of the exception. + /// Stacktrace public void AddException(string exceptionType, string stacktrace) { using (SQLiteCommand cmd = this.connection.CreateCommand()) { @@ -181,10 +206,18 @@ namespace ICSharpCode.UsageDataCollector } } + bool isDisposed; + + /// + /// Closes the session. + /// public void Dispose() { - EndSession(); - connection.Close(); + if (!isDisposed) { + isDisposed = true; + EndSession(); + connection.Dispose(); + } } } diff --git a/src/AddIns/Misc/UsageDataCollector/UsageDataUploader.cs b/src/AddIns/Misc/UsageDataCollector/UsageDataUploader.cs index 813c344187..887107bec5 100644 --- a/src/AddIns/Misc/UsageDataCollector/UsageDataUploader.cs +++ b/src/AddIns/Misc/UsageDataCollector/UsageDataUploader.cs @@ -24,6 +24,9 @@ namespace ICSharpCode.UsageDataCollector { string databaseFileName; + /// + /// Creates a new UsageDataUploader. + /// public UsageDataUploader(string databaseFileName) { this.databaseFileName = databaseFileName; @@ -38,6 +41,11 @@ namespace ICSharpCode.UsageDataCollector return connection; } + /// + /// Retrieves all stored data as XML text. + /// + /// The database version is not compatible with this + /// version of the AnalyticsSessionWriter. public string GetTextForStoredData() { UsageDataMessage message; @@ -60,6 +68,8 @@ namespace ICSharpCode.UsageDataCollector /// /// Starts the upload of the usage data. /// + /// The database version is not compatible with this + /// version of the AnalyticsSessionWriter. public void StartUpload() { UsageDataMessage message;