initial commit

This commit is contained in:
Jo 2025-01-07 02:06:59 +01:00
parent 6715289efe
commit 788c3389af
37645 changed files with 2526849 additions and 80 deletions

View file

@ -0,0 +1,120 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Threading.Tasks;
using Unity.Services.Core.Configuration.Internal;
using Unity.Services.Core.Environments.Internal;
using Unity.Services.Core.Scheduler.Internal;
using UnityEngine;
namespace Unity.Services.Core.Telemetry.Internal
{
/// <summary>
/// Handles common logic between all <see cref="Diagnostics"/> instances.
/// </summary>
class DiagnosticsHandler : TelemetryHandler<DiagnosticsPayload, Diagnostic>
{
class SendState
{
public DiagnosticsHandler Self;
public CachedPayload<DiagnosticsPayload> Payload;
}
public DiagnosticsHandler(
TelemetryConfig config, CachedPayload<DiagnosticsPayload> cache, IActionScheduler scheduler,
ICachePersister<DiagnosticsPayload> cachePersister, TelemetrySender sender)
: base(config, cache, scheduler, cachePersister, sender) {}
internal override void SendPersistedCache(CachedPayload<DiagnosticsPayload> persistedCache)
{
var sendAsync = m_Sender.SendAsync(persistedCache.Payload);
m_CachePersister.Delete();
var localState = new SendState
{
Self = this,
Payload = new CachedPayload<DiagnosticsPayload>
{
TimeOfOccurenceTicks = persistedCache.TimeOfOccurenceTicks,
Payload = persistedCache.Payload,
},
};
sendAsync.ContinueWith(OnSendAsyncCompleted, localState, TaskContinuationOptions.ExecuteSynchronously);
}
static void OnSendAsyncCompleted(Task sendOperation, object state)
{
if (!(state is SendState castState))
{
throw new ArgumentException("The given state is invalid.");
}
switch (sendOperation.Status)
{
case TaskStatus.Canceled:
case TaskStatus.Faulted:
{
castState.Self.Cache.AddRangeFrom(castState.Payload);
break;
}
case TaskStatus.RanToCompletion:
{
break;
}
default:
throw new ArgumentOutOfRangeException(
nameof(sendOperation.Status), "Can't continue without the send operation being completed.");
}
}
internal override void FetchSpecificCommonTags(ICloudProjectId cloudProjectId, IEnvironments environments)
{
var commonTags = Cache.Payload.DiagnosticsCommonTags;
commonTags.Clear();
commonTags[TagKeys.ApplicationVersion] = Application.version;
commonTags[TagKeys.ProductName] = Application.productName;
commonTags[TagKeys.CloudProjectId] = cloudProjectId.GetCloudProjectId();
commonTags[TagKeys.EnvironmentName] = environments.Current;
commonTags[TagKeys.ApplicationGenuine] = Application.genuineCheckAvailable
? Application.genuine.ToString(CultureInfo.InvariantCulture)
: "unavailable";
commonTags[TagKeys.InternetReachability] = Application.internetReachability.ToString();
}
internal override void SendCachedPayload()
{
if (Cache.IsEmpty())
return;
var sendAsync = m_Sender.SendAsync(Cache.Payload);
var localState = new SendState
{
Self = this,
Payload = new CachedPayload<DiagnosticsPayload>
{
TimeOfOccurenceTicks = Cache.TimeOfOccurenceTicks,
Payload = new DiagnosticsPayload
{
Diagnostics = new List<Diagnostic>(Cache.Payload.Diagnostics),
CommonTags = new Dictionary<string, string>(Cache.Payload.CommonTags),
DiagnosticsCommonTags = new Dictionary<string, string>(Cache.Payload.DiagnosticsCommonTags),
},
},
};
Cache.TimeOfOccurenceTicks = 0;
Cache.Payload.Diagnostics.Clear();
if (m_CachePersister.CanPersist)
{
m_CachePersister.Delete();
}
sendAsync.ContinueWith(OnSendAsyncCompleted, localState, TaskContinuationOptions.ExecuteSynchronously);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 34f5fe0d07586744aa92a02821c71e8d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,61 @@
using System;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Utilities;
using Unity.Services.Core.Configuration.Internal;
using Unity.Services.Core.Environments.Internal;
using Unity.Services.Core.Scheduler.Internal;
namespace Unity.Services.Core.Telemetry.Internal
{
/// <summary>
/// Handles common logic between all <see cref="Metrics"/> instances.
/// </summary>
class MetricsHandler : TelemetryHandler<MetricsPayload, Metric>
{
public MetricsHandler(
TelemetryConfig config, CachedPayload<MetricsPayload> cache, IActionScheduler scheduler,
ICachePersister<MetricsPayload> cachePersister, TelemetrySender sender)
: base(config, cache, scheduler, cachePersister, sender)
{
// prevent .ctor of StringEnumConverter from being stripped
AotHelper.EnsureType<StringEnumConverter>();
}
internal override void SendPersistedCache(CachedPayload<MetricsPayload> persistedCache)
{
if (!AreMetricsOutdated())
{
m_Sender.SendAsync(persistedCache.Payload);
}
m_CachePersister.Delete();
bool AreMetricsOutdated()
{
var differenceFromUtcNow = DateTime.UtcNow - new DateTime(persistedCache.TimeOfOccurenceTicks);
return differenceFromUtcNow.TotalSeconds > Config.PayloadExpirationSeconds;
}
}
internal override void FetchSpecificCommonTags(ICloudProjectId cloudProjectId, IEnvironments environments)
{
Cache.Payload.MetricsCommonTags.Clear();
}
internal override void SendCachedPayload()
{
if (Cache.Payload.Metrics.Count <= 0)
return;
m_Sender.SendAsync(Cache.Payload);
Cache.Payload.Metrics.Clear();
Cache.TimeOfOccurenceTicks = 0;
if (m_CachePersister.CanPersist)
{
m_CachePersister.Delete();
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 19ff1201e729ddc4d8128534ec9c435c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,168 @@
#if UNITY_ANDROID && !UNITY_EDITOR
using System;
#endif
using Newtonsoft.Json;
using Unity.Services.Core.Configuration.Internal;
using Unity.Services.Core.Environments.Internal;
using Unity.Services.Core.Internal;
using Unity.Services.Core.Scheduler.Internal;
using UnityEngine;
namespace Unity.Services.Core.Telemetry.Internal
{
/// <remarks>
/// A non-generic version of the class to hold non-generic static code in order to
/// avoid unnecessary duplication that happens with static members in generic classes.
/// </remarks>
abstract class TelemetryHandler
{
internal static string FormatOperatingSystemInfo(string rawOsInfo)
{
#if UNITY_ANDROID && !UNITY_EDITOR
//Android's os data is formatted as follow:
//"<Device system name> <Device system version> / API-<API level> (<ID>/<Version incremental>)"
//eg. "Android OS 10 / API-29 (HONORHRY-LX1T/10.0.0.200C636)"
var trimmedOsInfoSize = rawOsInfo.LastIndexOf(" (", StringComparison.Ordinal);
if (trimmedOsInfoSize < 0)
return rawOsInfo;
var osTag = rawOsInfo.Substring(0, trimmedOsInfoSize);
return osTag;
#else
return rawOsInfo;
#endif
}
}
abstract class TelemetryHandler<TPayload, TEvent> : TelemetryHandler
where TPayload : ITelemetryPayload
where TEvent : ITelemetryEvent
{
readonly IActionScheduler m_Scheduler;
protected readonly ICachePersister<TPayload> m_CachePersister;
protected readonly TelemetrySender m_Sender;
internal long SendingLoopScheduleId;
internal long PersistenceLoopScheduleId;
public TelemetryConfig Config { get; }
public CachedPayload<TPayload> Cache { get; }
protected TelemetryHandler(
TelemetryConfig config, CachedPayload<TPayload> cache, IActionScheduler scheduler,
ICachePersister<TPayload> cachePersister, TelemetrySender sender)
{
Config = config;
Cache = cache;
m_Scheduler = scheduler;
m_CachePersister = cachePersister;
m_Sender = sender;
}
public void Initialize(ICloudProjectId cloudProjectId, IEnvironments environments)
{
HandlePersistedCache();
FetchAllCommonTags(cloudProjectId, environments);
ScheduleSendingLoop();
if (m_CachePersister.CanPersist)
{
SchedulePersistenceLoop();
}
}
internal void HandlePersistedCache()
{
if (!m_CachePersister.CanPersist
|| !m_CachePersister.TryFetch(out var persistedCache))
return;
if (persistedCache.IsEmpty())
{
m_CachePersister.Delete();
return;
}
SendPersistedCache(persistedCache);
}
internal abstract void SendPersistedCache(CachedPayload<TPayload> persistedCache);
void FetchAllCommonTags(ICloudProjectId cloudProjectId, IEnvironments environments)
{
FetchTelemetryCommonTags();
FetchSpecificCommonTags(cloudProjectId, environments);
}
internal abstract void FetchSpecificCommonTags(ICloudProjectId cloudProjectId, IEnvironments environments);
internal void FetchTelemetryCommonTags()
{
var commonTags = Cache.Payload.CommonTags;
commonTags.Clear();
commonTags[TagKeys.ApplicationInstallMode] = Application.installMode.ToString();
commonTags[TagKeys.OperatingSystem] = FormatOperatingSystemInfo(SystemInfo.operatingSystem);
commonTags[TagKeys.Platform] = Application.platform.ToString();
commonTags[TagKeys.Engine] = "Unity";
commonTags[TagKeys.UnityVersion] = Application.unityVersion;
}
internal void ScheduleSendingLoop()
{
SendingLoopScheduleId = m_Scheduler.ScheduleAction(SendingLoop, Config.PayloadSendingMaxIntervalSeconds);
void SendingLoop()
{
ScheduleSendingLoop();
SendCachedPayload();
}
}
internal abstract void SendCachedPayload();
internal void SchedulePersistenceLoop()
{
PersistenceLoopScheduleId = m_Scheduler.ScheduleAction(
PersistenceLoop, Config.SafetyPersistenceIntervalSeconds);
void PersistenceLoop()
{
SchedulePersistenceLoop();
PersistCache();
}
}
internal void PersistCache()
{
if (!m_CachePersister.CanPersist
|| Cache.TimeOfOccurenceTicks <= 0
|| Cache.Payload.Count <= 0)
return;
m_CachePersister.Persist(Cache);
}
public void Register(TEvent telemetryEvent)
{
CoreLogger.LogTelemetry(
$"Cached the {typeof(TEvent).Name} event: {JsonConvert.SerializeObject(telemetryEvent)}");
Cache.Add(telemetryEvent);
if (!IsCacheFull())
return;
SendCachedPayload();
m_Scheduler.CancelAction(SendingLoopScheduleId);
ScheduleSendingLoop();
bool IsCacheFull()
{
return Cache.Payload.Count >= Config.MaxMetricCountPerPayload;
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cafed3ef1baf3db47aac0e065c7edee4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: