Merge pull request #9370 from umbraco/v8/feature/anon-telemetry
Anonymous telemetry
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -175,3 +175,4 @@ cypress.env.json
|
||||
/src/Umbraco.Tests.AcceptanceTest/package-lock.json
|
||||
/src/Umbraco.Tests.AcceptanceTest/cypress/videos/
|
||||
/src/Umbraco.Tests.AcceptanceTest/cypress/screenshots/
|
||||
src/Umbraco.Web.UI/Umbraco/telemetrics-id.umb
|
||||
|
||||
@@ -7,6 +7,8 @@ namespace Umbraco.Core.IO
|
||||
{
|
||||
public static string TinyMceConfig => SystemDirectories.Config + "/tinyMceConfig.config";
|
||||
|
||||
public static string TelemetricsIdentifier => SystemDirectories.Data + "/telemetrics-id.umb";
|
||||
|
||||
// TODO: Kill this off we don't have umbraco.config XML cache we now have NuCache
|
||||
public static string GetContentCacheXml(IGlobalSettings globalSettings)
|
||||
{
|
||||
|
||||
@@ -199,7 +199,7 @@ namespace Umbraco.Core.Migrations.Upgrade
|
||||
// to 8.9.0
|
||||
To<ExternalLoginTableUserData>("{B5838FF5-1D22-4F6C-BCEB-F83ACB14B575}");
|
||||
|
||||
// to 8.10.0...
|
||||
// to 8.10.0
|
||||
To<AddPropertyTypeLabelOnTopColumn>("{D6A8D863-38EC-44FB-91EC-ACD6A668BD18}");
|
||||
|
||||
//FINAL
|
||||
|
||||
132
src/Umbraco.Web/Telemetry/ReportSiteTask.cs
Normal file
132
src/Umbraco.Web/Telemetry/ReportSiteTask.cs
Normal file
@@ -0,0 +1,132 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Web.Scheduling;
|
||||
|
||||
namespace Umbraco.Web.Telemetry
|
||||
{
|
||||
public class ReportSiteTask : RecurringTaskBase
|
||||
{
|
||||
private IProfilingLogger _logger;
|
||||
private static HttpClient _httpClient;
|
||||
|
||||
public ReportSiteTask(IBackgroundTaskRunner<RecurringTaskBase> runner, int delayBeforeWeStart, int howOftenWeRepeat, IProfilingLogger logger)
|
||||
: base(runner, delayBeforeWeStart, howOftenWeRepeat)
|
||||
{
|
||||
_logger = logger;
|
||||
_httpClient = new HttpClient();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the background task to send the anynomous ID
|
||||
/// to telemetry service
|
||||
/// </summary>
|
||||
/// <returns>A value indicating whether to repeat the task.</returns>
|
||||
public override async Task<bool> PerformRunAsync(CancellationToken token)
|
||||
{
|
||||
// Try & find file at '/umbraco/telemetrics-id.umb'
|
||||
var telemetricsFilePath = IOHelper.MapPath(SystemFiles.TelemetricsIdentifier);
|
||||
|
||||
if (File.Exists(telemetricsFilePath) == false)
|
||||
{
|
||||
// Some users may have decided to not be tracked by deleting/removing the marker file
|
||||
_logger.Warn<ReportSiteTask>("No telemetry marker file found at '{filePath}' and will not report site to telemetry service", telemetricsFilePath);
|
||||
|
||||
// Stop repeating this task (no need to keep checking)
|
||||
// The only time it will recheck when the site is recycled
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
var telemetricsFileContents = string.Empty;
|
||||
try
|
||||
{
|
||||
// Open file & read its contents
|
||||
// It may throw due to file permissions or file locking
|
||||
telemetricsFileContents = File.ReadAllText(telemetricsFilePath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Silently swallow ex - but lets log it (ReadAllText throws a ton of different types of ex)
|
||||
// Hence the use of general exception type
|
||||
_logger.Error<ReportSiteTask>(ex, "Error in reading file contents of telemetry marker file found at '{filePath}'", telemetricsFilePath);
|
||||
|
||||
// Exit out early, but mark this task to be repeated in case its a file lock so it can be rechecked the next time round
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Parse as a GUID & verify its a GUID and not some random string
|
||||
// In case of users may have messed or decided to empty the file contents or put in something random
|
||||
if (Guid.TryParse(telemetricsFileContents, out var telemetrySiteIdentifier) == false)
|
||||
{
|
||||
// Some users may have decided to mess with file contents
|
||||
_logger.Warn<ReportSiteTask>("The telemetry marker file found at '{filePath}' with '{telemetrySiteId}' is not a valid identifier for the telemetry service", telemetricsFilePath, telemetrySiteIdentifier);
|
||||
|
||||
// Stop repeating this task (no need to keep checking)
|
||||
// The only time it will recheck when the site is recycled
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
// Send data to LIVE telemetry
|
||||
_httpClient.BaseAddress = new Uri("https://telemetry.umbraco.com/");
|
||||
|
||||
#if DEBUG
|
||||
// Send data to DEBUG telemetry service
|
||||
_httpClient.BaseAddress = new Uri("https://telemetry.rainbowsrock.net/");
|
||||
#endif
|
||||
|
||||
_httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json");
|
||||
|
||||
using (var request = new HttpRequestMessage(HttpMethod.Post, "installs/"))
|
||||
{
|
||||
var postData = new TelemetryReportData { Id = telemetrySiteIdentifier, Version = UmbracoVersion.SemanticVersion.ToSemanticString() };
|
||||
request.Content = new StringContent(JsonConvert.SerializeObject(postData), Encoding.UTF8, "application/json"); //CONTENT-TYPE header
|
||||
|
||||
// Set a low timeout - no need to use a larger default timeout for this POST request
|
||||
_httpClient.Timeout = new TimeSpan(0, 0, 1);
|
||||
|
||||
// Make a HTTP Post to telemetry service
|
||||
// https://telemetry.umbraco.com/installs/
|
||||
// Fire & Forget, do not need to know if its a 200, 500 etc
|
||||
var result = await _httpClient.SendAsync(request);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Silently swallow
|
||||
// The user does not need the logs being polluted if our service has fallen over or is down etc
|
||||
// Hence only loggigng this at a more verbose level (Which users should not be using in prod)
|
||||
_logger.Debug<ReportSiteTask>("There was a problem sending a request to the Umbraco telemetry service");
|
||||
}
|
||||
|
||||
// Keep recurring this task & pinging the telemetry service
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool IsAsync => true;
|
||||
|
||||
|
||||
[DataContract]
|
||||
private class TelemetryReportData
|
||||
{
|
||||
[DataMember(Name = "id")]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
[DataMember(Name = "version")]
|
||||
public string Version { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
34
src/Umbraco.Web/Telemetry/TelemetryComponent.cs
Normal file
34
src/Umbraco.Web/Telemetry/TelemetryComponent.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Web.Scheduling;
|
||||
|
||||
namespace Umbraco.Web.Telemetry
|
||||
{
|
||||
public class TelemetryComponent : IComponent
|
||||
{
|
||||
private IProfilingLogger _logger;
|
||||
private BackgroundTaskRunner<IBackgroundTask> _telemetryReporterRunner;
|
||||
|
||||
public TelemetryComponent(IProfilingLogger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
// backgrounds runners are web aware, if the app domain dies, these tasks will wind down correctly
|
||||
_telemetryReporterRunner = new BackgroundTaskRunner<IBackgroundTask>("TelemetryReporter", _logger);
|
||||
|
||||
int delayBeforeWeStart = 60 * 1000; // 60 * 1000ms = 1min (60,000)
|
||||
int howOftenWeRepeat = 60 * 1000 * 60 * 24; // 60 * 1000 * 60 * 24 = 24hrs (86400000)
|
||||
|
||||
// As soon as we add our task to the runner it will start to run (after its delay period)
|
||||
var task = new ReportSiteTask(_telemetryReporterRunner, delayBeforeWeStart, howOftenWeRepeat, _logger);
|
||||
_telemetryReporterRunner.TryAdd(task);
|
||||
}
|
||||
|
||||
public void Terminate()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
10
src/Umbraco.Web/Telemetry/TelemetryComposer.cs
Normal file
10
src/Umbraco.Web/Telemetry/TelemetryComposer.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Composing;
|
||||
|
||||
namespace Umbraco.Web.Telemetry
|
||||
{
|
||||
|
||||
[RuntimeLevel(MinLevel = RuntimeLevel.Run)]
|
||||
public class TelemetryComposer : ComponentComposer<TelemetryComponent>, ICoreComposer
|
||||
{ }
|
||||
}
|
||||
60
src/Umbraco.Web/Telemetry/TelemetryMarkerComponent.cs
Normal file
60
src/Umbraco.Web/Telemetry/TelemetryMarkerComponent.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
|
||||
namespace Umbraco.Web.Telemetry
|
||||
{
|
||||
public class TelemetryMarkerComponent : IComponent
|
||||
{
|
||||
private IProfilingLogger _logger;
|
||||
private IRuntimeState _runtime;
|
||||
|
||||
public TelemetryMarkerComponent(IProfilingLogger logger, IRuntimeState runtime)
|
||||
{
|
||||
_logger = logger;
|
||||
_runtime = runtime;
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
var telemetricsFilePath = IOHelper.MapPath(SystemFiles.TelemetricsIdentifier);
|
||||
|
||||
// Verify file does not exist already (if we are upgrading)
|
||||
// In a clean install we know it would not exist
|
||||
// If the site is upgraded and the file was removed it would re-create one
|
||||
// NOTE: If user removed the marker file to opt out it would re-create a new guid marker file & potentially skew
|
||||
if (_runtime.Level == RuntimeLevel.Upgrade && File.Exists(telemetricsFilePath))
|
||||
{
|
||||
_logger.Warn<TelemetryMarkerComponent>("When upgrading the anonymous telemetry file already existsed on disk at {filePath}", telemetricsFilePath);
|
||||
return;
|
||||
}
|
||||
else if (_runtime.Level == RuntimeLevel.Install && File.Exists(telemetricsFilePath))
|
||||
{
|
||||
// No need to log for when level is install if file exists (As this component hit several times during install process)
|
||||
return;
|
||||
}
|
||||
|
||||
// We are a clean install or an upgrade without the marker file
|
||||
// Generate GUID
|
||||
var telemetrySiteIdentifier = Guid.NewGuid();
|
||||
|
||||
// Write file contents
|
||||
try
|
||||
{
|
||||
File.WriteAllText(telemetricsFilePath, telemetrySiteIdentifier.ToString());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error<TelemetryMarkerComponent>(ex, "Unable to create telemetry file at {filePath}", telemetricsFilePath);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void Terminate()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
9
src/Umbraco.Web/Telemetry/TelemetryMarkerComposer.cs
Normal file
9
src/Umbraco.Web/Telemetry/TelemetryMarkerComposer.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Composing;
|
||||
|
||||
namespace Umbraco.Web.Telemetry
|
||||
{
|
||||
[RuntimeLevel(MinLevel = RuntimeLevel.Install, MaxLevel = RuntimeLevel.Upgrade)]
|
||||
public class TelemetryMarkerComposer : ComponentComposer<TelemetryMarkerComponent>, ICoreComposer
|
||||
{ }
|
||||
}
|
||||
@@ -295,6 +295,11 @@
|
||||
<Compile Include="Models\Link.cs" />
|
||||
<Compile Include="Models\LinkType.cs" />
|
||||
<Compile Include="Models\TemplateQuery\OperatorFactory.cs" />
|
||||
<Compile Include="Telemetry\ReportSiteTask.cs" />
|
||||
<Compile Include="Telemetry\TelemetryMarkerComponent.cs" />
|
||||
<Compile Include="Telemetry\TelemetryComponent.cs" />
|
||||
<Compile Include="Telemetry\TelemetryComposer.cs" />
|
||||
<Compile Include="Telemetry\TelemetryMarkerComposer.cs" />
|
||||
<Compile Include="Templates\HtmlLocalLinkParser.cs" />
|
||||
<Compile Include="Templates\HtmlImageSourceParser.cs" />
|
||||
<Compile Include="Templates\HtmlUrlParser.cs" />
|
||||
|
||||
Reference in New Issue
Block a user