diff --git a/build/nuget/Uno.WinUI.nuspec b/build/nuget/Uno.WinUI.nuspec
index c3e5bb87e603..66c1e278d755 100644
--- a/build/nuget/Uno.WinUI.nuspec
+++ b/build/nuget/Uno.WinUI.nuspec
@@ -608,6 +608,7 @@
+
diff --git a/src/SourceGenerators/Uno.UI.SourceGenerators/Telemetry/PersistenceChannel/BaseStorageService.cs b/src/SourceGenerators/Uno.UI.SourceGenerators/Telemetry/PersistenceChannel/BaseStorageService.cs
deleted file mode 100644
index 5eb6a55592de..000000000000
--- a/src/SourceGenerators/Uno.UI.SourceGenerators/Telemetry/PersistenceChannel/BaseStorageService.cs
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright (c) .NET Foundation and contributors. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-//
-// 2019/04/12 (Jerome Laban ):
-// - Extracted from dotnet.exe
-//
-
-using System;
-using System.Collections.Generic;
-using System.Threading.Tasks;
-using Microsoft.ApplicationInsights.Channel;
-
-namespace Uno.UI.SourceGenerators.Telemetry.PersistenceChannel
-{
- internal abstract class BaseStorageService
- {
- ///
- /// Peeked transmissions dictionary (maps file name to its full path). Holds all the transmissions that were peeked.
- ///
- ///
- /// Note: The value (=file's full path) is not required in the Storage implementation.
- /// If there was a concurrent Abstract Data Type Set it would have been used instead.
- /// However, since there is no concurrent Set, dictionary is used and the second value is ignored.
- ///
- protected IDictionary PeekedTransmissions;
-
- ///
- /// Gets or sets the maximum size of the storage in bytes. When limit is reached, the Enqueue method will drop new
- /// transmissions.
- ///
- internal ulong CapacityInBytes { get; set; }
-
- ///
- /// Gets or sets the maximum number of files. When limit is reached, the Enqueue method will drop new transmissions.
- ///
- internal uint MaxFiles { get; set; }
-
- internal abstract string StorageDirectoryPath { get; }
-
- ///
- /// Initializes the
- ///
- /// A folder name. Under this folder all the transmissions will be saved.
- internal abstract void Init(string desireStorageDirectoryPath);
-
- internal abstract StorageTransmission Peek();
-
- internal abstract void Delete(StorageTransmission transmission);
-
- internal abstract Task EnqueueAsync(Transmission transmission);
-
- protected void OnPeekedItemDisposed(string fileName)
- {
- try
- {
- if (PeekedTransmissions.ContainsKey(fileName))
- {
- PeekedTransmissions.Remove(fileName);
- }
- }
- catch (Exception e)
- {
- PersistenceChannelDebugLog.WriteException(e, "Failed to remove the item from storage items.");
- }
- }
- }
-}
diff --git a/src/SourceGenerators/Uno.UI.SourceGenerators/Telemetry/PersistenceChannel/FixedSizeQueue.cs b/src/SourceGenerators/Uno.UI.SourceGenerators/Telemetry/PersistenceChannel/FixedSizeQueue.cs
deleted file mode 100644
index e3663ec5c373..000000000000
--- a/src/SourceGenerators/Uno.UI.SourceGenerators/Telemetry/PersistenceChannel/FixedSizeQueue.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright (c) .NET Foundation and contributors. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-//
-// 2019/04/12 (Jerome Laban ):
-// - Extracted from dotnet.exe
-//
-
-using System.Collections.Generic;
-
-namespace Uno.UI.SourceGenerators.Telemetry.PersistenceChannel
-{
- ///
- /// A light fixed size queue. If Enqueue is called and queue's limit has reached the last item will be removed.
- /// This data structure is thread safe.
- ///
- internal class FixedSizeQueue
- {
- private readonly int _maxSize;
- private readonly Queue _queue = new Queue();
- private readonly object _queueLockObj = new object();
-
- internal FixedSizeQueue(int maxSize)
- {
- _maxSize = maxSize;
- }
-
- internal void Enqueue(T item)
- {
- lock (_queueLockObj)
- {
- if (_queue.Count == _maxSize)
- {
- _queue.Dequeue();
- }
-
- _queue.Enqueue(item);
- }
- }
-
- internal bool Contains(T item)
- {
- lock (_queueLockObj)
- {
- return _queue.Contains(item);
- }
- }
- }
-}
diff --git a/src/SourceGenerators/Uno.UI.SourceGenerators/Telemetry/PersistenceChannel/FlushManager.cs b/src/SourceGenerators/Uno.UI.SourceGenerators/Telemetry/PersistenceChannel/FlushManager.cs
deleted file mode 100644
index 850bcf769121..000000000000
--- a/src/SourceGenerators/Uno.UI.SourceGenerators/Telemetry/PersistenceChannel/FlushManager.cs
+++ /dev/null
@@ -1,62 +0,0 @@
-// // Copyright (c) .NET Foundation and contributors. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-//
-// 2019/04/12 (Jerome Laban ):
-// - Extracted from dotnet.exe
-//
-
-using System;
-using Microsoft.ApplicationInsights.Channel;
-using Microsoft.ApplicationInsights.Extensibility.Implementation;
-using IChannelTelemetry = Microsoft.ApplicationInsights.Channel.ITelemetry;
-
-namespace Uno.UI.SourceGenerators.Telemetry.PersistenceChannel
-{
- ///
- /// This class handles all the logic for flushing the In Memory buffer to the persistent storage.
- ///
- internal class FlushManager
- {
- ///
- /// The storage that is used to persist all the transmissions.
- ///
- private readonly BaseStorageService _storage;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The storage that persists the telemetries.
- internal FlushManager(BaseStorageService storage)
- {
- _storage = storage;
- }
-
- ///
- /// Gets or sets the service endpoint.
- ///
- ///
- /// Q: Why flushManager knows about the endpoint?
- /// A: Storage stores Transmission objects and Transmission objects contain the endpoint address.
- ///
- internal Uri EndpointAddress { get; set; }
-
-
- ///
- /// Persist the in-memory telemetry items.
- ///
- internal void Flush(IChannelTelemetry telemetryItem)
- {
- if (telemetryItem != null)
- {
- byte[] data = JsonSerializer.Serialize(new[] { telemetryItem });
- Transmission transmission = new Transmission(
- EndpointAddress,
- data,
- "application/x-json-stream",
- JsonSerializer.CompressionType);
-
- _storage.EnqueueAsync(transmission).ConfigureAwait(false).GetAwaiter().GetResult();
- }
- }
- }
-}
diff --git a/src/SourceGenerators/Uno.UI.SourceGenerators/Telemetry/PersistenceChannel/PersistenceChannel.cs b/src/SourceGenerators/Uno.UI.SourceGenerators/Telemetry/PersistenceChannel/PersistenceChannel.cs
deleted file mode 100644
index c470535cfba0..000000000000
--- a/src/SourceGenerators/Uno.UI.SourceGenerators/Telemetry/PersistenceChannel/PersistenceChannel.cs
+++ /dev/null
@@ -1,119 +0,0 @@
-// Copyright (c) .NET Foundation and contributors. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-//
-// 2019/04/12 (Jerome Laban ):
-// - Extracted from dotnet.exe
-//
-
-using System;
-using System.Threading;
-using Microsoft.ApplicationInsights.Channel;
-using IChannelTelemetry = Microsoft.ApplicationInsights.Channel.ITelemetry;
-
-namespace Uno.UI.SourceGenerators.Telemetry.PersistenceChannel
-{
- ///
- /// Represents a communication channel for sending telemetry to Application Insights via HTTPS.
- ///
- internal sealed class PersistenceChannel : ITelemetryChannel
- {
- internal const string TelemetryServiceEndpoint = "https://dc.services.visualstudio.com/v2/track";
-
- private readonly FlushManager _flushManager;
-
- private int _disposeCount;
- private readonly BaseStorageService _storage;
- private readonly PersistenceTransmitter _transmitter;
-
- ///
- /// Initializes a new instance of the class.
- ///
- ///
- /// Full path of a directory name. Under this folder all the transmissions will be saved.
- /// Setting this value groups channels, even from different processes.
- /// If 2 (or more) channels has the same storageFolderName only one channel will perform the sending even if the
- /// channel is in a different process/AppDomain/Thread.
- ///
- ///
- /// Defines the number of senders. A sender is a long-running thread that sends telemetry batches in intervals defined
- /// by .
- /// So the amount of senders also defined the maximum amount of http channels opened at the same time.
- ///
- public PersistenceChannel(string storageDirectoryPath = null, int sendersCount = 1)
- {
- _storage = new StorageService();
- _storage.Init(storageDirectoryPath);
- _transmitter = new PersistenceTransmitter(_storage, sendersCount);
- _flushManager = new FlushManager(_storage);
- EndpointAddress = TelemetryServiceEndpoint;
- }
-
- ///
- /// Gets or sets an interval between each successful sending.
- ///
- ///
- /// On error scenario this value is ignored and the interval will be defined using an exponential back-off
- /// algorithm.
- ///
- public TimeSpan? SendingInterval
- {
- get => _transmitter.SendingInterval;
- set => _transmitter.SendingInterval = value;
- }
-
-
- ///
- /// Gets or sets the maximum amount of files allowed in storage. When the limit is reached telemetries will be dropped.
- ///
- public uint MaxTransmissionStorageFilesCapacity
- {
- get => _storage.MaxFiles;
- set => _storage.MaxFiles = value;
- }
-
- ///
- /// This flag has no effect. But it is required by base class
- ///
- public bool? DeveloperMode { get; set; }
-
- ///
- /// Gets or sets the HTTP address where the telemetry is sent.
- ///
- public string EndpointAddress
- {
- get => _flushManager.EndpointAddress.ToString();
-
- set
- {
- string address = value ?? TelemetryServiceEndpoint;
- _flushManager.EndpointAddress = new Uri(address);
- }
- }
-
- ///
- /// Releases unmanaged and - optionally - managed resources.
- ///
- public void Dispose()
- {
- if (Interlocked.Increment(ref _disposeCount) == 1)
- {
- _transmitter?.Dispose();
- }
- }
-
- ///
- /// Sends an instance of ITelemetry through the channel.
- ///
- public void Send(IChannelTelemetry item)
- {
- _flushManager.Flush(item);
- }
-
- ///
- /// No operation, send will always flush. So nothing will be in memory
- ///
- public void Flush()
- {
- }
- }
-}
diff --git a/src/SourceGenerators/Uno.UI.SourceGenerators/Telemetry/PersistenceChannel/PersistenceChannelDebugLog.cs b/src/SourceGenerators/Uno.UI.SourceGenerators/Telemetry/PersistenceChannel/PersistenceChannelDebugLog.cs
deleted file mode 100644
index 18b9541a2d4d..000000000000
--- a/src/SourceGenerators/Uno.UI.SourceGenerators/Telemetry/PersistenceChannel/PersistenceChannelDebugLog.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (c) .NET Foundation and contributors. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-//
-// 2019/04/12 (Jerome Laban ):
-// - Extracted from dotnet.exe
-//
-
-using System;
-using System.Globalization;
-using System.IO;
-
-namespace Uno.UI.SourceGenerators.Telemetry.PersistenceChannel
-{
- internal static class PersistenceChannelDebugLog
- {
- private static readonly bool _isEnabled = IsEnabledByEnvironment();
-
- private static bool IsEnabledByEnvironment()
- {
- if (bool.TryParse(Environment.GetEnvironmentVariable("UNO_SOURCEGEN_ENABLE_PERSISTENCE_CHANNEL_DEBUG_OUTPUT"), out var enabled))
- {
- return enabled;
- }
-
- return false;
- }
-
- public static void WriteLine(string message)
- {
- if (_isEnabled)
- {
- Console.WriteLine(message);
- }
- }
-
- internal static void WriteException(Exception exception, string format, params string[] args)
- {
- var message = string.Format(CultureInfo.InvariantCulture, format, args);
- WriteLine(string.Format(CultureInfo.InvariantCulture, "{0} Exception: {1}", message, exception.ToString()));
- }
- }
-}
diff --git a/src/SourceGenerators/Uno.UI.SourceGenerators/Telemetry/PersistenceChannel/PersistenceTransmitter.cs b/src/SourceGenerators/Uno.UI.SourceGenerators/Telemetry/PersistenceChannel/PersistenceTransmitter.cs
deleted file mode 100644
index 67aeb23b8328..000000000000
--- a/src/SourceGenerators/Uno.UI.SourceGenerators/Telemetry/PersistenceChannel/PersistenceTransmitter.cs
+++ /dev/null
@@ -1,92 +0,0 @@
-// Copyright (c) .NET Foundation and contributors. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-//
-// 2019/04/12 (Jerome Laban ):
-// - Extracted from dotnet.exe
-//
-
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace Uno.UI.SourceGenerators.Telemetry.PersistenceChannel
-{
- ///
- /// Implements throttled and persisted transmission of telemetry to Application Insights.
- ///
- internal class PersistenceTransmitter : IDisposable
- {
- ///
- /// The number of times this object was disposed.
- ///
- private int _disposeCount;
-
- ///
- /// A list of senders that sends transmissions.
- ///
- private readonly List _senders = new List();
-
- ///
- /// The storage that is used to persist all the transmissions.
- ///
- private readonly BaseStorageService _storage;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The transmissions storage.
- /// The number of senders to create.
- ///
- /// A boolean value that indicates if this class should try and create senders. This is a
- /// workaround for unit tests purposes only.
- ///
- internal PersistenceTransmitter(BaseStorageService storage, int sendersCount, bool createSenders = true)
- {
- _storage = storage;
- if (createSenders)
- {
- for (int i = 0; i < sendersCount; i++)
- {
- _senders.Add(new Sender(_storage, this));
- }
- }
- }
-
- ///
- /// Gets or sets the interval between each successful sending.
- ///
- internal TimeSpan? SendingInterval { get; set; }
-
- ///
- /// Disposes the object.
- ///
- public void Dispose()
- {
- if (Interlocked.Increment(ref _disposeCount) == 1)
- {
- StopSenders();
- }
- }
-
- ///
- /// Stops the senders.
- ///
- /// As long as there is no Start implementation, this method should only be called from Dispose.
- private void StopSenders()
- {
- if (_senders == null)
- {
- return;
- }
-
- List stoppedTasks = new List();
- foreach (Sender sender in _senders)
- {
- stoppedTasks.Add(sender.StopAsync());
- }
-
- Task.WaitAll(stoppedTasks.ToArray());
- }
- }
-}
diff --git a/src/SourceGenerators/Uno.UI.SourceGenerators/Telemetry/PersistenceChannel/Sender.cs b/src/SourceGenerators/Uno.UI.SourceGenerators/Telemetry/PersistenceChannel/Sender.cs
deleted file mode 100644
index 49f24a39f016..000000000000
--- a/src/SourceGenerators/Uno.UI.SourceGenerators/Telemetry/PersistenceChannel/Sender.cs
+++ /dev/null
@@ -1,343 +0,0 @@
-// Copyright (c) .NET Foundation and contributors. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-//
-// 2019/04/12 (Jerome Laban ):
-// - Extracted from dotnet.exe
-//
-
-using System;
-using System.Globalization;
-using System.Net;
-using System.Net.NetworkInformation;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace Uno.UI.SourceGenerators.Telemetry.PersistenceChannel
-{
- ///
- /// Fetch transmissions from the storage and sends it.
- ///
- internal class Sender : IDisposable
- {
- ///
- /// The default sending interval.
- ///
- private readonly TimeSpan _defaultSendingInterval;
-
- ///
- /// A wait handle that flags the sender when to start sending again. The type is protected for unit test.
- ///
- protected readonly AutoResetEvent DelayHandler;
-
- ///
- /// Holds the maximum time for the exponential back-off algorithm. The sending interval will grow on every HTTP
- /// Exception until this max value.
- ///
- private readonly TimeSpan _maxIntervalBetweenRetries = TimeSpan.FromHours(1);
-
- ///
- /// When storage is empty it will be queried again after this interval.
- /// Decreasing to 5 sec to send first data (users and sessions).
- ///
- private readonly TimeSpan _sendingIntervalOnNoData = TimeSpan.FromSeconds(5);
-
- ///
- /// A wait handle that is being set when Sender is no longer sending.
- ///
- private readonly AutoResetEvent _stoppedHandler;
-
- ///
- /// The number of times this object was disposed.
- ///
- private int _disposeCount;
-
- ///
- /// The amount of time to wait, in the stop method, until the last transmission is sent.
- /// If time expires, the stop method will return even if the transmission hasn't been sent.
- ///
- private readonly TimeSpan _drainingTimeout;
-
- ///
- /// A boolean value that indicates if the sender should be stopped. The sender's while loop is checking this boolean
- /// value.
- ///
- private bool _stopped;
-
- ///
- /// The transmissions storage.
- ///
- private readonly BaseStorageService _storage;
-
- ///
- /// Holds the transmitter.
- ///
- private readonly PersistenceTransmitter _transmitter;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The storage that holds the transmissions to send.
- ///
- /// The persistence transmitter that manages this Sender.
- /// The transmitter will be used as a configuration class, it exposes properties like SendingInterval that will be read
- /// by Sender.
- ///
- ///
- /// A boolean value that determines if Sender should start sending immediately. This is only
- /// used for unit tests.
- ///
- internal Sender(BaseStorageService storage, PersistenceTransmitter transmitter, bool startSending = true)
- {
- _stopped = false;
- DelayHandler = new AutoResetEvent(false);
- _stoppedHandler = new AutoResetEvent(false);
- _drainingTimeout = TimeSpan.FromSeconds(100);
- _defaultSendingInterval = TimeSpan.FromSeconds(5);
-
- _transmitter = transmitter;
- _storage = storage;
-
- if (startSending)
- {
- // It is currently possible for the long - running task to be executed(and thereby block during WaitOne) on the UI thread when
- // called by a task scheduled on the UI thread. Explicitly specifying TaskScheduler.Default
- // when calling StartNew guarantees that Sender never blocks the main thread.
- Task.Factory.StartNew(SendLoop, CancellationToken.None, TaskCreationOptions.LongRunning,
- TaskScheduler.Default)
- .ContinueWith(
- t => PersistenceChannelDebugLog.WriteException(t.Exception, "Sender: Failure in SendLoop"),
- TaskContinuationOptions.OnlyOnFaulted);
- }
- }
-
- ///
- /// Gets the interval between each successful sending.
- ///
- private TimeSpan SendingInterval
- {
- get
- {
- if (_transmitter.SendingInterval != null)
- {
- return _transmitter.SendingInterval.Value;
- }
-
- return _defaultSendingInterval;
- }
- }
-
- ///
- /// Disposes the managed objects.
- ///
- public void Dispose()
- {
- if (Interlocked.Increment(ref _disposeCount) == 1)
- {
- StopAsync().ConfigureAwait(false).GetAwaiter().GetResult();
-
- DelayHandler.Dispose();
- _stoppedHandler.Dispose();
- }
- }
-
- ///
- /// Stops the sender.
- ///
- internal Task StopAsync()
- {
- // After delayHandler is set, a sending iteration will immediately start.
- // Setting stopped to true, will cause the iteration to skip the actual sending and stop immediately.
- _stopped = true;
- DelayHandler.Set();
-
- // if delayHandler was set while a transmission was being sent, the return task will wait for it to finish, for an additional second,
- // before it will mark the task as completed.
- return Task.Run(() =>
- {
- try
- {
- _stoppedHandler.WaitOne(_drainingTimeout);
- }
- catch (ObjectDisposedException)
- {
- }
- });
- }
-
- ///
- /// Send transmissions in a loop.
- ///
- protected void SendLoop()
- {
- TimeSpan prevSendingInterval = TimeSpan.Zero;
- TimeSpan sendingInterval = _sendingIntervalOnNoData;
- try
- {
- while (!_stopped)
- {
- using (StorageTransmission transmission = _storage.Peek())
- {
- if (_stopped)
- {
- // This second verification is required for cases where 'stopped' was set while peek was happening.
- // Once the actual sending starts the design is to wait until it finishes and deletes the transmission.
- // So no extra validation is required.
- break;
- }
-
- // If there is a transmission to send - send it.
- if (transmission != null)
- {
- bool shouldRetry = Send(transmission, ref sendingInterval);
- if (!shouldRetry)
- {
- // If retry is not required - delete the transmission.
- _storage.Delete(transmission);
- }
- }
- else
- {
- sendingInterval = _sendingIntervalOnNoData;
- }
- }
-
- LogInterval(prevSendingInterval, sendingInterval);
- DelayHandler.WaitOne(sendingInterval);
- prevSendingInterval = sendingInterval;
- }
-
- _stoppedHandler.Set();
- }
- catch (ObjectDisposedException)
- {
- }
- }
-
- ///
- /// Sends a transmission and handle errors.
- ///
- /// The transmission to send.
- ///
- /// When this value returns it will hold a recommendation for when to start the next sending
- /// iteration.
- ///
- /// True, if there was sent error and we need to retry sending, otherwise false.
- protected virtual bool Send(StorageTransmission transmission, ref TimeSpan nextSendInterval)
- {
- try
- {
- if (transmission != null)
- {
- bool isConnected = NetworkInterface.GetIsNetworkAvailable();
-
- // there is no internet connection available, return than.
- if (!isConnected)
- {
- PersistenceChannelDebugLog.WriteLine(
- "Cannot send data to the server. Internet connection is not available");
- return true;
- }
-
- transmission.SendAsync().ConfigureAwait(false).GetAwaiter().GetResult();
-
- // After a successful sending, try immediately to send another transmission.
- nextSendInterval = SendingInterval;
- }
- }
- catch (WebException e)
- {
- int? statusCode = GetStatusCode(e);
- nextSendInterval = CalculateNextInterval(statusCode, nextSendInterval, _maxIntervalBetweenRetries);
- return IsRetryable(statusCode, e.Status);
- }
- catch (Exception e)
- {
- nextSendInterval = CalculateNextInterval(null, nextSendInterval, _maxIntervalBetweenRetries);
- PersistenceChannelDebugLog.WriteException(e, "Unknown exception during sending");
- }
-
- return false;
- }
-
- ///
- /// Log next interval. Only log the interval when it changes by more then a minute. So if interval grow by 1 minute or
- /// decreased by 1 minute it will be logged.
- /// Logging every interval will just make the log noisy.
- ///
- private static void LogInterval(TimeSpan prevSendInterval, TimeSpan nextSendInterval)
- {
- if (Math.Abs(nextSendInterval.TotalSeconds - prevSendInterval.TotalSeconds) > 60)
- {
- PersistenceChannelDebugLog.WriteLine("next sending interval: " + nextSendInterval);
- }
- }
-
- ///
- /// Return the status code from the web exception or null if no such code exists.
- ///
- private static int? GetStatusCode(WebException e)
- {
- if (e.Response is HttpWebResponse httpWebResponse)
- {
- return (int)httpWebResponse.StatusCode;
- }
-
- return null;
- }
-
- ///
- /// Returns true if or are retryable.
- ///
- private static bool IsRetryable(int? httpStatusCode, WebExceptionStatus webExceptionStatus)
- {
- switch (webExceptionStatus)
- {
- case WebExceptionStatus.ProxyNameResolutionFailure:
- case WebExceptionStatus.NameResolutionFailure:
- case WebExceptionStatus.Timeout:
- case WebExceptionStatus.ConnectFailure:
- return true;
- }
-
- if (httpStatusCode == null)
- {
- return false;
- }
-
- switch (httpStatusCode.Value)
- {
- case 503: // Server in maintenance.
- case 408: // invalid request
- case 500: // Internal Server Error
- case 502: // Bad Gateway, can be common when there is no network.
- case 511: // Network Authentication Required
- return true;
- }
-
- return false;
- }
-
- ///
- /// Calculates the next interval using exponential back-off algorithm (with the exceptions of few error codes that
- /// reset the interval to .
- ///
- private TimeSpan CalculateNextInterval(int? httpStatusCode, TimeSpan currentSendInterval, TimeSpan maxInterval)
- {
- // if item is expired, no need for exponential back-off
- if (httpStatusCode != null && httpStatusCode.Value == 400 /* expired */)
- {
- return SendingInterval;
- }
-
- // exponential back-off.
- if (Math.Abs(currentSendInterval.TotalSeconds) < 1)
- {
- return TimeSpan.FromSeconds(1);
- }
-
- double nextIntervalInSeconds = Math.Min(currentSendInterval.TotalSeconds * 2, maxInterval.TotalSeconds);
-
- return TimeSpan.FromSeconds(nextIntervalInSeconds);
- }
- }
-}
diff --git a/src/SourceGenerators/Uno.UI.SourceGenerators/Telemetry/PersistenceChannel/SnapshottingCollection.cs b/src/SourceGenerators/Uno.UI.SourceGenerators/Telemetry/PersistenceChannel/SnapshottingCollection.cs
deleted file mode 100644
index 051adda5cea0..000000000000
--- a/src/SourceGenerators/Uno.UI.SourceGenerators/Telemetry/PersistenceChannel/SnapshottingCollection.cs
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright (c) .NET Foundation and contributors. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-//
-// 2019/04/12 (Jerome Laban ):
-// - Extracted from dotnet.exe
-//
-
-using System.Collections;
-using System.Collections.Generic;
-using System.Diagnostics;
-
-namespace Uno.UI.SourceGenerators.Telemetry.PersistenceChannel
-{
- internal abstract class SnapshottingCollection : ICollection
- where TCollection : class, ICollection
- {
- protected readonly TCollection Collection;
- protected TCollection snapshot;
-
- protected SnapshottingCollection(TCollection collection)
- {
- Debug.Assert(collection != null, "collection");
- Collection = collection;
- }
-
- public int Count => GetSnapshot().Count;
-
- public bool IsReadOnly => false;
-
- public void Add(TItem item)
- {
- lock (Collection)
- {
- Collection.Add(item);
- snapshot = default;
- }
- }
-
- public void Clear()
- {
- lock (Collection)
- {
- Collection.Clear();
- snapshot = default;
- }
- }
-
- public bool Contains(TItem item)
- {
- return GetSnapshot().Contains(item);
- }
-
- public void CopyTo(TItem[] array, int arrayIndex)
- {
- GetSnapshot().CopyTo(array, arrayIndex);
- }
-
- public bool Remove(TItem item)
- {
- lock (Collection)
- {
- bool removed = Collection.Remove(item);
- if (removed)
- {
- snapshot = default;
- }
-
- return removed;
- }
- }
-
- public IEnumerator GetEnumerator()
- {
- return GetSnapshot().GetEnumerator();
- }
-
- IEnumerator IEnumerable.GetEnumerator()
- {
- return GetEnumerator();
- }
-
- protected abstract TCollection CreateSnapshot(TCollection collection);
-
- protected TCollection GetSnapshot()
- {
- TCollection localSnapshot = snapshot;
- if (localSnapshot == null)
- {
- lock (Collection)
- {
- snapshot = CreateSnapshot(Collection);
- localSnapshot = snapshot;
- }
- }
-
- return localSnapshot;
- }
- }
-}
diff --git a/src/SourceGenerators/Uno.UI.SourceGenerators/Telemetry/PersistenceChannel/SnapshottingDictionary.cs b/src/SourceGenerators/Uno.UI.SourceGenerators/Telemetry/PersistenceChannel/SnapshottingDictionary.cs
deleted file mode 100644
index 83897f5d0069..000000000000
--- a/src/SourceGenerators/Uno.UI.SourceGenerators/Telemetry/PersistenceChannel/SnapshottingDictionary.cs
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright (c) .NET Foundation and contributors. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-//
-// 2019/04/12 (Jerome Laban ):
-// - Extracted from dotnet.exe
-//
-
-using System.Collections.Generic;
-
-namespace Uno.UI.SourceGenerators.Telemetry.PersistenceChannel
-{
- internal class SnapshottingDictionary :
- SnapshottingCollection, IDictionary>, IDictionary
- {
- public SnapshottingDictionary()
- : base(new Dictionary())
- {
- }
-
- public ICollection Keys => GetSnapshot().Keys;
-
- public ICollection Values => GetSnapshot().Values;
-
- public TValue this[TKey key]
- {
- get => GetSnapshot()[key];
-
- set
- {
- lock (Collection)
- {
- Collection[key] = value;
- snapshot = null;
- }
- }
- }
-
- public void Add(TKey key, TValue value)
- {
- lock (Collection)
- {
- Collection.Add(key, value);
- snapshot = null;
- }
- }
-
- public bool ContainsKey(TKey key)
- {
- return GetSnapshot().ContainsKey(key);
- }
-
- public bool Remove(TKey key)
- {
- lock (Collection)
- {
- bool removed = Collection.Remove(key);
- if (removed)
- {
- snapshot = null;
- }
-
- return removed;
- }
- }
-
- public bool TryGetValue(TKey key, out TValue value)
- {
- return GetSnapshot().TryGetValue(key, out value);
- }
-
- protected sealed override IDictionary CreateSnapshot(IDictionary collection)
- {
- return new Dictionary(collection);
- }
- }
-}
diff --git a/src/SourceGenerators/Uno.UI.SourceGenerators/Telemetry/PersistenceChannel/StorageService.cs b/src/SourceGenerators/Uno.UI.SourceGenerators/Telemetry/PersistenceChannel/StorageService.cs
deleted file mode 100644
index b43163c0d495..000000000000
--- a/src/SourceGenerators/Uno.UI.SourceGenerators/Telemetry/PersistenceChannel/StorageService.cs
+++ /dev/null
@@ -1,364 +0,0 @@
-// Copyright (c) .NET Foundation and contributors. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-//
-// 2019/04/12 (Jerome Laban ):
-// - Extracted from dotnet.exe
-//
-
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.ApplicationInsights.Channel;
-
-namespace Uno.UI.SourceGenerators.Telemetry.PersistenceChannel
-{
- internal sealed class StorageService : BaseStorageService
- {
- private const string DefaultStorageFolderName = "TelemetryStorageService";
- private readonly FixedSizeQueue _deletedFilesQueue = new FixedSizeQueue(10);
-
- private readonly object _peekLockObj = new object();
- private readonly object _storageFolderLock = new object();
- private string _storageDirectoryPath;
- private string _storageDirectoryPathUsed;
- private long _storageCountFiles;
- private bool _storageFolderInitialized;
- private long _storageSize;
- private uint _transmissionsDropped;
-
- ///
- /// Gets the storage's folder name.
- ///
- internal override string StorageDirectoryPath => _storageDirectoryPath;
-
- ///
- /// Gets the storage folder. If storage folder couldn't be created, null will be returned.
- ///
- private string StorageFolder
- {
- get
- {
- if (!_storageFolderInitialized)
- {
- lock (_storageFolderLock)
- {
- if (!_storageFolderInitialized)
- {
- try
- {
- _storageDirectoryPathUsed = _storageDirectoryPath;
-
- if (!Directory.Exists(_storageDirectoryPathUsed))
- {
- Directory.CreateDirectory(_storageDirectoryPathUsed);
- }
- }
- catch (Exception e)
- {
- _storageDirectoryPathUsed = null;
- PersistenceChannelDebugLog.WriteException(e, "Failed to create storage folder");
- }
-
- _storageFolderInitialized = true;
- }
- }
- }
-
- return _storageDirectoryPathUsed;
- }
- }
-
- internal override void Init(string storageDirectoryPath)
- {
- PeekedTransmissions = new SnapshottingDictionary();
-
- VerifyOrSetDefaultStorageDirectoryPath(storageDirectoryPath);
-
- CapacityInBytes = 10 * 1024 * 1024; // 10 MB
- MaxFiles = 100;
-
- Task.Run(DeleteObsoleteFiles)
- .ContinueWith(
- task =>
- {
- PersistenceChannelDebugLog.WriteException(
- task.Exception,
- "Storage: Unhandled exception in DeleteObsoleteFiles");
- },
- TaskContinuationOptions.OnlyOnFaulted);
- }
-
- private void VerifyOrSetDefaultStorageDirectoryPath(string desireStorageDirectoryPath)
- {
- if (string.IsNullOrEmpty(desireStorageDirectoryPath))
- {
- _storageDirectoryPath = Path.Combine(
- Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
- DefaultStorageFolderName);
- }
- else
- {
- if (!Path.IsPathRooted(desireStorageDirectoryPath))
- {
- throw new ArgumentException($"{nameof(desireStorageDirectoryPath)} need to be rooted (full path)");
- }
-
- _storageDirectoryPath = desireStorageDirectoryPath;
- }
- }
-
- ///
- /// Reads an item from the storage. Order is Last-In-First-Out.
- /// When the Transmission is no longer needed (it was either sent or failed with a non-retryable error) it should be
- /// disposed.
- ///
- internal override StorageTransmission Peek()
- {
- IEnumerable files = GetFiles("*.trn", 50);
-
- lock (_peekLockObj)
- {
- foreach (string file in files)
- {
- try
- {
- // if a file was peeked before, skip it (wait until it is disposed).
- if (PeekedTransmissions.ContainsKey(file) == false &&
- _deletedFilesQueue.Contains(file) == false)
- {
- // Load the transmission from disk.
- StorageTransmission storageTransmissionItem = LoadTransmissionFromFileAsync(file)
- .ConfigureAwait(false).GetAwaiter().GetResult();
-
- // when item is disposed it should be removed from the peeked list.
- storageTransmissionItem.Disposing = item => OnPeekedItemDisposed(file);
-
- // add the transmission to the list.
- PeekedTransmissions.Add(file, storageTransmissionItem.FullFilePath);
- return storageTransmissionItem;
- }
- }
- catch (Exception e)
- {
- PersistenceChannelDebugLog.WriteException(
- e,
- "Failed to load an item from the storage. file: {0}",
- file);
- }
- }
- }
-
- return null;
- }
-
- internal override void Delete(StorageTransmission item)
- {
- try
- {
- if (StorageFolder == null)
- {
- return;
- }
-
- // Initial storage size calculation.
- CalculateSize();
-
- long fileSize = GetSize(item.FileName);
- File.Delete(Path.Combine(StorageFolder, item.FileName));
-
- _deletedFilesQueue.Enqueue(item.FileName);
-
- // calculate size
- Interlocked.Add(ref _storageSize, -fileSize);
- Interlocked.Decrement(ref _storageCountFiles);
- }
- catch (IOException e)
- {
- PersistenceChannelDebugLog.WriteException(e, "Failed to delete a file. file: {0}", item == null ? "null" : item.FullFilePath);
- }
- }
-
- internal override async Task EnqueueAsync(Transmission transmission)
- {
- try
- {
- if (transmission == null || StorageFolder == null)
- {
- return;
- }
-
- // Initial storage size calculation.
- CalculateSize();
-
- if ((ulong)_storageSize >= CapacityInBytes || _storageCountFiles >= MaxFiles)
- {
- // if max storage capacity has reached, drop the transmission (but log every 100 lost transmissions).
- if (_transmissionsDropped++ % 100 == 0)
- {
- PersistenceChannelDebugLog.WriteLine("Total transmissions dropped: " + _transmissionsDropped);
- }
-
- return;
- }
-
- // Writes content to a temporary file and only then rename to avoid the Peek from reading the file before it is being written.
- // Creates the temp file name
- string tempFileName = Guid.NewGuid().ToString("N");
-
- // Now that the file got created we can increase the files count
- Interlocked.Increment(ref _storageCountFiles);
-
- // Saves transmission to the temp file
- await SaveTransmissionToFileAsync(transmission, tempFileName).ConfigureAwait(false);
-
- // Now that the file is written increase storage size.
- long temporaryFileSize = GetSize(tempFileName);
- Interlocked.Add(ref _storageSize, temporaryFileSize);
-
- // Creates a new file name
- string now = DateTime.UtcNow.ToString("yyyyMMddHHmmss", CultureInfo.InvariantCulture);
- string newFileName = string.Format(CultureInfo.InvariantCulture, "{0}_{1}.trn", now, tempFileName);
-
- // Renames the file
- File.Move(Path.Combine(StorageFolder, tempFileName), Path.Combine(StorageFolder, newFileName));
- }
- catch (Exception e)
- {
- PersistenceChannelDebugLog.WriteException(e, "EnqueueAsync");
- }
- }
-
- private async Task SaveTransmissionToFileAsync(Transmission transmission, string file)
- {
- try
- {
- using (Stream stream = File.OpenWrite(Path.Combine(StorageFolder, file)))
- {
- await StorageTransmission.SaveAsync(transmission, stream).ConfigureAwait(false);
- }
- }
- catch (UnauthorizedAccessException)
- {
- string message =
- string.Format(
- CultureInfo.InvariantCulture,
- "Failed to save transmission to file. UnauthorizedAccessException. File path: {0}, FileName: {1}",
- StorageFolder, file);
- PersistenceChannelDebugLog.WriteLine(message);
- throw;
- }
- }
-
- private async Task LoadTransmissionFromFileAsync(string file)
- {
- try
- {
- using (Stream stream = File.OpenRead(Path.Combine(StorageFolder, file)))
- {
- StorageTransmission storageTransmissionItem =
- await StorageTransmission.CreateFromStreamAsync(stream, file).ConfigureAwait(false);
- return storageTransmissionItem;
- }
- }
- catch (Exception e)
- {
- string message =
- string.Format(
- CultureInfo.InvariantCulture,
- "Failed to load transmission from file. File path: {0}, FileName: {1}, Exception: {2}",
- "storageFolderName", file, e);
- PersistenceChannelDebugLog.WriteLine(message);
- throw;
- }
- }
-
- ///
- /// Get files from .
- ///
- /// Define the logic for sorting the files.
- /// Defines a file extension. This method will return only files with this extension.
- ///
- /// Define how many files to return. This can be useful when the directory has a lot of files, in that case
- /// GetFilesAsync will have a performance hit.
- ///
- private IEnumerable GetFiles(string filterByExtension, int top)
- {
- try
- {
- if (StorageFolder != null)
- {
- return Directory.GetFiles(StorageFolder, filterByExtension).Take(top);
- }
- }
- catch (Exception e)
- {
- PersistenceChannelDebugLog.WriteException(e, "Peek failed while get files from storage.");
- }
-
- return Enumerable.Empty();
- }
-
- ///
- /// Gets a file's size.
- ///
- private long GetSize(string file)
- {
- using (FileStream stream = File.OpenRead(Path.Combine(StorageFolder, file)))
- {
- return stream.Length;
- }
- }
-
- ///
- /// Check the storage limits and return true if they reached.
- /// Storage limits are defined by the number of files and the total size on disk.
- ///
- private void CalculateSize()
- {
- string[] storageFiles = Directory.GetFiles(StorageFolder, "*.*");
-
- _storageCountFiles = storageFiles.Length;
-
- long storageSizeInBytes = 0;
- foreach (string file in storageFiles)
- {
- storageSizeInBytes += GetSize(file);
- }
-
- _storageSize = storageSizeInBytes;
- }
-
- ///
- /// Enqueue is saving a transmission to a tmp file and after a successful write operation it renames it to a
- /// trn file.
- /// A file without a trn extension is ignored by Storage.Peek(), so if a process is taken down before rename
- /// happens
- /// it will stay on the disk forever.
- /// This thread deletes files with the tmp extension that exists on disk for more than 5 minutes.
- ///
- private void DeleteObsoleteFiles()
- {
- try
- {
- IEnumerable files = GetFiles("*.tmp", 50);
- foreach (string file in files)
- {
- DateTime creationTime = File.GetCreationTimeUtc(Path.Combine(StorageFolder, file));
- // if the file is older then 5 minutes - delete it.
- if (DateTime.UtcNow - creationTime >= TimeSpan.FromMinutes(5))
- {
- File.Delete(Path.Combine(StorageFolder, file));
- }
- }
- }
- catch (Exception e)
- {
- PersistenceChannelDebugLog.WriteException(e, "Failed to delete tmp files.");
- }
- }
- }
-}
diff --git a/src/SourceGenerators/Uno.UI.SourceGenerators/Telemetry/PersistenceChannel/StorageTransmission.cs b/src/SourceGenerators/Uno.UI.SourceGenerators/Telemetry/PersistenceChannel/StorageTransmission.cs
deleted file mode 100644
index 0070b3ede03b..000000000000
--- a/src/SourceGenerators/Uno.UI.SourceGenerators/Telemetry/PersistenceChannel/StorageTransmission.cs
+++ /dev/null
@@ -1,133 +0,0 @@
-// Copyright (c) .NET Foundation and contributors. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-//
-// 2019/04/12 (Jerome Laban ):
-// - Extracted from dotnet.exe
-//
-
-using System;
-using System.Globalization;
-using System.IO;
-using System.Threading.Tasks;
-using Microsoft.ApplicationInsights.Channel;
-
-namespace Uno.UI.SourceGenerators.Telemetry.PersistenceChannel
-{
- internal class StorageTransmission : Transmission, IDisposable
- {
- internal Action Disposing;
-
- protected StorageTransmission(string fullPath, Uri address, byte[] content, string contentType,
- string contentEncoding)
- : base(address, content, contentType, contentEncoding)
- {
- FullFilePath = fullPath;
- FileName = Path.GetFileName(fullPath);
- }
-
- internal string FileName { get; }
-
- internal string FullFilePath { get; }
-
- ///
- /// Disposing the storage transmission.
- ///
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- ///
- /// Creates a new transmission from the specified .
- ///
- /// Return transmission loaded from file; return null if the file is corrupted.
- internal static async Task CreateFromStreamAsync(Stream stream, string fileName)
- {
- StreamReader reader = new StreamReader(stream);
- Uri address = await ReadAddressAsync(reader).ConfigureAwait(false);
- string contentType = await ReadHeaderAsync(reader, "Content-Type").ConfigureAwait(false);
- string contentEncoding = await ReadHeaderAsync(reader, "Content-Encoding").ConfigureAwait(false);
- byte[] content = await ReadContentAsync(reader).ConfigureAwait(false);
- return new StorageTransmission(fileName, address, content, contentType, contentEncoding);
- }
-
- ///
- /// Saves the transmission to the specified .
- ///
- internal static async Task SaveAsync(Transmission transmission, Stream stream)
- {
- StreamWriter writer = new StreamWriter(stream);
- try
- {
- await writer.WriteLineAsync(transmission.EndpointAddress.ToString()).ConfigureAwait(false);
- await writer.WriteLineAsync("Content-Type" + ":" + transmission.ContentType).ConfigureAwait(false);
- await writer.WriteLineAsync("Content-Encoding" + ":" + transmission.ContentEncoding)
- .ConfigureAwait(false);
- await writer.WriteLineAsync(string.Empty).ConfigureAwait(false);
- await writer.WriteAsync(Convert.ToBase64String(transmission.Content)).ConfigureAwait(false);
- }
- finally
- {
- writer.Flush();
- }
- }
-
- private static async Task ReadHeaderAsync(TextReader reader, string headerName)
- {
- string line = await reader.ReadLineAsync().ConfigureAwait(false);
- if (string.IsNullOrEmpty(line))
- {
- throw new FormatException(string.Format(CultureInfo.InvariantCulture, "{0} header is expected.",
- headerName));
- }
-
- string[] parts = line.Split(':');
- if (parts.Length != 2)
- {
- throw new FormatException(string.Format(CultureInfo.InvariantCulture,
- "Unexpected header format. {0} header is expected. Actual header: {1}", headerName, line));
- }
-
- if (parts[0] != headerName)
- {
- throw new FormatException(string.Format(CultureInfo.InvariantCulture,
- "{0} header is expected. Actual header: {1}", headerName, line));
- }
-
- return parts[1].Trim();
- }
-
- private static async Task ReadAddressAsync(TextReader reader)
- {
- string addressLine = await reader.ReadLineAsync().ConfigureAwait(false);
- if (string.IsNullOrEmpty(addressLine))
- {
- throw new FormatException("Transmission address is expected.");
- }
-
- Uri address = new Uri(addressLine);
- return address;
- }
-
- private static async Task ReadContentAsync(TextReader reader)
- {
- string content = await reader.ReadToEndAsync().ConfigureAwait(false);
- if (string.IsNullOrEmpty(content) || content == Environment.NewLine)
- {
- throw new FormatException("Content is expected.");
- }
-
- return Convert.FromBase64String(content);
- }
-
- private void Dispose(bool disposing)
- {
- if (disposing)
- {
- Action disposingDelegate = Disposing;
- disposingDelegate?.Invoke(this);
- }
- }
- }
-}
diff --git a/src/SourceGenerators/Uno.UI.SourceGenerators/Telemetry/TelemetryClient.cs b/src/SourceGenerators/Uno.UI.SourceGenerators/Telemetry/TelemetryClient.cs
deleted file mode 100644
index e38c207ae50b..000000000000
--- a/src/SourceGenerators/Uno.UI.SourceGenerators/Telemetry/TelemetryClient.cs
+++ /dev/null
@@ -1,218 +0,0 @@
-// Copyright (c) .NET Foundation and contributors. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-//
-// 2019/04/12 (Jerome Laban ):
-// - Extracted from dotnet.exe
-//
-
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.IO;
-using System.Linq;
-using System.Threading.Tasks;
-using Microsoft.ApplicationInsights;
-using Microsoft.ApplicationInsights.Extensibility;
-using Microsoft.DotNet.PlatformAbstractions;
-
-namespace Uno.UI.SourceGenerators.Telemetry
-{
- public class Telemetry
- {
- internal static string CurrentSessionId;
- private TelemetryClient _client;
- private Dictionary _commonProperties;
- private Dictionary _commonMeasurements;
- private TelemetryConfiguration _telemetryConfig;
- private Task _trackEventTask;
- private string _storageDirectoryPath;
- private string _settingsStorageDirectoryPath;
- private PersistenceChannel.PersistenceChannel _persistenceChannel;
- private const string InstrumentationKey = "9a44058e-1913-4721-a979-9582ab8bedce";
- private const string TelemetryOptout = "UNO_PLATFORM_TELEMETRY_OPTOUT";
-
- public bool Enabled { get; }
-
- public Telemetry() : this(null) { }
-
- public Telemetry(Func enabledProvider) : this(null, enabledProvider: enabledProvider) { }
-
- public Telemetry(
- string sessionId,
- bool blockThreadInitialization = false,
- Func enabledProvider = null)
- {
- if (bool.TryParse(Environment.GetEnvironmentVariable(TelemetryOptout), out var telemetryOptOut))
- {
- Enabled = !telemetryOptOut;
- }
- else
- {
- Enabled = !enabledProvider?.Invoke() ?? true;
- }
-
- if (!Enabled)
- {
- return;
- }
-
- // Store the session ID in a static field so that it can be reused
- CurrentSessionId = sessionId ?? Guid.NewGuid().ToString();
- if (blockThreadInitialization)
- {
- InitializeTelemetry();
- }
- else
- {
- //initialize in task to offload to parallel thread
- _trackEventTask = Task.Run(() => InitializeTelemetry());
- }
- }
-
- public void TrackEvent(
- string eventName,
- (string key, string value)[] properties,
- (string key, double value)[] measurements)
- => TrackEvent(eventName, properties?.ToDictionary(p => p.key, p => p.value), measurements?.ToDictionary(p => p.key, p => p.value));
-
- public void TrackEvent(string eventName, IDictionary properties,
- IDictionary measurements)
- {
- if (!Enabled)
- {
- return;
- }
-
- //continue the task in different threads
- _trackEventTask = _trackEventTask.ContinueWith(
- x => TrackEventTask(eventName, properties, measurements)
- );
- }
-
- public void Flush()
- {
- if (!Enabled || _trackEventTask == null)
- {
- return;
- }
-
- // Skip the wait if the task has not yet been activated
- if (_trackEventTask.Status != TaskStatus.WaitingForActivation)
- {
- _trackEventTask.Wait(TimeSpan.FromSeconds(1));
- }
- }
-
- public void Dispose()
- {
- _persistenceChannel?.Dispose();
- _telemetryConfig?.Dispose();
- }
-
- public void ThreadBlockingTrackEvent(string eventName, IDictionary properties, IDictionary measurements)
- {
- if (!Enabled)
- {
- return;
- }
- TrackEventTask(eventName, properties, measurements);
- }
-
- private void InitializeTelemetry()
- {
- try
- {
- _storageDirectoryPath = Path.Combine(Path.GetTempPath(), ".uno", "telemetry");
-
- // Store the settings on in the user profile for linux
- if (RuntimeEnvironment.OperatingSystemPlatform == Platform.Linux)
- {
- _settingsStorageDirectoryPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".uno", "telemetry");
- }
- else
- {
- _settingsStorageDirectoryPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Uno Platform", "telemetry");
- }
-
- _persistenceChannel = new PersistenceChannel.PersistenceChannel(
- storageDirectoryPath: _storageDirectoryPath);
-
- _persistenceChannel.SendingInterval = TimeSpan.FromMilliseconds(1);
-
- _commonProperties = new TelemetryCommonProperties(_settingsStorageDirectoryPath).GetTelemetryCommonProperties();
- _commonMeasurements = new Dictionary();
-
- _telemetryConfig = new TelemetryConfiguration { InstrumentationKey = InstrumentationKey };
- _client = new TelemetryClient(_telemetryConfig);
- _client.InstrumentationKey = InstrumentationKey;
- _client.Context.User.Id = _commonProperties[TelemetryCommonProperties.MachineId];
- _client.Context.Session.Id = CurrentSessionId;
- _client.Context.Device.OperatingSystem = RuntimeEnvironment.OperatingSystem;
- }
- catch (Exception e)
- {
- _client = null;
- // we don't want to fail the tool if telemetry fails.
- Debug.Fail(e.ToString());
- }
- }
-
- private void TrackEventTask(
- string eventName,
- IDictionary properties,
- IDictionary measurements)
- {
- if (_client == null)
- {
- return;
- }
-
- try
- {
- var eventProperties = GetEventProperties(properties);
- var eventMeasurements = GetEventMeasures(measurements);
-
- _client.TrackEvent(PrependProducerNamespace(eventName), eventProperties, eventMeasurements);
- }
- catch (Exception e)
- {
- Debug.Fail(e.ToString());
- }
- }
-
- private static string PrependProducerNamespace(string eventName)
- {
- return "uno/generation/" + eventName;
- }
-
- private Dictionary GetEventMeasures(IDictionary measurements)
- {
- var eventMeasurements = new Dictionary(_commonMeasurements);
- if (measurements != null)
- {
- foreach (var measurement in measurements)
- {
- eventMeasurements[measurement.Key] = measurement.Value;
- }
- }
- return eventMeasurements;
- }
-
- private Dictionary GetEventProperties(IDictionary properties)
- {
- if (properties != null)
- {
- var eventProperties = new Dictionary(_commonProperties);
- foreach (var property in properties)
- {
- eventProperties[property.Key] = property.Value;
- }
- return eventProperties;
- }
- else
- {
- return _commonProperties;
- }
- }
- }
-}
diff --git a/src/SourceGenerators/Uno.UI.SourceGenerators/Telemetry/TelemetryCommonProperties.cs b/src/SourceGenerators/Uno.UI.SourceGenerators/Telemetry/TelemetryCommonProperties.cs
deleted file mode 100644
index 977160813fc2..000000000000
--- a/src/SourceGenerators/Uno.UI.SourceGenerators/Telemetry/TelemetryCommonProperties.cs
+++ /dev/null
@@ -1,155 +0,0 @@
-// Copyright (c) .NET Foundation and contributors. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-//
-// 2019/04/12 (Jerome Laban ):
-// - Extracted from dotnet.exe
-//
-
-using System;
-using System.Collections.Generic;
-using Microsoft.DotNet.PlatformAbstractions;
-using System.IO;
-using System.Security;
-using Microsoft.Win32;
-using System.Linq;
-using RuntimeEnvironment = Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment;
-using RuntimeInformation = System.Runtime.InteropServices.RuntimeInformation;
-using System.Runtime.InteropServices;
-using System.Diagnostics;
-using System.Reflection;
-using Uno.UI.SourceGenerators.Helpers;
-using System.Security.Cryptography;
-using System.Net.NetworkInformation;
-
-namespace Uno.UI.SourceGenerators.Telemetry
-{
- internal class TelemetryCommonProperties
- {
- public TelemetryCommonProperties(
- string storageDirectoryPath,
- Func getCurrentDirectory = null)
- {
- _getCurrentDirectory = getCurrentDirectory ?? Directory.GetCurrentDirectory;
- _storageDirectoryPath = storageDirectoryPath;
- }
-
- private Func _getCurrentDirectory;
- private string _storageDirectoryPath;
- public const string OSVersion = "OS Version";
- public const string OSPlatform = "OS Platform";
- public const string OutputRedirected = "Output Redirected";
- public const string RuntimeId = "Runtime Id";
- public const string MachineId = "Machine ID";
- public const string ProductVersion = "Product Version";
- public const string TelemetryProfile = "Telemetry Profile";
- public const string CurrentPathHash = "Current Path Hash";
- public const string KernelVersion = "Kernel Version";
-
- private const string TelemetryProfileEnvironmentVariable = "DOTNET_CLI_TELEMETRY_PROFILE";
-
- public Dictionary GetTelemetryCommonProperties() => new Dictionary
- {
- {OSVersion, RuntimeEnvironment.OperatingSystemVersion},
- {OSPlatform, RuntimeEnvironment.OperatingSystemPlatform.ToString()},
- {OutputRedirected, Console.IsOutputRedirected.ToString()},
- {RuntimeId, RuntimeEnvironment.GetRuntimeIdentifier()},
- {ProductVersion, GetProductVersion()},
- {TelemetryProfile, Environment.GetEnvironmentVariable(TelemetryProfileEnvironmentVariable)},
- {MachineId, GetMachineId()},
- {CurrentPathHash, HashBuilder.Build(_getCurrentDirectory())},
- {KernelVersion, GetKernelVersion()},
- };
-
- private string GetMachineId()
- {
- var machineHashPath = Path.Combine(_storageDirectoryPath, ".machinehash");
-
- if (File.Exists(machineHashPath))
- {
- if (File.ReadAllText(machineHashPath) is { Length: 32 /* hash */ or 36 /* guid */ } readHash)
- {
- return readHash;
- }
- }
-
- string hash = null;
- try
- {
- var macAddr =
- (
- from nic in NetworkInterface.GetAllNetworkInterfaces()
- where nic.OperationalStatus == OperationalStatus.Up
- select nic.GetPhysicalAddress().ToString()
- ).FirstOrDefault();
-
- hash = HashBuilder.Build(macAddr);
-
- if (!Directory.Exists(Path.GetDirectoryName(machineHashPath)))
- {
- Directory.CreateDirectory(Path.GetDirectoryName(machineHashPath));
- }
-
- File.WriteAllText(machineHashPath, hash);
- }
- catch (Exception e)
- {
- Debug.Fail($"Failed to get Mac address: {e}");
-
- // if the hash was set, but the write failed, let's continue.
- hash ??= Guid.NewGuid().ToString();
- }
-
- return hash;
- }
-
- private string GetProductVersion()
- {
- if (this.GetType().Assembly.GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute)).FirstOrDefault() is AssemblyInformationalVersionAttribute attribute)
- {
- return attribute.InformationalVersion;
- }
-
- return "Unknown";
- }
-
-
- ///
- /// Returns a string identifying the OS kernel.
- /// For Unix this currently comes from "uname -srv".
- /// For Windows this currently comes from RtlGetVersion().
- ///
- /// Here are some example values:
- ///
- /// Alpine.36 Linux 4.9.60-linuxkit-aufs #1 SMP Mon Nov 6 16:00:12 UTC 2017
- /// Centos.73 Linux 3.10.0-514.26.2.el7.x86_64 #1 SMP Tue Jul 4 15:04:05 UTC 2017
- /// Debian.87 Linux 3.16.0-4-amd64 #1 SMP Debian 3.16.39-1+deb8u2 (2017-03-07)
- /// Debian.90 Linux 4.9.0-2-amd64 #1 SMP Debian 4.9.18-1 (2017-03-30)
- /// fedora.25 Linux 4.11.3-202.fc25.x86_64 #1 SMP Mon Jun 5 16:38:21 UTC 2017
- /// Fedora.26 Linux 4.14.15-200.fc26.x86_64 #1 SMP Wed Jan 24 04:26:15 UTC 2018
- /// Fedora.27 Linux 4.14.14-300.fc27.x86_64 #1 SMP Fri Jan 19 13:19:54 UTC 2018
- /// OpenSuse.423 Linux 4.4.104-39-default #1 SMP Thu Jan 4 08:11:03 UTC 2018 (7db1912)
- /// RedHat.69 Linux 2.6.32-696.20.1.el6.x86_64 #1 SMP Fri Jan 12 15:07:59 EST 2018
- /// RedHat.72 Linux 3.10.0-514.21.1.el7.x86_64 #1 SMP Sat Apr 22 02:41:35 EDT 2017
- /// RedHat.73 Linux 3.10.0-514.21.1.el7.x86_64 #1 SMP Sat Apr 22 02:41:35 EDT 2017
- /// SLES.12 Linux 4.4.103-6.38-default #1 SMP Mon Dec 25 20:44:33 UTC 2017 (e4b9067)
- /// suse.422 Linux 4.4.49-16-default #1 SMP Sun Feb 19 17:40:35 UTC 2017 (70e9954)
- /// Ubuntu.1404 Linux 3.19.0-65-generic #73~14.04.1-Ubuntu SMP Wed Jun 29 21:05:22 UTC 2016
- /// Ubuntu.1604 Linux 4.13.0-1005-azure #7-Ubuntu SMP Mon Jan 8 21:37:36 UTC 2018
- /// Ubuntu.1604.WSL Linux 4.4.0-43-Microsoft #1-Microsoft Wed Dec 31 14:42:53 PST 2014
- /// Ubuntu.1610 Linux 4.8.0-45-generic #48-Ubuntu SMP Fri Mar 24 11:46:39 UTC 2017
- /// Ubuntu.1704 Linux 4.10.0-19-generic #21-Ubuntu SMP Thu Apr 6 17:04:57 UTC 2017
- /// Ubuntu.1710 Linux 4.13.0-25-generic #29-Ubuntu SMP Mon Jan 8 21:14:41 UTC 2018
- /// OSX1012 Darwin 16.7.0 Darwin Kernel Version 16.7.0: Thu Jan 11 22:59:40 PST 2018; root:xnu-3789.73.8~1/RELEASE_X86_64
- /// OSX1013 Darwin 17.4.0 Darwin Kernel Version 17.4.0: Sun Dec 17 09:19:54 PST 2017; root:xnu-4570.41.2~1/RELEASE_X86_64
- /// Windows.10 Microsoft Windows 10.0.14393
- /// Windows.10.Core Microsoft Windows 10.0.14393
- /// Windows.10.Nano Microsoft Windows 10.0.14393
- /// Windows.7 Microsoft Windows 6.1.7601 S
- /// Windows.81 Microsoft Windows 6.3.9600
- ///
- private static string GetKernelVersion()
- {
- return RuntimeInformation.OSDescription;
- }
- }
-}
diff --git a/src/SourceGenerators/Uno.UI.SourceGenerators/Uno.UI.SourceGenerators.csproj b/src/SourceGenerators/Uno.UI.SourceGenerators/Uno.UI.SourceGenerators.csproj
index 483b92d29ba2..2675e89377fa 100644
--- a/src/SourceGenerators/Uno.UI.SourceGenerators/Uno.UI.SourceGenerators.csproj
+++ b/src/SourceGenerators/Uno.UI.SourceGenerators/Uno.UI.SourceGenerators.csproj
@@ -12,8 +12,7 @@
-
-
+
diff --git a/src/SourceGenerators/Uno.UI.SourceGenerators/XamlGenerator/XamlCodeGeneration.Telemetry.cs b/src/SourceGenerators/Uno.UI.SourceGenerators/XamlGenerator/XamlCodeGeneration.Telemetry.cs
index f04a169f51b3..ae4596b71890 100644
--- a/src/SourceGenerators/Uno.UI.SourceGenerators/XamlGenerator/XamlCodeGeneration.Telemetry.cs
+++ b/src/SourceGenerators/Uno.UI.SourceGenerators/XamlGenerator/XamlCodeGeneration.Telemetry.cs
@@ -9,18 +9,21 @@
using Uno.Roslyn;
using Microsoft.CodeAnalysis;
using Uno.Extensions;
-using Uno.UI.SourceGenerators.Telemetry;
+using Uno.DevTools.Telemetry;
namespace Uno.UI.SourceGenerators.XamlGenerator
{
internal partial class XamlCodeGeneration
{
- private Telemetry.Telemetry _telemetry;
+ private const string InstrumentationKey = "9a44058e-1913-4721-a979-9582ab8bedce";
+
+ private Telemetry _telemetry;
public bool IsRunningCI =>
!Environment.GetEnvironmentVariable("TF_BUILD").IsNullOrEmpty() // https://docs.microsoft.com/en-us/azure/devops/pipelines/build/variables?tabs=yaml&view=azure-devops#system-variables
|| !Environment.GetEnvironmentVariable("TRAVIS").IsNullOrEmpty() // https://docs.travis-ci.com/user/environment-variables/#default-environment-variables
|| !Environment.GetEnvironmentVariable("JENKINS_URL").IsNullOrEmpty() // https://wiki.jenkins.io/display/JENKINS/Building+a+software+project#Buildingasoftwareproject-belowJenkinsSetEnvironmentVariables
+ || !Environment.GetEnvironmentVariable("GITHUB_REPOSITORY").IsNullOrEmpty() // https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/store-information-in-variables#default-environment-variables
|| !Environment.GetEnvironmentVariable("APPVEYOR").IsNullOrEmpty(); // https://www.appveyor.com/docs/environment-variables/
private void InitTelemetry(GeneratorExecutionContext context)
@@ -32,7 +35,24 @@ private void InitTelemetry(GeneratorExecutionContext context)
|| telemetryOptOut.Equals("1", StringComparison.OrdinalIgnoreCase)
|| _isDesignTimeBuild;
- _telemetry = new Telemetry.Telemetry(isTelemetryOptout);
+ string getCurrentDirectory()
+ {
+ var solutionDir = context.GetMSBuildPropertyValue("SolutionDir");
+ if (!string.IsNullOrEmpty(solutionDir))
+ {
+ return solutionDir;
+ }
+
+ var projectDir = context.GetMSBuildPropertyValue("MSBuildProjectFullPath");
+ if (!string.IsNullOrEmpty(projectDir))
+ {
+ return projectDir;
+ }
+
+ return Environment.CurrentDirectory;
+ }
+
+ _telemetry = new Telemetry(InstrumentationKey, enabledProvider: isTelemetryOptout, currentDirectoryProvider: getCurrentDirectory);
}
private bool IsTelemetryEnabled => _telemetry?.Enabled ?? false;
diff --git a/src/SourceGenerators/Uno.UI.SourceGenerators/XamlGenerator/XamlCodeGeneration.cs b/src/SourceGenerators/Uno.UI.SourceGenerators/XamlGenerator/XamlCodeGeneration.cs
index ad2efa1db815..9c46c9a78a15 100644
--- a/src/SourceGenerators/Uno.UI.SourceGenerators/XamlGenerator/XamlCodeGeneration.cs
+++ b/src/SourceGenerators/Uno.UI.SourceGenerators/XamlGenerator/XamlCodeGeneration.cs
@@ -12,7 +12,7 @@
using Uno.Roslyn;
using Microsoft.CodeAnalysis;
using Uno.Extensions;
-using Uno.UI.SourceGenerators.Telemetry;
+using Uno.DevTools.Telemetry;
using Uno.UI.Xaml;
using System.Drawing;
using __uno::Uno.Xaml;
diff --git a/src/SourceGenerators/Uno.UI.SourceGenerators/XamlGenerator/XamlCodeGenerator.cs b/src/SourceGenerators/Uno.UI.SourceGenerators/XamlGenerator/XamlCodeGenerator.cs
index 0db416f50d22..e0de4c87b13f 100644
--- a/src/SourceGenerators/Uno.UI.SourceGenerators/XamlGenerator/XamlCodeGenerator.cs
+++ b/src/SourceGenerators/Uno.UI.SourceGenerators/XamlGenerator/XamlCodeGenerator.cs
@@ -8,7 +8,7 @@
using Microsoft.CodeAnalysis;
using Uno.Roslyn;
using Uno.UI.SourceGenerators.Helpers;
-using Uno.UI.SourceGenerators.Telemetry;
+using Uno.DevTools.Telemetry;
namespace Uno.UI.SourceGenerators.XamlGenerator
{