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 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 Constructors /// /// Initializes a new instance of the class. /// /// The application manager. /// The authentication provider. public DefaultEventLogger(IStudioApplicationManager applicationManager, IAuthenticationProvider authenticationProvider) { _hostName = Environment.MachineName; _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() { 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; } } /// /// 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) { 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) { 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() { 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 } }