-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2 from NikTheNak/main
Event batching
- Loading branch information
Showing
27 changed files
with
1,062 additions
and
649 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
## 0.2.0 | ||
|
||
- Events are now sent in batches to reduce network overhead | ||
- Automatic flush of events when app loses focus | ||
- While offline, events will be enqueue and sent when the app is back online | ||
- Added an option to set the appVersion during initialization | ||
- Replaced MiniJSON for TinyJSON for better serialization | ||
- Fixed issue with OS version | ||
|
||
## 0.0.1 | ||
|
||
- Initial release |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
using UnityEditor; | ||
|
||
namespace AptabaseSDK | ||
{ | ||
[CustomEditor(typeof(Settings))] | ||
public class SettingsEditor : Editor | ||
{ | ||
public override void OnInspectorGUI() | ||
{ | ||
var settings = (Settings)target; | ||
|
||
EditorGUILayout.PropertyField(serializedObject.FindProperty("AppKey")); | ||
|
||
if (settings.AppKey.Contains("SH")) | ||
EditorGUILayout.PropertyField(serializedObject.FindProperty("SelfHostURL")); | ||
|
||
EditorGUILayout.PropertyField(serializedObject.FindProperty("AppBuildNumber")); | ||
|
||
EditorGUILayout.PropertyField(serializedObject.FindProperty("EnableOverride")); | ||
if (settings.EnableOverride) | ||
{ | ||
EditorGUILayout.PropertyField(serializedObject.FindProperty("AppVersion")); | ||
EditorGUILayout.PropertyField(serializedObject.FindProperty("FlushInterval")); | ||
} | ||
|
||
serializedObject.ApplyModifiedProperties(); | ||
} | ||
} | ||
} |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,22 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Globalization; | ||
using System.Text; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using AptabaseSDK.TinyJson; | ||
using UnityEngine; | ||
using UnityEngine.Networking; | ||
|
||
namespace AptabaseSDK | ||
{ | ||
public static class Aptabase | ||
{ | ||
private static string _sessionId = NewSessionId(); | ||
private static Dispatcher _dispatcher; | ||
private static EnvironmentInfo _env; | ||
private static Settings _settings; | ||
|
||
private static DateTime _lastTouched = DateTime.UtcNow; | ||
private static string _baseURL; | ||
|
||
private static readonly TimeSpan _sessionTimeout = TimeSpan.FromMinutes(60); | ||
private static readonly Dictionary<string, string> _hosts = new() | ||
{ | ||
|
@@ -24,16 +26,14 @@ public static class Aptabase | |
{ "SH", "" }, | ||
}; | ||
|
||
private const string EVENT_ENDPOINT = "/api/v0/event"; | ||
private const string SDK_VERSION = "[email protected]"; | ||
|
||
private static AptabaseSettings _settings; | ||
private static int _flushTimer; | ||
private static CancellationTokenSource _pollingCancellationTokenSource; | ||
|
||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] | ||
private static void Initialize() | ||
{ | ||
//load settings | ||
_settings = Resources.Load<AptabaseSettings>("AptabaseSettings"); | ||
_settings = Resources.Load<Settings>("AptabaseSettings"); | ||
if (_settings == null) | ||
{ | ||
Debug.LogWarning("Aptabase Settings not found. Tracking will be disabled"); | ||
|
@@ -48,8 +48,70 @@ private static void Initialize() | |
Debug.LogWarning($"The Aptabase App Key {key} is invalid. Tracking will be disabled"); | ||
return; | ||
} | ||
|
||
_env = Environment.GetEnvironmentInfo(Version.GetVersionInfo(_settings)); | ||
|
||
_baseURL = GetBaseUrl(parts[1]); | ||
_dispatcher = new Dispatcher(_settings.AppKey, _baseURL, _env); | ||
|
||
//create listener | ||
var eventFocusHandler = new GameObject("AptabaseService"); | ||
eventFocusHandler.AddComponent<AptabaseService>(); | ||
} | ||
|
||
private static async void StartPolling(int flushTimer) | ||
{ | ||
StopPolling(); | ||
|
||
_flushTimer = flushTimer; | ||
_pollingCancellationTokenSource = new CancellationTokenSource(); | ||
|
||
while (_pollingCancellationTokenSource is { IsCancellationRequested: false }) | ||
{ | ||
try | ||
{ | ||
await Task.Delay(_flushTimer, _pollingCancellationTokenSource.Token); | ||
Flush(); | ||
} | ||
catch (TaskCanceledException) | ||
{ | ||
break; | ||
} | ||
} | ||
} | ||
|
||
private static void StopPolling() | ||
{ | ||
if (_flushTimer <= 0) | ||
return; | ||
|
||
_pollingCancellationTokenSource?.Cancel(); | ||
_pollingCancellationTokenSource = null; | ||
_flushTimer = 0; | ||
} | ||
|
||
public static void OnApplicationFocus(bool hasFocus) | ||
{ | ||
if (hasFocus) | ||
{ | ||
StartPolling(GetFlushInterval()); | ||
} | ||
else | ||
{ | ||
Flush(); | ||
StopPolling(); | ||
} | ||
} | ||
|
||
private static string EvalSessionId() | ||
{ | ||
var now = DateTime.UtcNow; | ||
var timeSince = now.Subtract(_lastTouched); | ||
if (timeSince >= _sessionTimeout) | ||
_sessionId = NewSessionId(); | ||
|
||
_lastTouched = now; | ||
return _sessionId; | ||
} | ||
|
||
private static string GetBaseUrl(string region) | ||
|
@@ -68,71 +130,35 @@ private static string GetBaseUrl(string region) | |
return _hosts[region]; | ||
} | ||
|
||
public static void TrackEvent(string eventName, Dictionary<string, object> props = null) | ||
public static void Flush() | ||
{ | ||
SendEvent(eventName, props); | ||
_dispatcher.Flush(); | ||
} | ||
|
||
private static async void SendEvent(string eventName, Dictionary<string, object> props) | ||
public static void TrackEvent(string eventName, Dictionary<string, object> props = null) | ||
{ | ||
if (string.IsNullOrEmpty(_baseURL)) | ||
return; | ||
|
||
try | ||
props ??= new Dictionary<string, object>(); | ||
var eventData = new Event() | ||
{ | ||
var now = DateTime.UtcNow; | ||
var timeSince = now.Subtract(_lastTouched); | ||
if (timeSince >= _sessionTimeout) | ||
_sessionId = NewSessionId(); | ||
|
||
_lastTouched = now; | ||
|
||
props ??= new Dictionary<string, object>(); | ||
|
||
//create the main dictionary for EventData | ||
var eventData = Json.Serialize(new Dictionary<string, object>(5) | ||
{ | ||
{ "timestamp", DateTime.UtcNow.ToString("o") }, | ||
{ "sessionId", _sessionId }, | ||
{ "eventName", eventName }, | ||
{ "systemProps", new Dictionary<string, object>(7) | ||
{ | ||
{ "isDebug", Application.isEditor || Debug.isDebugBuild }, | ||
{ "osName", Application.platform.ToString() }, | ||
{ "osVersion", SystemInfo.operatingSystem }, | ||
{ "locale", CultureInfo.CurrentCulture.Name }, | ||
{ "appVersion", Application.version }, | ||
{ "appBuildNumber", _settings.BuildNumber }, | ||
{ "sdkVersion", SDK_VERSION } | ||
}}, | ||
{ "props", props } | ||
}); | ||
|
||
//send request to end point | ||
using var www = new UnityWebRequest($"{_baseURL}{EVENT_ENDPOINT}", | ||
UnityWebRequest.kHttpVerbPOST); | ||
www.SetRequestHeader("Content-Type", "application/json"); | ||
www.SetRequestHeader("App-Key", _settings.AppKey); | ||
var requestBytes = Encoding.UTF8.GetBytes(eventData); | ||
www.uploadHandler = new UploadHandlerRaw(requestBytes); | ||
www.downloadHandler = new DownloadHandlerBuffer(); | ||
|
||
var operation = www.SendWebRequest(); | ||
timestamp = DateTime.UtcNow.ToString("o"), | ||
sessionId = EvalSessionId(), | ||
eventName = eventName, | ||
systemProps = _env, | ||
props = props | ||
}; | ||
|
||
_dispatcher.Enqueue(eventData); | ||
} | ||
|
||
//wait for complete | ||
while (!operation.isDone) | ||
await Task.Yield(); | ||
private static int GetFlushInterval() | ||
{ | ||
if (_settings.EnableOverride && _settings.FlushInterval > 0) | ||
return Mathf.Max(0, _settings.FlushInterval); | ||
|
||
//handle results | ||
if (www.result != UnityWebRequest.Result.Success) | ||
{ | ||
Debug.LogError($"Failed to perform TrackEvent due to {www.responseCode} and response body {www.error}"); | ||
} | ||
} | ||
catch (Exception e) | ||
{ | ||
Debug.LogError($"Failed to perform TrackEvent {e}"); | ||
} | ||
return _env.isDebug ? 2000 : 60000; | ||
} | ||
|
||
private static string NewSessionId() => Guid.NewGuid().ToString().ToLower(); | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
using UnityEngine; | ||
|
||
namespace AptabaseSDK | ||
{ | ||
public class AptabaseService : MonoBehaviour | ||
{ | ||
private void Awake() | ||
{ | ||
DontDestroyOnLoad(gameObject); | ||
} | ||
|
||
private void OnApplicationFocus(bool hasFocus) | ||
{ | ||
Aptabase.OnApplicationFocus(hasFocus); | ||
} | ||
} | ||
} |
2 changes: 1 addition & 1 deletion
2
Runtime/ThirdParty/MiniJSON.cs.meta → Runtime/AptabaseService.cs.meta
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.