using Google.Protobuf;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Tango.BL;
using Tango.BL.Entities;
using Tango.BL.Enumerations;
using Tango.Core;
using Tango.Integration.ExternalBridge;
using Tango.Logging;
using Tango.MachineStudio.Common.Authentication;
using Tango.MachineStudio.Common.Diagnostics;
using Tango.MachineStudio.Common.StudioApplication;
using Tango.PMR.Diagnostics;
using Tango.Integration.Operation;
using Tango.Core.ExtensionMethods;
using Tango.Transport;
namespace Tango.MachineStudio.Common.EventLogging
{
///
/// Represents the default database events logger.
///
///
public class DefaultEventLogger : ExtendedObject, IEventLogger
{
private ObservablesContext _db;
private static object _lockInit = new object();
private Thread _logThread;
private ConcurrentQueue _events;
private IStudioApplicationManager _application;
private IAuthenticationProvider _authentication;
private Dictionary _eventTypesGuids;
private String _hostName;
private bool _isInitialized;
private List _pendingEvents;
#region Events
///
/// Occurs when a new machine event has been logged.
///
public event EventHandler NewLog;
#endregion
#region Properties
///
/// Gets or sets a value indicating whether to save the incoming events to database.
///
public bool SaveToDB { get; set; }
#endregion
#region Constructors
///
/// Initializes a new instance of the class.
///
/// The application manager.
/// The authentication provider.
public DefaultEventLogger(IStudioApplicationManager applicationManager, IAuthenticationProvider authenticationProvider)
{
_hostName = Environment.MachineName;
SaveToDB = true;
_events = new ConcurrentQueue();
_pendingEvents = new List();
_eventTypesGuids = new Dictionary();
_application = applicationManager;
_authentication = authenticationProvider;
_logThread = new Thread(LogThreadMethod);
_logThread.IsBackground = true;
_logThread.Start();
_application.ConnectedMachineChanged += _application_ConnectedMachineChanged;
}
#endregion
#region Private Methods
private void Init()
{
lock (_lockInit)
{
if (!_isInitialized)
{
try
{
_db = ObservablesContext.CreateDefault();
_db.EventTypes.ToList();
foreach (var type in _db.EventTypes)
{
_eventTypesGuids.Add((EventTypes)type.Code, type);
}
_isInitialized = true;
}
catch
{
_isInitialized = false;
}
}
}
}
#endregion
#region Event Handlers
///
/// Handle the application manager connected machine changed event.
///
/// The sender.
/// The machine.
private void _application_ConnectedMachineChanged(object sender, IExternalBridgeClient machine)
{
if (machine != null)
{
if (machine.MachineEventsStateProvider != null)
{
machine.MachineEventsStateProvider.NewEvents -= MachineEventsStateProvider_NewEvents;
machine.MachineEventsStateProvider.NewEvents += MachineEventsStateProvider_NewEvents;
machine.MachineEventsStateProvider.EventsResolved -= MachineEventsStateProvider_EventsResolved;
machine.MachineEventsStateProvider.EventsResolved += MachineEventsStateProvider_EventsResolved;
}
machine.RequestSent -= Machine_RequestSent;
machine.RequestFailed -= Machine_RequestFailed;
machine.ResponseReceived -= Machine_ResponseReceived;
machine.RequestSent += Machine_RequestSent;
machine.RequestFailed += Machine_RequestFailed;
machine.ResponseReceived += Machine_ResponseReceived;
SaveToDB = !(machine is IExternalBridgeSecureClient);
}
}
///
/// Handles the RequestSent event of the connected machine.
///
/// The sender.
/// The message.
private void Machine_RequestSent(object sender, IMessage message)
{
//Log(EventTypes.REQUEST_SENT, String.Format("Sending request '{0}'...{1}{2}", message.GetType().Name, Environment.NewLine, message.ToJsonString()));
}
///
/// Handles the RequestFailed event of the connected machine.
///
/// The source of the event.
/// The instance containing the event data.
private void Machine_RequestFailed(object sender, RequestFailedEventArgs e)
{
//Log(EventTypes.REQUEST_FAILED, String.Format("Request failed '{0}'...{1}{2}{1}{3}", e.Message.GetType().Name, Environment.NewLine, e.Message.ToJsonString(), e.Exception.ToString()));
}
///
/// Handles the ResponseReceived event of the connected machine.
///
/// The sender.
/// The message.
private void Machine_ResponseReceived(object sender, IMessage message)
{
//Log(EventTypes.RESPONSE_RECEIVED, String.Format("Response received '{0}'...{1}{2}", message.GetType().Name, Environment.NewLine, message.ToJsonString()));
}
///
/// Handles the connected machine events state provider NewEvents event.
///
/// The sender.
/// The events.
private void MachineEventsStateProvider_NewEvents(object sender, IEnumerable events)
{
foreach (var ev in events)
{
Log(ev);
}
}
///
/// Handles the connected machine events state provider EventsResolved event.
///
/// The sender.
/// The events.
private void MachineEventsStateProvider_EventsResolved(object sender, IEnumerable events)
{
//foreach (var ev in events)
//{
// Log(String.Format("Event '{0}' resolved.", ev.EventType.Name));
//}
}
#endregion
#region Logging
///
/// Logs the specified machine event.
///
/// The machine event.
public void Log(MachinesEvent machineEvent)
{
Init();
machineEvent.HostName = _hostName;
machineEvent.EventType = _eventTypesGuids[machineEvent.Type];
if (_application.ConnectedMachine == null || _authentication.CurrentUser == null)
{
_pendingEvents.Add(machineEvent);
}
else
{
lock (_pendingEvents)
{
if (_pendingEvents.Count > 0)
{
var pending = _pendingEvents.ToList();
_pendingEvents.Clear();
foreach (var ev in pending)
{
Log(ev);
}
}
}
LogManager.Log("Logging event " + machineEvent.EventType.Name);
machineEvent.MachineGuid = _application.Machine.Guid;
machineEvent.UserGuid = _authentication.CurrentUser.Guid;
machineEvent.User = _authentication.CurrentUser;
_events.Enqueue(machineEvent);
NewLog?.Invoke(this, machineEvent);
}
}
///
/// Logs the specified event type.
///
/// Type of the event.
/// The message.
public void Log(EventTypes eventType, string message, bool write_to_db = true)
{
Init();
MachinesEvent machineEvent = new MachinesEvent();
machineEvent.DateTime = DateTime.UtcNow;
machineEvent.Description = message;
machineEvent.EventType = _eventTypesGuids[eventType];
machineEvent.EventTypeGuid = machineEvent.EventType.Guid;
if (write_to_db && SaveToDB)
{
Log(machineEvent);
}
else
{
NewLog?.Invoke(this, machineEvent);
}
}
///
/// Logs the specified hardware event.
///
/// The hardware event.
public void Log(Event hardwareEvent)
{
Log((EventTypes)hardwareEvent.Type, hardwareEvent.Message);
}
///
/// Logs the specified exception using the .
///
/// The exception.
public void Log(Exception exception)
{
Log(EventTypes.APPLICATION_EXCEPTION, exception.ToString());
}
///
/// Logs the specified exception using the .
///
/// The exception.
///
public void Log(Exception exception, string description)
{
Log(EventTypes.APPLICATION_EXCEPTION, description + Environment.NewLine + exception.ToString());
}
///
/// Logs the specified message using the .
///
/// The message.
public void Log(String message)
{
Log(EventTypes.APPLICATION_INFORMATION, message);
}
///
/// Logging thread loop.
///
private void LogThreadMethod()
{
while (true)
{
FlushAll();
Thread.Sleep(5000);
}
}
///
/// Immediately saves all pending events to database.
///
public void FlushAll()
{
if (!SaveToDB)
{
return;
}
bool _saveChanges = false;
while (_events.Count > 0)
{
MachinesEvent ev = null;
if (_events.TryDequeue(out ev))
{
ev.User = null;
_db.MachinesEvents.Add(ev);
_saveChanges = true;
}
}
if (_saveChanges)
{
try
{
_db.SaveChanges();
}
catch (Exception ex)
{
LogManager.Log(ex, "Error saving machine event to database.");
}
}
}
#endregion
}
}