You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

405 lines
14 KiB

1 month ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using LitJson;
using Sog;
namespace ThinkingData.Analytics
{
public class ThinkingdataAnalytics
{
private const string LibVersion = "1.3.2";
private const string LibName = "tga_csharp_sdk";
private static readonly Regex KeyPattern =
new Regex("^(#[a-z][a-z0-9_]{0,49})|([a-z][a-z0-9_]{0,50})$", RegexOptions.IgnoreCase);
private readonly IConsumer _consumer;
private readonly bool _enableUuid;
private readonly Dictionary<string, object> _pubicProperties;
/*
* tga类Consumer的一个实例化对象
* @param consumer BatchConsumer,LoggerConsumer实例
*/
public ThinkingdataAnalytics(IConsumer consumer) : this(consumer, false)
{
}
public ThinkingdataAnalytics(IConsumer consumer, bool enableUuid)
{
_consumer = consumer;
_enableUuid = enableUuid;
_pubicProperties = new Dictionary<string, object>();
ClearPublicProperties();
}
/*
* track接口track事件中添加公共属性
* @param properties
*/
public void SetPublicProperties(Dictionary<string, object> properties)
{
lock (_pubicProperties)
{
foreach (var kvp in properties)
{
_pubicProperties[kvp.Key] = kvp.Value;
}
}
}
/*
* track属性将不会再加入公共属性
*/
public void ClearPublicProperties()
{
lock (_pubicProperties)
{
_pubicProperties.Clear();
_pubicProperties.Add("#lib", LibName);
_pubicProperties.Add("#lib_version", LibVersion);
_pubicProperties.Add("#zone_offset", TimeZoneInfo.Local.BaseUtcOffset.Hours);
}
}
// 记录一个没有任何属性的事件
public void Track(string account_id, string distinct_id, string event_name)
{
_Add(account_id, distinct_id, "track", event_name, null, null);
}
/*
* ()
* @param account_id ID
* @param distinct_id ID
* @param event_name
* @param properties
*/
public void Track(string account_id, string distinct_id, string event_name,
Dictionary<string, object> properties)
{
if (string.IsNullOrEmpty(event_name))
{
throw new SystemException("The event name must be provided.");
}
_Add(account_id, distinct_id, "track", event_name, null, properties);
}
/*
*
* @param account_id ID
* @param distinct_id ID
* @param event_name
* @param event_id ID
* @param properties
*/
public void TrackUpdate(string account_id, string distinct_id, string event_name, string event_id,
Dictionary<string, object> properties)
{
if (string.IsNullOrEmpty(event_name))
{
throw new SystemException("The event name must be provided.");
}
if (string.IsNullOrEmpty(event_id))
{
throw new SystemException("The event id must be provided.");
}
_Add(account_id, distinct_id, "track_update", event_name, event_id, properties);
}
/*
*
* @param account_id ID
* @param distinct_id ID
* @param event_name
* @param event_id ID
* @param properties
*/
public void TrackOverwrite(string account_id, string distinct_id, string event_name, string event_id,
Dictionary<string, object> properties)
{
if (string.IsNullOrEmpty(event_name))
{
throw new SystemException("The event name must be provided.");
}
if (string.IsNullOrEmpty(event_id))
{
throw new SystemException("The event id must be provided.");
}
_Add(account_id, distinct_id, "track_overwrite", event_name, event_id, properties);
}
/*
*
* @param account_id ID
* @param distinct_id ID
* @param properties
*/
public void UserSet(string account_id, string distinct_id, Dictionary<string, object> properties)
{
_Add(account_id, distinct_id, "user_set", properties);
}
/*
*
* @param account_id ID
* @param distinct_id 访 ID
* @param properties
*/
public void UserUnSet(string account_id, string distinct_id, List<string> properties)
{
var props = properties.ToDictionary<string, string, object>(property => property, property => 0);
_Add(account_id, distinct_id, "user_unset", props);
}
/**
* ,,.
* @param account_id ID
* @param distinct_id ID
* @param properties
*/
public void UserSetOnce(string account_id, string distinct_id, Dictionary<string, object> properties)
{
_Add(account_id, distinct_id, "user_setOnce", properties);
}
/**
* key对应的内容
* @param account_id ID
* @param distinct_id ID
* @param properties
*/
public void UserSetOnce(string account_id, string distinct_id, string property, object value)
{
var properties = new Dictionary<string, object> {{property, value}};
_Add(account_id, distinct_id, "user_setOnce", properties);
}
/*
*
* @param account_id ID
* @param distinct_id ID
* @param properties
*/
public void UserAdd(string account_id, string distinct_id, Dictionary<string, object> properties)
{
_Add(account_id, distinct_id, "user_add", properties);
}
public void UserAdd(string account_id, string distinct_id, string property, long value)
{
var properties = new Dictionary<string, object> {{property, value}};
_Add(account_id, distinct_id, "user_add", properties);
}
/*
*
* @param account_id ID
* @param distinct_id ID
* @param properties
*/
public void UserAppend(string account_id, string distinct_id, Dictionary<string, object> properties)
{
_Add(account_id, distinct_id, "user_append", properties);
}
/**
* ,
* @param account_id ID
* @param distinct_id ID
*/
public void UserDelete(string account_id, string distinct_id)
{
_Add(account_id, distinct_id, "user_del", new Dictionary<string, object>());
}
/// 立即发送缓存中的所有日志
public void Flush()
{
_consumer.Flush();
}
//关闭并退出 sdk 所有线程,停止前会清空所有本地数据
public void Close()
{
_consumer.Close();
}
private static bool IsNumber(object value)
{
return (value is sbyte) || (value is short) || (value is int) || (value is long) || (value is byte)
|| (value is ushort) || (value is uint) || (value is ulong) || (value is decimal) ||
(value is float) || (value is double);
}
private static void AssertProperties(string type, IDictionary<string, object> properties)
{
if (null == properties)
{
return;
}
foreach (var kvp in properties)
{
var key = kvp.Key;
var value = kvp.Value;
if (null == value)
{
continue;
}
if (KeyPattern.IsMatch(key))
{
if (!IsNumber(value) && !(value is string) && !(value is DateTime) && !(value is bool) &&
!(value is IList) && !(value is JsonData))
{
throw new ArgumentException(
"The supported data type including: Number, String, Date, Boolean,List. Invalid property: {key}");
}
// IList<object> list = value as List<object>;
// if (list != null)
// for (var i = 0; i < list.Count; i++)
// {
// if (list[i] is DateTime)
// {
// list[i] = (DateTime.Now).ToString("yyyy-MM-dd HH:mm:ss.fff");
// }
// }
if (type == "user_add" && !IsNumber(value))
{
throw new ArgumentException("Only Number is allowed for user_add. Invalid property:" + key);
}
}
else
{
throw new ArgumentException("The " + type + "'" + key + "' is invalid.");
}
}
}
private void _Add(string account_id, string distinct_id, string type, IDictionary<string, object> properties)
{
_Add(account_id, distinct_id, type, null, null, properties);
}
private void _Add(string account_id, string distinct_id, string type, string event_name, string event_id,
IDictionary<string, object> properties)
{
if (string.IsNullOrEmpty(account_id) && string.IsNullOrEmpty(distinct_id))
{
throw new SystemException("account_id or distinct_id must be provided. ");
}
var eventProperties = new Dictionary<string, object>(properties);
if (type == "track" || type == "track_update" || type == "track_overwrite")
{
if (_pubicProperties != null)
lock (_pubicProperties)
{
foreach (var kvp in _pubicProperties)
{
if(eventProperties.ContainsKey(kvp.Key) == false)
eventProperties.Add(kvp.Key, kvp.Value);
else
TraceLog.Error("try add same key ,key:" + kvp.Key + " val:" + kvp.Value);
}
}
}
var evt = new Dictionary<string, object>();
if (account_id != null)
{
evt.Add("#account_id", account_id);
}
if (distinct_id != null)
{
evt.Add("#distinct_id", distinct_id);
}
if (event_name != null)
{
evt.Add("#event_name", event_name);
}
if (event_id != null)
{
evt.Add("#event_id", event_id);
}
//#uuid 只支持UUID标准格式xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
if (eventProperties.ContainsKey("#uuid"))
{
evt.Add("#uuid", eventProperties["#uuid"]);
eventProperties.Remove("#uuid");
}
else if (_enableUuid)
{
evt.Add("#uuid", Guid.NewGuid().ToString("D"));
}
AssertProperties(type, eventProperties);
if (eventProperties.ContainsKey("#ip"))
{
evt.Add("#ip", eventProperties["#ip"]);
eventProperties.Remove("#ip");
}
//#first_check_id
if (eventProperties.ContainsKey("#first_check_id"))
{
evt.Add("#first_check_id", eventProperties["#first_check_id"]);
eventProperties.Remove("#first_check_id");
}
// if (properties != null)
// {
// foreach (var kvp in properties)
// {
// if (kvp.Value is DateTime time)
// {
// eventProperties[kvp.Key] = time.ToString("yyyy-MM-dd HH:mm:ss.fff");
// }
// }
// }
if (eventProperties.ContainsKey("#time"))
{
evt.Add("#time", eventProperties["#time"]);
eventProperties.Remove("#time");
}
else
{
evt.Add("#time", DateTime.Now);
}
//加上时区偏移
// if (eventProperties.ContainsKey("#zone_offset"))
// {
// evt.Add("#zone_offset", eventProperties["#zone_offset"]);
// eventProperties.Remove("#zone_offset");
// }
// else
// {
// evt.Add("#zone_offset", TimeZoneInfo.Local.BaseUtcOffset.Hours);
// }
evt.Add("#type", type);
evt.Add("properties", eventProperties);
_consumer.Send(evt);
}
}
}