797 lines
33 KiB
C#
797 lines
33 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Reflection;
|
|
using System.Threading;
|
|
using Bloomberg.Client.Components;
|
|
using Bloomberg.Client.Components.Connection;
|
|
using Bloomberg.Client.Components.Settings;
|
|
using Bloomberg.Client.Crash;
|
|
using Bloomberg.Client.Report;
|
|
using Bloomberg.Message.Components;
|
|
using Bloomberg.Message.Entities;
|
|
using System.Linq;
|
|
using System.Diagnostics;
|
|
using JD.Core;
|
|
|
|
namespace Bloomberg.Client
|
|
{
|
|
public class Reporter : IDisposable
|
|
{
|
|
public const string BloombergVersion = "2.2.0.4641";
|
|
public const int MaximumChangelistsFromIniFile = 10;
|
|
public const int MaximumConnectionsFromIniFile = 10;
|
|
|
|
#region Private members
|
|
|
|
private readonly CrashHandler _crashHandler = new CrashHandler();
|
|
private InternalLogFile _logFile;
|
|
|
|
private RemoteDataWriter _remoteDataWriter = new RemoteDataWriter();
|
|
|
|
private Mutex _crashAlreadyHandledFlag;
|
|
private Mutex _managedFlagMutex;
|
|
|
|
private const string CRASH_ALREADY_HANDLED_FLAG_BASE = "CRASH_ALREADY_HANDLED_FLAG";
|
|
private const string NATIVE_BLOOMBERG_MUTEX_NAME_BASE = "BLOOMBERG_NATIVE_CLIENT_FLAG";
|
|
private const string MANAGED_BLOOMBERG_MUTEX_NAME_BASE = "BLOOMBERG_MANAGED_CLIENT_FLAG";
|
|
|
|
private string _crashAlreadyHandledFlagName;
|
|
private string _nativeBloombergMutexName;
|
|
private string _managedBloombergMutexName;
|
|
|
|
#endregion
|
|
|
|
#region Properties
|
|
|
|
/// <summary>
|
|
/// Singleton
|
|
/// </summary>
|
|
public static Reporter Instance { get; private set; }
|
|
public bool HasBeenInit { get; private set; }
|
|
public BloombergInterface BloombergInterface { get; private set; }
|
|
public ReportManager ReportManager { get; private set; }
|
|
public RemoteDataCollector RemoteDataCollector { get; private set; }
|
|
public int RemoteDataPort { get { return _remoteDataWriter.Port; } }
|
|
|
|
/// <summary>
|
|
/// Specify if the crash reporting is still enabled.
|
|
/// This can be changed when connection to server or reporter failed.
|
|
/// </summary>
|
|
public bool CrashReportingEnabled
|
|
{
|
|
get
|
|
{
|
|
return _crashHandler.IsEnabled;
|
|
}
|
|
set
|
|
{
|
|
_crashHandler.IsEnabled = value;
|
|
}
|
|
}
|
|
|
|
private BloombergSettings _bloombergSettings;
|
|
public BloombergSettings BloombergSettings { get { return _bloombergSettings; } private set { _bloombergSettings = value; } }
|
|
|
|
private GameSettings _gameSettings;
|
|
public GameSettings GameSettings { get { return _gameSettings; } private set { _gameSettings = value; } }
|
|
|
|
public ConnectionManager ConnectionManager { get; private set; }
|
|
|
|
private SubTypeManager _subTypeManager;
|
|
public SubTypeManager SubTypeManager { get { return _subTypeManager; } private set { _subTypeManager = value; } }
|
|
|
|
#endregion
|
|
|
|
#region Initialization
|
|
|
|
/// <summary>
|
|
/// Private constructor called by Initialize
|
|
/// </summary>
|
|
/// <param name="bloombergInterface"></param>
|
|
private Reporter(BloombergInterface bloombergInterface)
|
|
{
|
|
BloombergInterface = bloombergInterface;
|
|
ReportManager = new ReportManager();
|
|
RemoteDataCollector = new RemoteDataCollector();
|
|
ConnectionManager = new ConnectionManager();
|
|
SubTypeManager = new SubTypeManager();
|
|
HasBeenInit = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Client has already instantiated his interface
|
|
/// </summary>
|
|
/// <param name="bloombergInterface">The interface implemented by the client</param>
|
|
/// <param name="crashHandlerEnabled">True to enable crash handling</param>
|
|
public static void Initialize(BloombergInterface bloombergInterface, bool crashHandlerEnabled = true)
|
|
{
|
|
if (bloombergInterface == null)
|
|
throw new NullReferenceException("The bloombergInterface parameter cannot be null");
|
|
|
|
if (Instance == null)
|
|
{
|
|
Instance = new Reporter(bloombergInterface);
|
|
Instance.CrashReportingEnabled = crashHandlerEnabled;
|
|
Instance.InternalInit();
|
|
|
|
Tracer.Log(TracerLogLevel.INFO, "Reporter initialized");
|
|
}
|
|
else
|
|
{
|
|
Tracer.Log(TracerLogLevel.INFO, "Reporter already initialized.");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Un uninitialize Bloomberg
|
|
/// </summary>
|
|
public static void UnInitialize()
|
|
{
|
|
if (Instance != null)
|
|
{
|
|
Instance.InternalUnInit();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// This does the initialization of bloomberg.
|
|
/// - Get the GameSettings & BloombergSettings
|
|
/// - Initialize log file
|
|
/// - VerifyConnection
|
|
/// </summary>
|
|
private void InternalInit()
|
|
{
|
|
_logFile = new InternalLogFile();
|
|
_logFile.Initialize();
|
|
|
|
InitGameSettings();
|
|
InitBloombergSettings();
|
|
|
|
InitializeBloombergMutexNames();
|
|
|
|
if (!CreateBloombergManagedMutex())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(BloombergSettings.ConfigFilePath) && !BloombergSettings.ConfigFilePath.EndsWith(Path.DirectorySeparatorChar.ToString()))
|
|
BloombergSettings.ConfigFilePath += Path.DirectorySeparatorChar;
|
|
|
|
string commonFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonDocuments), "Ubisoft\\Bloomberg");
|
|
string userFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "Ubisoft\\Bloomberg");
|
|
|
|
var currentAssemblyLocation = Path.GetDirectoryName(GetType().Assembly.Location());
|
|
|
|
var settingsPaths = new[] { BloombergSettings.ConfigFilePath, "c:\\", "", commonFolder, userFolder, currentAssemblyLocation };
|
|
|
|
SettingLoader settingLoader = null;
|
|
foreach (var path in settingsPaths.Skip(string.IsNullOrEmpty(BloombergSettings.ConfigFilePath) ? 1 : 0))
|
|
{
|
|
try
|
|
{
|
|
var fullFilePath = Path.Combine(path, BloombergSettings.BloombergSettingsFileName);
|
|
Tracer.LogFormat(TracerLogLevel.INFO, "Trying to open Bloomberg settings file: {0}.", Path.GetFullPath(fullFilePath));
|
|
|
|
if (!File.Exists(fullFilePath))
|
|
continue;
|
|
|
|
settingLoader = new SettingLoader(fullFilePath);
|
|
|
|
Tracer.LogFormat(TracerLogLevel.INFO, "Reading Bloomberg settings from {0}.", Path.GetFullPath(fullFilePath));
|
|
|
|
break;
|
|
}
|
|
catch (FileNotFoundException)
|
|
{
|
|
// try next path
|
|
}
|
|
}
|
|
|
|
if (settingLoader != null)
|
|
{
|
|
InitBloombergSettingsFromIniFile(settingLoader);
|
|
InitGameSettingsFromIniFile(settingLoader);
|
|
InitConnectionsFromIniFile(settingLoader);
|
|
}
|
|
else
|
|
{
|
|
Tracer.LogFormat(TracerLogLevel.INFO, "{0} not found. Default settings will be used.", BloombergSettings.BloombergSettingsFileName);
|
|
}
|
|
|
|
BloombergInterface.GetBloombergSettings(ref _bloombergSettings);
|
|
BloombergInterface.GetGameSettings(ref _gameSettings);
|
|
|
|
_crashHandler.Initialize();
|
|
|
|
_remoteDataWriter.StartListening();
|
|
|
|
HasBeenInit = true;
|
|
Tracer.LogFormat(TracerLogLevel.INFO, "Reporter.InternalInit: Bloomberg is initialized.");
|
|
}
|
|
|
|
private void InitializeBloombergMutexNames()
|
|
{
|
|
int processId = Process.GetCurrentProcess().Id;
|
|
|
|
_crashAlreadyHandledFlagName = CRASH_ALREADY_HANDLED_FLAG_BASE + "_" + processId;
|
|
_nativeBloombergMutexName = NATIVE_BLOOMBERG_MUTEX_NAME_BASE + "_" + processId;
|
|
_managedBloombergMutexName = MANAGED_BLOOMBERG_MUTEX_NAME_BASE + "_" + processId;
|
|
}
|
|
|
|
private bool CreateBloombergManagedMutex()
|
|
{
|
|
try
|
|
{
|
|
// We open a mutex to let know to the native Bloomberg client that the managed client has been initialized
|
|
_managedFlagMutex = new Mutex(false, _managedBloombergMutexName);
|
|
return true;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Tracer.LogFormat(TracerLogLevel.ERROR, "Bloomberg managed Mutex creation failed. Bloomberg client won't be initialized. Is there another bloomberg CSharp client enabled ? Exception : {0}", e.Message);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private void InitBloombergSettings()
|
|
{
|
|
BloombergSettings = new BloombergSettings();
|
|
}
|
|
|
|
private void InitGameSettings()
|
|
{
|
|
GameSettings = new GameSettings();
|
|
}
|
|
|
|
class IniConnectionSpecs
|
|
{
|
|
// ReSharper disable UnusedAutoPropertyAccessor.Local
|
|
// assigned through reflection
|
|
public ConnectionType? ConnectionType { get; set; }
|
|
public ReportType? ConnectionReportType { get; set; }
|
|
public string ConnectionPath { get; set; }
|
|
public string ConnectionIp { get; set; }
|
|
public int? ConnectionPort { get; set; }
|
|
public int? ConnectionRpcPort { get; set; }
|
|
public string ConnectionProxyRanges { get; set; }
|
|
// ReSharper restore UnusedAutoPropertyAccessor.Local
|
|
}
|
|
|
|
private void InitConnectionsFromIniFile(SettingLoader settingLoader)
|
|
{
|
|
string userName = null;
|
|
string reporterMachineName = null;
|
|
ReadReporterSettings(ref userName, ref reporterMachineName);
|
|
|
|
if (userName != null)
|
|
BloombergSettings.Username = userName;
|
|
|
|
// iterate through all possible connection indices
|
|
for (uint connIndex = 0; connIndex < MaximumConnectionsFromIniFile; ++connIndex)
|
|
{
|
|
// load and validate generic connection settings
|
|
|
|
var connectionSpecs = new IniConnectionSpecs();
|
|
settingLoader.ProcessProperties(connectionSpecs, "Connections", null, connIndex);
|
|
|
|
// Skip unspecified connections. This allows the user to comment a connection without having to renumber all others.
|
|
if (!connectionSpecs.ConnectionType.HasValue)
|
|
continue;
|
|
|
|
if (!connectionSpecs.ConnectionReportType.HasValue)
|
|
{
|
|
Tracer.LogFormat(TracerLogLevel.WARN, "ConnectionReportType{0} in section [Connections] in {1} must be specified for the connection to be valid.", connIndex, BloombergSettings.BloombergSettingsFileName);
|
|
continue;
|
|
}
|
|
|
|
if (connectionSpecs.ConnectionType.Value == ConnectionType.File)
|
|
{
|
|
// if it's a file connection, validate it and create it
|
|
if (connectionSpecs.ConnectionPath == null)
|
|
{
|
|
Tracer.LogFormat(TracerLogLevel.WARN, "ConnectionPath{0} in section [Connections] in {1} must be specified for the connection to be valid.", connIndex, BloombergSettings.BloombergSettingsFileName);
|
|
continue;
|
|
}
|
|
|
|
ConnectionManager.AddFileConnection(connectionSpecs.ConnectionPath, connectionSpecs.ConnectionReportType.Value);
|
|
}
|
|
else
|
|
{
|
|
// if it is a socket connection, validate it and create it
|
|
if (!connectionSpecs.ConnectionPort.HasValue || connectionSpecs.ConnectionPort.Value < 1024 || connectionSpecs.ConnectionPort.Value > UInt16.MaxValue)
|
|
{
|
|
Tracer.LogFormat(TracerLogLevel.WARN, "ConnectionPort{0} in section [Connections] in {1} must be a 16-bit number >= 1024 for the connection to be valid.", connIndex, BloombergSettings.BloombergSettingsFileName);
|
|
continue;
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(connectionSpecs.ConnectionIp))
|
|
{
|
|
if (connectionSpecs.ConnectionType != ConnectionType.Reporter)
|
|
{
|
|
Tracer.LogFormat(TracerLogLevel.WARN, "ConnectionIp{0} in section [Connections] in {1} must be specified for the connection to be valid.", connIndex, BloombergSettings.BloombergSettingsFileName);
|
|
continue;
|
|
}
|
|
|
|
connectionSpecs.ConnectionIp = reporterMachineName ?? _bloombergSettings.MachineName;
|
|
}
|
|
|
|
string[] proxies = null;
|
|
|
|
if (connectionSpecs.ConnectionProxyRanges != null)
|
|
{
|
|
proxies = connectionSpecs.ConnectionProxyRanges.Split(',').Where(s => s.Length > 0).ToArray();
|
|
}
|
|
|
|
ConnectionManager.AddSocketConnection(
|
|
new ServerInformation(
|
|
connectionSpecs.ConnectionIp, connectionSpecs.ConnectionPort.Value, connectionSpecs.ConnectionRpcPort.HasValue ? connectionSpecs.ConnectionRpcPort.Value : 0
|
|
),
|
|
connectionSpecs.ConnectionReportType.Value, connectionSpecs.ConnectionType.Value, proxies
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static bool ReadReporterSettings(ref string userName, ref string reporterMachineName)
|
|
{
|
|
var settingsPaths = new[] { BloombergSettings.ConfigFilePath, "c:\\", "" };
|
|
|
|
string[] bloombergConfig = null;
|
|
try
|
|
{
|
|
foreach (var path in settingsPaths.Skip(string.IsNullOrEmpty(BloombergSettings.ConfigFilePath) ? 1 : 0))
|
|
{
|
|
var bloombergConfigPath = Path.Combine(path, BloombergSettings.BloombergConfigFileName);
|
|
Tracer.LogFormat(TracerLogLevel.INFO, "Trying to open Reporter connection settings file: {0}.", Path.GetFullPath(bloombergConfigPath));
|
|
|
|
if (!File.Exists(bloombergConfigPath))
|
|
continue;
|
|
|
|
try
|
|
{
|
|
bloombergConfig = File.ReadAllLines(bloombergConfigPath);
|
|
Tracer.LogFormat(TracerLogLevel.INFO, "Reading reporter connection settings from {0}.", BloombergSettings.BloombergConfigFileName);
|
|
break;
|
|
}
|
|
catch (FileNotFoundException)
|
|
{
|
|
// try next path
|
|
}
|
|
}
|
|
}
|
|
catch (ArgumentNullException)
|
|
{
|
|
Tracer.LogFormat(TracerLogLevel.WARN, "{0} is empty. Local user name and computer name will be used.", BloombergSettings.BloombergConfigFileName);
|
|
return false;
|
|
}
|
|
|
|
if (bloombergConfig == null || bloombergConfig.Length == 0 || String.IsNullOrEmpty(bloombergConfig[0]))
|
|
{
|
|
Tracer.LogFormat(TracerLogLevel.WARN, "{0} was not found or is empty. Local user name and computer name will be used.", BloombergSettings.BloombergConfigFileName);
|
|
return false;
|
|
}
|
|
|
|
var pair = bloombergConfig[0].Split('=');
|
|
if (pair.Length != 2)
|
|
{
|
|
Tracer.LogFormat(TracerLogLevel.WARN, "{0} does not meet the username=computername format. Local user name and computer name will be used.",
|
|
BloombergSettings.BloombergConfigFileName);
|
|
return false;
|
|
}
|
|
|
|
userName = pair[0].Trim();
|
|
reporterMachineName = pair[1].Trim();
|
|
|
|
Tracer.LogFormat(TracerLogLevel.WARN, "Using username='{0}' and reporter computer name = {1} as read from {2}.", userName, reporterMachineName, BloombergSettings.BloombergConfigFileName);
|
|
|
|
return true;
|
|
}
|
|
|
|
private void InitGameSettingsFromIniFile(SettingLoader settingLoader)
|
|
{
|
|
settingLoader.ProcessProperties(GameSettings, "Game");
|
|
}
|
|
|
|
private void InitBloombergSettingsFromIniFile(SettingLoader settingLoader)
|
|
{
|
|
settingLoader.ProcessProperties(BloombergSettings, "Bloomberg");
|
|
}
|
|
|
|
public bool CheckIfCrashAlreadyHandled()
|
|
{
|
|
bool isCrashAlreadyHandled = false;
|
|
Mutex mutex = null;
|
|
try
|
|
{
|
|
mutex = Mutex.OpenExisting(_crashAlreadyHandledFlagName);
|
|
isCrashAlreadyHandled = true;
|
|
Tracer.LogFormat(TracerLogLevel.INFO,
|
|
"Mutex \"{0}\" already opened by native client. We don't analyze exception",
|
|
_crashAlreadyHandledFlagName);
|
|
}
|
|
catch (WaitHandleCannotBeOpenedException)
|
|
{
|
|
Tracer.LogFormat(TracerLogLevel.DEBUG, "CrashHandler.CheckIfCrashAlreadyHandled: Crash is not already handled by native client.");
|
|
}
|
|
catch (Exception other)
|
|
{
|
|
Tracer.LogFormat(TracerLogLevel.ERROR,
|
|
"CrashHandler.CheckIfCrashAlreadyHandled: Mutex.OpenExisting \"{0}\" failed for unexpected reason. Exception : {1}",
|
|
_crashAlreadyHandledFlagName, other.Message);
|
|
}
|
|
|
|
if (mutex != null)
|
|
{
|
|
mutex.Close();
|
|
}
|
|
|
|
return isCrashAlreadyHandled;
|
|
}
|
|
|
|
public void CreateCrashAlreadyHandledFlag()
|
|
{
|
|
Mutex mutex = null;
|
|
try
|
|
{
|
|
mutex = new Mutex(false, _crashAlreadyHandledFlagName);
|
|
Tracer.LogFormat(TracerLogLevel.DEBUG, "CrashHandler.CreateCrashAlreadyHandledFlag: Crash already handled Mutex created by managed client.");
|
|
}
|
|
catch (Exception other)
|
|
{
|
|
Tracer.LogFormat(TracerLogLevel.ERROR,
|
|
"CrashHandler.CreateCrashAlreadyHandledFlag: Mutex.OpenExisting \"{0}\" failed for unexpected reason. Exception : {1}",
|
|
_crashAlreadyHandledFlagName, other.Message);
|
|
}
|
|
_crashAlreadyHandledFlag = mutex;
|
|
}
|
|
|
|
public bool CheckIfNativeClientEnabled()
|
|
{
|
|
bool isNativeClientEnabled = false;
|
|
Mutex mutex = null;
|
|
try
|
|
{
|
|
mutex = Mutex.OpenExisting(_nativeBloombergMutexName);
|
|
isNativeClientEnabled = true;
|
|
Tracer.LogFormat(TracerLogLevel.DEBUG, "CrashHandler.CheckIfNativeClientEnabled: Native client flag Mutex created.");
|
|
}
|
|
catch (WaitHandleCannotBeOpenedException)
|
|
{
|
|
Tracer.LogFormat(TracerLogLevel.DEBUG, "CrashHandler.CheckIfNativeClientEnabled: Native client flag Mutex not created.");
|
|
}
|
|
catch (Exception other)
|
|
{
|
|
Tracer.LogFormat(TracerLogLevel.ERROR,
|
|
"CrashHandler.CheckIfNativeClientEnabled: Mutex.OpenExisting \"{0}\" failed for unexpected reason. Exception : {1}",
|
|
_nativeBloombergMutexName, other.Message);
|
|
}
|
|
|
|
if (mutex != null)
|
|
{
|
|
mutex.Close();
|
|
}
|
|
|
|
return isNativeClientEnabled;
|
|
}
|
|
|
|
private class SettingLoader
|
|
{
|
|
private readonly IniFile _iniFile;
|
|
private static readonly Dictionary<Type, Func<string, object>> Translators;
|
|
static SettingLoader()
|
|
{
|
|
Translators = new Dictionary<Type, Func<string, object>>();
|
|
|
|
var intTranslator = new Func<string, object>(val => Int32.Parse(val));
|
|
Translators.Add(typeof(int), intTranslator);
|
|
|
|
Translators.Add(typeof(bool), delegate(string val)
|
|
{
|
|
bool boolVal;
|
|
if (Boolean.TryParse(val, out boolVal))
|
|
return (object)boolVal;
|
|
|
|
return (object)Convert.ToBoolean(intTranslator(val));
|
|
});
|
|
|
|
Translators.Add(typeof(uint), val => uint.Parse(val));
|
|
Translators.Add(typeof(TracerLogLevel), val => Enum.Parse(typeof(TracerLogLevel), val));
|
|
Translators.Add(typeof(ChangelistType), val => Enum.Parse(typeof(ChangelistType), val));
|
|
Translators.Add(typeof(ReportType), val => Enum.Parse(typeof(ReportType), val));
|
|
Translators.Add(typeof(ConnectionType), val => Enum.Parse(typeof(ConnectionType), val));
|
|
Translators.Add(typeof(string), val => val);
|
|
}
|
|
|
|
public SettingLoader(string filename)
|
|
{
|
|
_iniFile = new IniFile(filename);
|
|
}
|
|
|
|
private object ExtractSetting(string sectionName, object settings, PropertyInfo key, bool useKeyName, string prefix = null, uint? index = null)
|
|
{
|
|
var value = _iniFile.GetValue(sectionName,
|
|
(
|
|
prefix ??
|
|
String.Empty)
|
|
+ (useKeyName ? key.Name : String.Empty) +
|
|
(index.HasValue ? index.Value.ToString() : String.Empty));
|
|
if (value == null)
|
|
{
|
|
if (key.PropertyType.IsGenericType && key.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
|
|
key.SetValue(settings, null, null);
|
|
|
|
return null;
|
|
}
|
|
|
|
try
|
|
{
|
|
var typeToUse = key.PropertyType.IsGenericType && key.PropertyType.GetGenericTypeDefinition() == typeof (Nullable<>)
|
|
? key.PropertyType.GetGenericArguments()[0]
|
|
: key.PropertyType;
|
|
|
|
var translatedVal = Translators[typeToUse](value);
|
|
key.SetValue(settings, translatedVal, null);
|
|
|
|
return translatedVal;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
if (ex is FormatException || ex is ArgumentException || ex is OverflowException)
|
|
Tracer.LogFormat(TracerLogLevel.ERROR,
|
|
"Value <{0}> is not translatable to <{1}> for key {2}.", value,
|
|
key.PropertyType.Name, key.Name);
|
|
else if (ex is KeyNotFoundException)
|
|
Tracer.LogFormat(TracerLogLevel.ERROR,
|
|
"Value type <{0}> not implemented in settings loader for key {1}.",
|
|
key.PropertyType.Name, key.Name);
|
|
else
|
|
throw;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public void ProcessProperties(object settingsObject, string sectionName, string prefix = null, uint? index = null)
|
|
{
|
|
// nice generic loop with ugly special cases so that the ini file setting names mean something and are similar to those of the native client
|
|
foreach (var key in settingsObject.GetType().GetProperties())
|
|
{
|
|
// it seems that testing directly for List<ChangeListInfo> does not work
|
|
if (key.PropertyType.IsGenericType && key.PropertyType.GetGenericTypeDefinition() == typeof(List<>) && key.PropertyType.GetGenericArguments()[0] == typeof(ChangelistInfo))
|
|
{
|
|
var clList = (List<ChangelistInfo>)key.GetValue(settingsObject, null);
|
|
|
|
for (uint currentIndex = 0; currentIndex < MaximumChangelistsFromIniFile; ++currentIndex)
|
|
{
|
|
var clInfo = new ChangelistInfo();
|
|
ProcessProperties(clInfo, sectionName, "Changelist", currentIndex);
|
|
ProcessProperties(clInfo, sectionName, "BranchRoot", currentIndex);
|
|
if (clInfo.PerforceServer != null)
|
|
clList.Add(clInfo);
|
|
}
|
|
}
|
|
else if (key.PropertyType == typeof(VersionInfo))
|
|
ProcessProperties(key.GetValue(settingsObject, null), sectionName, prefix, index);
|
|
else
|
|
{
|
|
bool useKeyName = true;
|
|
string localPrefix = prefix;
|
|
string localSectionName = sectionName;
|
|
if (settingsObject is VersionInfo && key.Name == "Name")
|
|
{
|
|
useKeyName = false;
|
|
localPrefix = "Version";
|
|
}
|
|
else if (key.Name == "Changelist")
|
|
localPrefix = null;
|
|
|
|
ExtractSetting(localSectionName, settingsObject, key, useKeyName, localPrefix, index);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Uninitialize Bloomberg
|
|
/// </summary>
|
|
private void InternalUnInit()
|
|
{
|
|
Tracer.LogFormat(TracerLogLevel.INFO, "Reporter.Dispose: Bloomberg will be uninitialized.");
|
|
|
|
if (_remoteDataWriter != null)
|
|
_remoteDataWriter.StopListening();
|
|
|
|
_managedFlagMutex = null;
|
|
|
|
if (ReportStats.Instance != null)
|
|
ReportStats.Instance.UnInitialize();
|
|
|
|
if (_crashHandler != null)
|
|
_crashHandler.UnInitialize();
|
|
|
|
if (_logFile != null)
|
|
_logFile.UnInitialize();
|
|
|
|
if (_crashAlreadyHandledFlag != null)
|
|
{
|
|
_crashAlreadyHandledFlag.Close();
|
|
}
|
|
_crashAlreadyHandledFlag = null;
|
|
|
|
if (_managedFlagMutex != null)
|
|
{
|
|
_managedFlagMutex.Dispose();
|
|
}
|
|
}
|
|
|
|
public void RegisterCrashHandler(bool registerCrashHandler)
|
|
{
|
|
_crashHandler.RegisterCrashHandler(registerCrashHandler);
|
|
}
|
|
|
|
public bool GetCrashHandlerRegistered()
|
|
{
|
|
return _crashHandler.IsRegistered();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Reports
|
|
/// <summary>
|
|
/// These are multi thread safe:
|
|
/// 1. Trigger a report by passing down your own report to log some statistics or other things.
|
|
/// 2. Trigger a crash by passing and error type, error message
|
|
/// 3. For stats and flags you can either pass a ReportStats or ReportFlag (it will use the TriggerReport method)
|
|
/// or use the internal ReportStats and ReportFlags by ignoring this parameters. In this case, and only for stats,
|
|
/// the report will be processed asynchronously if you are using the BloombergThread.
|
|
/// </summary>
|
|
/// <param name="report"></param>
|
|
/// <returns></returns>
|
|
public ActionResult TriggerReport(ReportInterface report)
|
|
{
|
|
return !HasBeenInit ? ActionResult.Error : ReportManager.Process(report);
|
|
}
|
|
|
|
// This is this method that will be eventually called
|
|
public ActionResult TriggerError(ReportCrash reportCrash, string errorMessage, Exception e)
|
|
{
|
|
return !HasBeenInit ? ActionResult.Error : _crashHandler.TriggerCrash(reportCrash, errorMessage, e);
|
|
}
|
|
|
|
[Obsolete("This method is deprecated. Please use the TriggerError method.", true)]
|
|
public ActionResult TriggerCrash(ReportCrash reportCrash, string errorMessage, Exception e)
|
|
{
|
|
return TriggerError(reportCrash, errorMessage, e);
|
|
}
|
|
|
|
|
|
[Obsolete("This method is deprecated. Please use the TriggerError method.", true)]
|
|
public ActionResult TriggerCrash(ReportType type, string errorMessage)
|
|
{
|
|
return TriggerError(type, Bloomberg.Message.Entities.TypeInfo.SUBTYPE_DEFAULT, errorMessage, null, false);
|
|
}
|
|
|
|
[Obsolete("This method is deprecated. Please use the TriggerError method.", true)]
|
|
public ActionResult TriggerCrash(ReportType type, string errorMessage, Exception e)
|
|
{
|
|
return TriggerError(type, Bloomberg.Message.Entities.TypeInfo.SUBTYPE_DEFAULT, errorMessage, e, false);
|
|
}
|
|
|
|
public ActionResult TriggerError(ReportType type, string errorMessage)
|
|
{
|
|
return TriggerError(type, Bloomberg.Message.Entities.TypeInfo.SUBTYPE_DEFAULT, errorMessage, null, false);
|
|
}
|
|
|
|
public ActionResult TriggerError(ReportType type, string errorMessage, Exception e)
|
|
{
|
|
return TriggerError(type, Bloomberg.Message.Entities.TypeInfo.SUBTYPE_DEFAULT, errorMessage, e, false);
|
|
}
|
|
|
|
public ActionResult TriggerError(ReportType type, string subType, string errorMessage, Exception e, bool isAsync)
|
|
{
|
|
SubTypeConfiguration subTypeConfiguration = Reporter.Instance.SubTypeManager.GetSubTypeConfiguration(subType);
|
|
|
|
if (subTypeConfiguration == null)
|
|
{
|
|
Tracer.LogFormat(TracerLogLevel.ERROR, "Could not find sub-type configuration with name {0}. General configuration for report of type {1} will be applied.", subType, type.ToString());
|
|
subType = Bloomberg.Message.Entities.TypeInfo.SUBTYPE_DEFAULT;
|
|
subTypeConfiguration = Reporter.Instance.SubTypeManager.GetSubTypeConfiguration(subType);
|
|
}
|
|
|
|
string subTypeConfigurationStr = subTypeConfiguration.ToString();
|
|
string subTypeConfigurationMessage = subTypeConfigurationStr == string.Empty ? string.Empty : $" and sub-type configuration : {subTypeConfigurationStr}";
|
|
|
|
Tracer.LogFormat(TracerLogLevel.INFO, "Triggering a report of type {0} with sub-type {1}{2}.", type.ToString(), subType, subTypeConfigurationMessage);
|
|
|
|
ReportInterface report = ReportInterface.GetReportInstance(type);
|
|
if (report != null && report is ReportCrash)
|
|
{
|
|
var crash = report as ReportCrash;
|
|
crash.SetType(type);
|
|
crash.SetSubType(subType);
|
|
crash.IsAsync = isAsync;
|
|
|
|
ActionResult result = TriggerError(crash, errorMessage, e);
|
|
|
|
return isAsync ? ActionResult.None : result;
|
|
}
|
|
return ActionResult.Error;
|
|
}
|
|
|
|
public ActionResult TriggerErrorAsync(ReportType type, string errorMessage)
|
|
{
|
|
return TriggerErrorAsync(type, errorMessage, null);
|
|
}
|
|
|
|
public ActionResult TriggerErrorAsync(ReportType type, string errorMessage, Exception e)
|
|
{
|
|
return TriggerError(type, Bloomberg.Message.Entities.TypeInfo.SUBTYPE_DEFAULT, errorMessage, e, true);
|
|
}
|
|
|
|
public ActionResult TriggerFlag()
|
|
{
|
|
return TriggerFlag(ReportFlag.Instance);
|
|
}
|
|
|
|
public ActionResult TriggerFlag(ReportFlag reportFlag)
|
|
{
|
|
return TriggerReport(reportFlag);
|
|
}
|
|
|
|
public void TriggerStatsSend()
|
|
{
|
|
TriggerStatsSend(ReportStats.Instance);
|
|
}
|
|
|
|
public void TriggerStatsSend(ReportStats reportStats)
|
|
{
|
|
if (!BloombergSettings.EnableStats) return;
|
|
|
|
if (reportStats != null)
|
|
{
|
|
reportStats.ForceSendingAllStats();
|
|
}
|
|
else
|
|
{
|
|
ReportStats.Instance.ForceSendingAllStats();
|
|
}
|
|
}
|
|
|
|
public void TriggerStatsSendAsync()
|
|
{
|
|
TriggerStatsSendAsync(ReportStats.Instance);
|
|
}
|
|
|
|
public void TriggerStatsSendAsync(ReportStats reportStats)
|
|
{
|
|
if (!BloombergSettings.EnableStats) return;
|
|
|
|
if (reportStats != null)
|
|
{
|
|
reportStats.ForceSendingAllStatsAsync();
|
|
}
|
|
else
|
|
{
|
|
ReportStats.Instance.ForceSendingAllStatsAsync();
|
|
}
|
|
}
|
|
|
|
public void SetSessionName(string sessionName)
|
|
{
|
|
ReportStats.Instance.SessionName = sessionName;
|
|
}
|
|
#endregion
|
|
|
|
public void Dispose()
|
|
{
|
|
_crashHandler?.Dispose();
|
|
_logFile?.Dispose();
|
|
_remoteDataWriter?.Dispose();
|
|
_crashAlreadyHandledFlag?.Dispose();
|
|
_managedFlagMutex?.Dispose();
|
|
RemoteDataCollector?.Dispose();
|
|
}
|
|
}
|
|
}
|