Skip to content

[DRAFT] Telemetry #1896

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions WinUIGallery/Telemetry/Events/EmptyEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using Microsoft.Diagnostics.Telemetry.Internal;
using System;
using System.Diagnostics.Tracing;

namespace AIDevGallery.Telemetry;

[EventData]
internal class EmptyEvent : EventBase
{
public override PartA_PrivTags PartA_PrivTags { get; }

public EmptyEvent(PartA_PrivTags tags)
{
PartA_PrivTags = tags;
}

public override void ReplaceSensitiveStrings(Func<string?, string?> replaceSensitiveStrings)
{
// No sensitive string
}
}
42 changes: 42 additions & 0 deletions WinUIGallery/Telemetry/Events/EventBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using Microsoft.Diagnostics.Telemetry.Internal;
using System;
using System.Diagnostics.Tracing;

namespace AIDevGallery.Telemetry;

/// <summary>
/// Base class for all telemetry events to ensure they are properly tagged.
/// </summary>
/// <remarks>
/// The public properties of each event are logged in the telemetry.
/// We should not change an event's properties, as that could break the processing of that event's data.
/// </remarks>
[EventData]
public abstract class EventBase
{
/// <summary>
/// Gets the privacy datatype tag for the telemetry event.
/// </summary>
#pragma warning disable CA1707 // Identifiers should not contain underscores
public abstract PartA_PrivTags PartA_PrivTags
#pragma warning restore CA1707 // Identifiers should not contain underscores
{
get;
}

/// <summary>
/// Replaces all the strings in this event that may contain PII using the provided function.
/// </summary>
/// <remarks>
/// This is called by <see cref="ITelemetry"/> before logging the event.
/// It is the responsibility of each event to ensure we replace all strings with possible PII;
/// we ensure we at least consider this by forcing to implement this.
/// </remarks>
/// <param name="replaceSensitiveStrings">
/// A function that replaces all the sensitive strings in a given string with tokens
/// </param>
public abstract void ReplaceSensitiveStrings(Func<string?, string?> replaceSensitiveStrings);
}
35 changes: 35 additions & 0 deletions WinUIGallery/Telemetry/Events/NavigatedToSampleEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using Microsoft.Diagnostics.Telemetry;
using Microsoft.Diagnostics.Telemetry.Internal;
using System;
using System.Diagnostics.Tracing;

namespace AIDevGallery.Telemetry.Events;

[EventData]
internal class NavigatedToSampleEvent : EventBase
{
public override PartA_PrivTags PartA_PrivTags => PrivTags.ProductAndServiceUsage;

public string Name { get; }

public DateTime StartTime { get; }

private NavigatedToSampleEvent(string name, DateTime startTime)
{
Name = name;
StartTime = startTime;
}

public override void ReplaceSensitiveStrings(Func<string?, string?> replaceSensitiveStrings)
{
// No sensitive strings to replace.
}

public static void Log(string name)
{
TelemetryFactory.Get<ITelemetry>().Log("NavigatedToSample_Event", LogLevel.Critical, new NavigatedToSampleEvent(name, DateTime.Now));
}
}
80 changes: 80 additions & 0 deletions WinUIGallery/Telemetry/ITelemetry.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Diagnostics.CodeAnalysis;

namespace AIDevGallery.Telemetry;

