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
///
/// Singleton
///
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; } }
///
/// Specify if the crash reporting is still enabled.
/// This can be changed when connection to server or reporter failed.
///
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
///
/// Private constructor called by Initialize
///
///
private Reporter(BloombergInterface bloombergInterface)
{
BloombergInterface = bloombergInterface;
ReportManager = new ReportManager();
RemoteDataCollector = new RemoteDataCollector();
ConnectionManager = new ConnectionManager();
SubTypeManager = new SubTypeManager();
HasBeenInit = false;
}
///
/// Client has already instantiated his interface
///
/// The interface implemented by the client
/// True to enable crash handling
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.");
}
}
///
/// Un uninitialize Bloomberg
///
public static void UnInitialize()
{
if (Instance != null)
{
Instance.InternalUnInit();
}
}
///
/// This does the initialization of bloomberg.
/// - Get the GameSettings & BloombergSettings
/// - Initialize log file
/// - VerifyConnection
///
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> Translators;
static SettingLoader()
{
Translators = new Dictionary>();
var intTranslator = new Func(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 does not work
if (key.PropertyType.IsGenericType && key.PropertyType.GetGenericTypeDefinition() == typeof(List<>) && key.PropertyType.GetGenericArguments()[0] == typeof(ChangelistInfo))
{
var clList = (List)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);
}
}
}
}
///
/// Uninitialize Bloomberg
///
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
///
/// 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.
///
///
///
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();
}
}
}