Browse Source

Add documentation for UsageDataCollector.

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@4922 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Daniel Grunwald 16 years ago
parent
commit
41462b6dc8
  1. 18
      src/AddIns/Misc/UsageDataCollector/AnalyticsMonitor.cs
  2. 54
      src/AddIns/Misc/UsageDataCollector/UsageDataMessage.cs
  3. 49
      src/AddIns/Misc/UsageDataCollector/UsageDataSessionWriter.cs
  4. 10
      src/AddIns/Misc/UsageDataCollector/UsageDataUploader.cs

18
src/AddIns/Misc/UsageDataCollector/AnalyticsMonitor.cs

@ -27,6 +27,9 @@ namespace ICSharpCode.UsageDataCollector @@ -27,6 +27,9 @@ namespace ICSharpCode.UsageDataCollector
}
}
/// <summary>
/// Allows to enable/disable the usage data monitoring.
/// </summary>
public static bool Enabled {
get {
return string.Equals(PropertyService.Get("ICSharpCode.UsageDataCollector.Enabled"), bool.TrueString, StringComparison.OrdinalIgnoreCase);
@ -52,7 +55,7 @@ namespace ICSharpCode.UsageDataCollector @@ -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 @@ -63,13 +66,17 @@ namespace ICSharpCode.UsageDataCollector
SharpDevelop.Gui.WorkbenchSingleton.WorkbenchUnloaded += delegate { CloseSession(); };
}
/// <summary>
/// Opens the database connection, updates the database if required.
/// Will start an upload to the server, if required.
/// </summary>
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 @@ -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 @@ -95,6 +102,11 @@ namespace ICSharpCode.UsageDataCollector
}
}
/// <summary>
/// Retrieves all stored data as XML text.
/// </summary>
/// <exception cref="IncompatibleDatabaseException">The database version is not compatible with this
/// version of the AnalyticsSessionWriter.</exception>
public string GetTextForStoredData()
{
UsageDataUploader uploader = new UsageDataUploader(dbFileName);

54
src/AddIns/Misc/UsageDataCollector/UsageDataMessage.cs

@ -11,9 +11,16 @@ using System.Runtime.Serialization; @@ -11,9 +11,16 @@ using System.Runtime.Serialization;
namespace ICSharpCode.UsageDataCollector
{
/// <summary>
/// Represents a message used to send data to the server.
/// </summary>
[DataContract]
sealed class UsageDataMessage
{
/// <summary>
/// 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.
/// </summary>
[DataMember]
public Guid UserID;
@ -33,12 +40,23 @@ namespace ICSharpCode.UsageDataCollector @@ -33,12 +40,23 @@ namespace ICSharpCode.UsageDataCollector
[DataContract]
sealed class UsageDataSession
{
/// <summary>
/// ID of the session, usually unique per user (unless the user restores a backup of the database).
/// Could be used to detect duplicate uploads.
/// </summary>
[DataMember]
public long SessionID;
/// <summary>
/// Timestamp when the session was started.
/// </summary>
[DataMember]
public DateTime StartTime;
/// <summary>
/// Timestamp when the session finished.
/// Nullable because the end time might not be recorded if the application crashed.
/// </summary>
[DataMember]
public DateTime? EndTime;
@ -52,6 +70,11 @@ namespace ICSharpCode.UsageDataCollector @@ -52,6 +70,11 @@ namespace ICSharpCode.UsageDataCollector
public List<UsageDataException> Exceptions = new List<UsageDataException>();
}
/// <summary>
/// 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).
/// </summary>
[DataContract]
sealed class UsageDataEnvironmentProperty
{
@ -62,31 +85,62 @@ namespace ICSharpCode.UsageDataCollector @@ -62,31 +85,62 @@ namespace ICSharpCode.UsageDataCollector
public string Value;
}
/// <summary>
/// Represents a feature being used.
/// </summary>
[DataContract]
sealed class UsageDataFeatureUse
{
/// <summary>
/// The time when the feature was used.
/// </summary>
[DataMember]
public DateTime Time;
/// <summary>
/// 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.
/// </summary>
[DataMember]
public DateTime? EndTime;
/// <summary>
/// Name of the feature.
/// </summary>
[DataMember]
public string FeatureName;
/// <summary>
/// How the feature was activated (Menu, Toolbar, Shortcut, etc.)
/// </summary>
[DataMember]
public string ActivationMethod;
}
/// <summary>
/// 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.
/// </summary>
[DataContract]
sealed class UsageDataException
{
/// <summary>
/// The time when the exception occurred.
/// </summary>
[DataMember]
public DateTime Time;
/// <summary>
/// The fully qualified typename of the exception.
/// </summary>
[DataMember]
public string ExceptionType;
/// <summary>
/// The stack trace of the exception. The 'at'/'in' words are potentially localized.
/// </summary>
[DataMember]
public string StackTrace;
}

49
src/AddIns/Misc/UsageDataCollector/UsageDataSessionWriter.cs

@ -12,9 +12,13 @@ using System.Runtime.Serialization; @@ -12,9 +12,13 @@ using System.Runtime.Serialization;
namespace ICSharpCode.UsageDataCollector
{
/// <summary>
/// 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.
/// </summary>
public class AnalyticsSessionWriter : IDisposable
public class UsageDataSessionWriter : IDisposable
{
SQLiteConnection connection;
long sessionID;
@ -22,8 +26,9 @@ namespace ICSharpCode.UsageDataCollector @@ -22,8 +26,9 @@ namespace ICSharpCode.UsageDataCollector
/// <summary>
/// Opens/Creates the database and starts writing a new session to it.
/// </summary>
/// <exception cref="IncompatibleDatabaseException"></exception>
public AnalyticsSessionWriter(string databaseFileName)
/// <exception cref="IncompatibleDatabaseException">The database version is not compatible with this
/// version of the AnalyticsSessionWriter.</exception>
public UsageDataSessionWriter(string databaseFileName)
{
SQLiteConnectionStringBuilder conn = new SQLiteConnectionStringBuilder();
conn.Add("Data Source", databaseFileName);
@ -129,6 +134,11 @@ namespace ICSharpCode.UsageDataCollector @@ -129,6 +134,11 @@ namespace ICSharpCode.UsageDataCollector
}
}
/// <summary>
/// Adds environment data to the current session.
/// </summary>
/// <param name="name">Name of the data entry.</param>
/// <param name="value">Value of the data entry.</param>
public void AddEnvironmentData(string name, string value)
{
using (SQLiteCommand cmd = this.connection.CreateCommand()) {
@ -141,6 +151,12 @@ namespace ICSharpCode.UsageDataCollector @@ -141,6 +151,12 @@ namespace ICSharpCode.UsageDataCollector
}
}
/// <summary>
/// Adds a feature use to the session.
/// </summary>
/// <param name="featureName">Name of the feature.</param>
/// <param name="activationMethod">How the feature was activated (Menu, Toolbar, Shortcut, etc.)</param>
/// <returns>ID that can be used for <see cref="WriteEndTimeForFeature"/></returns>
public long AddFeatureUse(string featureName, string activationMethod)
{
long featureRowId;
@ -160,15 +176,24 @@ namespace ICSharpCode.UsageDataCollector @@ -160,15 +176,24 @@ namespace ICSharpCode.UsageDataCollector
return featureRowId;
}
public void WriteEndTimeForFeature(long featureID)
/// <summary>
/// Marks the end time for a feature use.
/// </summary>
/// <param name="featureUseID">A value returned from <see cref="AddFeatureUse"/>.</param>
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();
}
}
/// <summary>
/// Adds an exception report to the session.
/// </summary>
/// <param name="exceptionType">Full .NET type name of the exception.</param>
/// <param name="stacktrace">Stacktrace</param>
public void AddException(string exceptionType, string stacktrace)
{
using (SQLiteCommand cmd = this.connection.CreateCommand()) {
@ -181,10 +206,18 @@ namespace ICSharpCode.UsageDataCollector @@ -181,10 +206,18 @@ namespace ICSharpCode.UsageDataCollector
}
}
bool isDisposed;
/// <summary>
/// Closes the session.
/// </summary>
public void Dispose()
{
EndSession();
connection.Close();
if (!isDisposed) {
isDisposed = true;
EndSession();
connection.Dispose();
}
}
}

10
src/AddIns/Misc/UsageDataCollector/UsageDataUploader.cs

@ -24,6 +24,9 @@ namespace ICSharpCode.UsageDataCollector @@ -24,6 +24,9 @@ namespace ICSharpCode.UsageDataCollector
{
string databaseFileName;
/// <summary>
/// Creates a new UsageDataUploader.
/// </summary>
public UsageDataUploader(string databaseFileName)
{
this.databaseFileName = databaseFileName;
@ -38,6 +41,11 @@ namespace ICSharpCode.UsageDataCollector @@ -38,6 +41,11 @@ namespace ICSharpCode.UsageDataCollector
return connection;
}
/// <summary>
/// Retrieves all stored data as XML text.
/// </summary>
/// <exception cref="IncompatibleDatabaseException">The database version is not compatible with this
/// version of the AnalyticsSessionWriter.</exception>
public string GetTextForStoredData()
{
UsageDataMessage message;
@ -60,6 +68,8 @@ namespace ICSharpCode.UsageDataCollector @@ -60,6 +68,8 @@ namespace ICSharpCode.UsageDataCollector
/// <summary>
/// Starts the upload of the usage data.
/// </summary>
/// <exception cref="IncompatibleDatabaseException">The database version is not compatible with this
/// version of the AnalyticsSessionWriter.</exception>
public void StartUpload()
{
UsageDataMessage message;

Loading…
Cancel
Save