internal interface ITelemetry
{
/// <summary>
/// Add a string that we should try stripping out of some of our telemetry for sensitivity reasons (ex. VM name, etc.).
/// We can never be 100% sure we can remove every string, but this should greatly reduce us collecting PII.
/// Note that the order in which AddSensitive is called matters, as later when we call ReplaceSensitiveStrings, it will try
/// finding and replacing the earlier strings first. This can be helpful, since we can target specific
/// strings (like username) first, which should help preserve more information helpful for diagnosis.
/// </summary>
/// <param name="name">Sensitive string to add (ex. "c:\xyz")</param>
/// <param name="replaceWith">string to replace it with (ex. "-path-")</param>
public void AddSensitiveString(string name, string replaceWith);

/// <summary>
/// Gets a value indicating whether telemetry is on
/// For future use if we add a registry key or some other setting to check if telemetry is turned on.
/// </summary>
public bool IsTelemetryOn { get; }

/// <summary>
/// Gets or sets a value indicating whether diagnostic telemetry is on.
/// </summary>
public bool IsDiagnosticTelemetryOn { get; set; }

/// <summary>
/// Logs an exception at Measure level. To log at Critical level, the event name needs approval.
/// </summary>
/// <param name="action">What we trying to do when the exception occurred.</param>
/// <param name="e">Exception object</param>
/// <param name="relatedActivityId">Optional relatedActivityId which will allow to correlate this telemetry with other telemetry in the same action/activity or thread and correlate them</param>
public void LogException(string action, Exception e, Guid? relatedActivityId = null);

/// <summary>
/// Log the time an action took (ex. time spent on a tool).
/// </summary>
/// <param name="eventName">The measurement we're performing (ex. "DeployTime").</param>
/// <param name="timeTakenMilliseconds">How long the action took in milliseconds.</param>
/// <param name="relatedActivityId">Optional relatedActivityId which will allow to correlate this telemetry with other telemetry in the same action/activity or thread and correlate them</param>
public void LogTimeTaken(string eventName, uint timeTakenMilliseconds, Guid? relatedActivityId = null);

/// <summary>
/// Log an event with no additional data.
/// </summary>
/// <param name="eventName">The name of the event to log</param>
/// <param name="isError">Set to true if an error condition raised this event.</param>
/// <param name="relatedActivityId">Optional relatedActivityId which will allow to correlate this telemetry with other telemetry in the same action/activity or thread and correlate them</param>
public void LogCritical(string eventName, bool isError = false, Guid? relatedActivityId = null);

/// <summary>
/// Log an informational event. Typically used for just a single event that's only called one place in the code.
/// If you are logging the same event multiple times, it's best to add a helper method in Telemetry
/// </summary>
/// <param name="eventName">Name of the error event</param>
/// <param name="level">Determines whether to upload the data to our servers, and on how many machines.</param>
/// <param name="data">Values to send to the telemetry system.</param>
/// <param name="relatedActivityId">Optional relatedActivityId which will allow to correlate this telemetry with other telemetry in the same action/activity or thread and correlate them</param>
/// <typeparam name="T">Anonymous type.</typeparam>
public void Log<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>(string eventName, LogLevel level, T data, Guid? relatedActivityId = null)
where T : EventBase;

/// <summary>
/// Log an error event. Typically used for just a single event that's only called one place in the code.
/// If you are logging the same event multiple times, it's best to add a helper method in Telemetry
/// </summary>
/// <param name="eventName">Name of the error event</param>
/// <param name="level">Determines whether to upload the data to our servers, and on how many machines.</param>
/// <param name="data">Values to send to the telemetry system.</param>
/// <param name="relatedActivityId">Optional relatedActivityId which will allow to correlate this telemetry with other telemetry in the same action/activity or thread and correlate them</param>
/// <typeparam name="T">Anonymous type.</typeparam>
public void LogError<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>(string eventName, LogLevel level, T data, Guid? relatedActivityId = null)
where T : EventBase;
}
39 changes: 39 additions & 0 deletions WinUIGallery/Telemetry/LogLevel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

namespace AIDevGallery.Telemetry;

/// <summary>
/// Telemetry Levels.
/// These levels are defined by our telemetry system, so it's possible the sampling
/// could change in the future.
/// There aren't any convenient enums we can consume, so create our own.
/// </summary>
public enum LogLevel
{
/// <summary>
/// Local.
/// Only log telemetry locally on the machine (similar to an ETW event).
/// </summary>
Local,

/// <summary>
/// Info.
/// Send telemetry from internal and flighted machines, but no external retail machines.
/// </summary>
Info,

/// <summary>
/// Measure.
/// Send telemetry from internal and flighted machines, plus a small, sample % of retail machines.
/// Should only be used for telemetry we use to derive measures from.
/// </summary>
Measure,

/// <summary>
/// Critical.
/// Send telemetry from all devices sampled at 100%.
/// Should only be used for approved events.
/// </summary>
Critical,
}
57 changes: 57 additions & 0 deletions WinUIGallery/Telemetry/PrivacyConsentHelpers.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Linq;
using Windows.Globalization;

namespace AIDevGallery.Telemetry;

internal static class PrivacyConsentHelpers
{
private static readonly string[] PrivacySensitiveRegions =
[
"AUT",
"BEL",
"BGR",
"BRA",
"CAN",
"HRV",
"CYP",
"CZE",
"DNK",
"EST",
"FIN",
"FRA",
"DEU",
"GRC",
"HUN",
"ISL",
"IRL",
"ITA",
"KOR", // Double Check
"LVA",
"LIE",
"LTU",
"LUX",
"MLT",
"NLD",
"NOR",
"POL",
"PRT",
"ROU",
"SVK",
"SVN",
"ESP",
"SWE",
"CHE",
"GBR",
];

public static bool IsPrivacySensitiveRegion()
{
var geographicRegion = new GeographicRegion();

return PrivacySensitiveRegions.Contains(geographicRegion.CodeThreeLetter, StringComparer.OrdinalIgnoreCase);
}
}
Loading