Created
September 17, 2015 11:44
-
-
Save smarenich/fa0e95fe64036d5fdddf to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Collections.Specialized; | |
using System.Data.SqlClient; | |
using System.Diagnostics; | |
using System.Runtime.ExceptionServices; | |
using System.Runtime.Serialization; | |
using System.Text; | |
using System.Threading; | |
using System.Web; | |
using System.Linq; | |
using System.IO; | |
using System.Reflection; | |
using System.Configuration; | |
using System.Configuration.Provider; | |
using System.Web.Configuration; | |
using System.Security.Permissions; | |
using PX.Common; | |
using PX.Data; | |
using System.Runtime.InteropServices; | |
using PX.Web.UI; | |
using System.Collections; | |
public static class SessionLogger | |
{ | |
private class UserInfo | |
{ | |
public string Username; | |
public Dictionary<String, SessionInfo> Sessions = new Dictionary<String, SessionInfo>(); | |
public DateTime UserStart = DateTime.Now; | |
public DateTime UserLast = DateTime.Now; | |
} | |
private class SessionInfo | |
{ | |
public String SessionID; | |
public Dictionary<String, WindowInfo> Windows = new Dictionary<String, WindowInfo>(); | |
public DateTime SessionStart = DateTime.Now; | |
public DateTime SessionLast = DateTime.Now; | |
} | |
private class WindowInfo | |
{ | |
public String Suffix; | |
public String Company; | |
public DateTime WindowStart = DateTime.Now; | |
public DateTime WindowLast = DateTime.Now; | |
} | |
private class GuidInfo | |
{ | |
public String Guid; | |
public String SessionID; | |
public String WindowID; | |
public String UserID; | |
public String CompanyID; | |
public String ScreenID; | |
public DateTime First = DateTime.Now; | |
public DateTime Last = DateTime.Now; | |
} | |
private const uint HRFileLocked = 0x80070020; | |
private const uint HRPortionOfFileLocked = 0x80070021; | |
[ThreadStatic] | |
private static bool IsRecursive; | |
private static Dictionary<String, GuidInfo> Guids = new Dictionary<String, GuidInfo>(); | |
private static Dictionary<String, UserInfo> Sessions = new Dictionary<String, UserInfo>(); | |
public static void Initialise() | |
{ | |
AppDomain.CurrentDomain.FirstChanceException += OnCurrentDomainOnFirstChanceException; | |
} | |
private static void OnCurrentDomainOnFirstChanceException(object o, FirstChanceExceptionEventArgs args) | |
{ | |
PrepareLogException(args.Exception); | |
} | |
private static void PrepareLogException(Exception exception) | |
{ | |
if (exception is IOException) | |
{ | |
return; // can`t write something to file, so do nothing to prevent stack overflow | |
} | |
if (IsRecursive) | |
return; | |
try | |
{ | |
IsRecursive = true; | |
SessionLogger.LogException(exception); | |
} | |
catch | |
{ | |
// prevent stack overflow | |
} | |
finally | |
{ | |
IsRecursive = false; | |
} | |
} | |
private static void LogException(Exception exception) | |
{ | |
String path = Path.Combine(AppDomain.CurrentDomain.GetData("DataDirectory").ToString(), "Session.log"); | |
var trace = new StackTrace(2); | |
if (IsExcluded(exception, trace)) | |
return; | |
HttpContext context = HttpContext.Current; | |
if (context == null || context.Session == null || context.Request == null) return; | |
string screenid = PXContext.GetScreenID() ?? String.Empty; | |
string username = PXLogin.ExtractUsername(PXContext.PXIdentity.IdentityName) ?? String.Empty; | |
string company = PXLogin.ExtractCompany(PXContext.PXIdentity.IdentityName) ?? String.Empty; | |
bool abandone = context.Session.IsNewSession; | |
string contextGuid = (string)PXContext.Session["DataSourceSessionID"]; | |
string pageGuid = JSManager.ReadHiddenField(context.Request, "DataSourceSessionID"); | |
string sessionsuffix = PXSessionStateStore.GetSuffix(context) ?? "NONE"; | |
string sessionID = context.Session.SessionID ?? "NONE"; | |
string sessionTimeout = context.Session.Timeout.ToString(); | |
StringBuilder otherinfo = new StringBuilder(); | |
UserInfo user; | |
if (Sessions.TryGetValue(username, out user)) | |
{ | |
otherinfo.AppendLine(("User First Request: " + user.UserStart.ToString("HH:mm:ss.ffff"))); | |
foreach (SessionInfo item in user.Sessions.Values) | |
{ | |
otherinfo.AppendFormat("SessionID:{0} (FR:{1}, LR{2}): ", item.SessionID, item.SessionStart.ToString("HH:mm:ss.ffff"), item.SessionLast.ToString("HH:mm:ss.ffff")); | |
otherinfo.AppendLine(); | |
foreach (WindowInfo window in item.Windows.Values) | |
{ | |
otherinfo.AppendFormat("\tW:{0},C:{1}(FR:{2},LR{3})", window.Suffix, window.Company, window.WindowStart.ToString("HH:mm:ss.ffff"), window.WindowLast.ToString("HH:mm:ss.ffff")); | |
} | |
otherinfo.AppendLine(); | |
} | |
} | |
StringBuilder guidinfo = new StringBuilder(); | |
GuidInfo guid; | |
if (Guids.TryGetValue(pageGuid, out guid)) | |
{ | |
guidinfo.AppendLine("Guid: " + guid.Guid); | |
guidinfo.AppendLine("Guid User: " + guid.UserID); | |
guidinfo.AppendLine("Guid Company: " + guid.CompanyID); | |
guidinfo.AppendLine("Guid Session: " + guid.SessionID); | |
guidinfo.AppendLine("Guid Window: " + guid.WindowID); | |
guidinfo.AppendLine("Guid Screen: " + guid.ScreenID); | |
guidinfo.AppendLine("Guid FirstRequest: " + guid.First.ToString("HH:mm:ss.ffff")); | |
guidinfo.AppendLine("Guid LastRequest: " + guid.Last.ToString("HH:mm:ss.ffff")); | |
guidinfo.AppendLine(); | |
guidinfo.AppendLine("All Guids for this user:"); | |
} | |
else guidinfo.AppendLine("There is not entry for guid {'" + pageGuid + "'}"); | |
guidinfo.AppendLine(); | |
guidinfo.AppendLine("Other Guids for users " + username + ":"); | |
foreach (GuidInfo gi in Guids.Values.Where(g => g.UserID == username).OrderBy(g => g.Last)) | |
{ | |
guidinfo.AppendFormat("Guid: {0}, User: {1}, Company: {2}, Session: {3}, Screen: {4}, Window: {5}, FR: {6}, LR: {7}", | |
gi.Guid, gi.UserID, gi.CompanyID, gi.SessionID, gi.ScreenID, gi.WindowID, gi.First.ToString("HH:mm:ss.ffff"), gi.Last.ToString("HH:mm:ss.ffff")); | |
guidinfo.AppendLine(); | |
} | |
guidinfo.AppendLine(); | |
/*guidinfo.AppendLine("All Other Guids:"); | |
foreach (GuidInfo gi in Guids.Values.OrderBy(g => g.Last)) | |
{ | |
guidinfo.AppendFormat("Guid: {0}, User: {1}, Company: {2}, Session: {3}, Screen: {4}, Window: {5}, FR: {6}, LR: {7}", | |
gi.Guid, gi.UserID, gi.CompanyID, gi.SessionID, gi.ScreenID, gi.WindowID, gi.First.ToString("HH:mm:ss.ffff"), gi.Last.ToString("HH:mm:ss.ffff")); | |
guidinfo.AppendLine(); | |
} | |
guidinfo.AppendLine();*/ | |
Stream stream; | |
while (!TryOpen(path, out stream)) | |
{ | |
Thread.Sleep(100); // wait for file to unlock | |
} | |
using (stream) | |
{ | |
StreamWriter tw = new StreamWriter(stream); | |
tw.WriteLine("\n================================================================================"); | |
tw.WriteLine("================================================================================"); | |
tw.Write(DateTime.Now.ToString("MMM/dd HH:mm:ss.ffff ")); | |
if (HttpContext.Current != null) | |
{ | |
tw.Write(" "); | |
tw.Write(HttpContext.Current.Request.Url); | |
} | |
tw.WriteLine(); | |
tw.WriteLine("Message: " + exception.Message); | |
tw.WriteLine("User: " + username); | |
tw.WriteLine("Company: " + company); | |
tw.WriteLine("Screen: " + screenid); | |
tw.WriteLine("Session Timeout: " + sessionTimeout); | |
tw.WriteLine(); | |
tw.WriteLine("ContextSessionKey: " + contextGuid); | |
tw.WriteLine("PageSessionKey: " + pageGuid); | |
tw.WriteLine("IsNewSession: " + abandone); | |
tw.WriteLine(); | |
tw.WriteLine("SessionID: " + sessionID); | |
tw.WriteLine("Session Suffix: " + sessionsuffix); | |
tw.WriteLine(); | |
if (abandone) | |
tw.WriteLine("Session expired, reason: Context.Session.IsNewSession = true"); | |
if (!String.Equals(pageGuid, contextGuid)) | |
tw.WriteLine("Session expired, reason: different IDs. ID in PXContext.Session: '{0}', ID in JS hidden field: '{1}'.", contextGuid, pageGuid); | |
tw.WriteLine(); | |
tw.WriteLine(); | |
tw.WriteLine("Guid Info: "); | |
tw.WriteLine(guidinfo.ToString()); | |
tw.WriteLine("Total Guids: " + Guids.Count); | |
tw.WriteLine(); | |
tw.WriteLine(); | |
tw.WriteLine("Other Info: "); | |
tw.WriteLine(otherinfo.ToString()); | |
tw.WriteLine(); | |
tw.Close(); | |
} | |
} | |
public static void OnPreHandler() | |
{ | |
//LogSession(); | |
} | |
public static void OnPostHandler() | |
{ | |
LogSession(); | |
LogGuid(); | |
} | |
public static void LogSession() | |
{ | |
HttpContext context = HttpContext.Current; | |
if (context == null || context.Session == null || context.Request == null) return; | |
try | |
{ | |
string username = PXLogin.ExtractUsername(PXContext.PXIdentity.IdentityName) ?? String.Empty; | |
string company = PXLogin.ExtractCompany(PXContext.PXIdentity.IdentityName) ?? String.Empty; | |
string sessionid = context.Session.SessionID; | |
if (String.IsNullOrEmpty(username)) return; | |
string suffix = PXSessionStateStore.GetSuffix(context) ?? "NONE"; | |
lock (((ICollection)Sessions).SyncRoot) | |
{ | |
UserInfo user; | |
if (!Sessions.TryGetValue(username, out user)) | |
Sessions[username] = user = new UserInfo() { Username = username }; | |
user.UserLast = DateTime.Now; | |
SessionInfo item; | |
if (!user.Sessions.TryGetValue(sessionid, out item)) | |
user.Sessions[sessionid] = item = new SessionInfo() { SessionID = sessionid }; | |
item.SessionLast = DateTime.Now; | |
WindowInfo window; | |
if (!item.Windows.TryGetValue(suffix, out window)) | |
item.Windows[suffix] = window = new WindowInfo() { Suffix = suffix }; | |
window.Company = company; | |
window.WindowLast = DateTime.Now; | |
} | |
} | |
catch { return; } | |
} | |
public static void LogGuid() | |
{ | |
HttpContext context = HttpContext.Current; | |
if (context == null || context.Session == null || context.Request == null) return; | |
try | |
{ | |
string username = PXLogin.ExtractUsername(PXContext.PXIdentity.IdentityName) ?? String.Empty; | |
string company = PXLogin.ExtractCompany(PXContext.PXIdentity.IdentityName) ?? String.Empty; | |
string sessionid = context.Session.SessionID; | |
if (String.IsNullOrEmpty(username)) return; | |
string suffix = PXSessionStateStore.GetSuffix(context) ?? "NONE"; | |
string guid = (string)PXContext.Session["DataSourceSessionID"]; | |
string screenid = PXContext.GetScreenID() ?? "NONE"; | |
if (!String.IsNullOrEmpty(guid)) | |
{ | |
lock (((ICollection)Guids).SyncRoot) | |
{ | |
GuidInfo info; | |
if (!Guids.TryGetValue(guid, out info)) | |
Guids[guid] = info = new GuidInfo() { UserID = username, CompanyID = company, SessionID = sessionid, WindowID = suffix, Guid = guid, ScreenID = screenid }; | |
if (!info.CompanyID.Contains(company)) info.WindowID = info.WindowID + ";" + company; | |
if (!info.ScreenID.Contains(screenid)) info.ScreenID = info.ScreenID + ";" + screenid; | |
if (!info.WindowID.Contains(suffix)) info.WindowID = info.WindowID + ";" + suffix; | |
info.Last = DateTime.Now; | |
} | |
} | |
} | |
catch { return; } | |
} | |
public static void SessionExpired(string userName, string ipAddress, string sessionID) | |
{ | |
PrepareLogException(new Exception(String.Format("Session Expired: {2}, username: {0}, ipAddress: {1}", userName, ipAddress, sessionID))); | |
} | |
private static bool IsExcluded(Exception exception, StackTrace trace) | |
{ | |
return !exception.Message.Contains("Session has expired"); | |
} | |
private static bool FileIsLocked(IOException ioException) | |
{ | |
var errorCode = (uint)Marshal.GetHRForException(ioException); | |
return errorCode == HRFileLocked || errorCode == HRPortionOfFileLocked; | |
} | |
private static bool TryOpen(string path, out Stream stream) | |
{ | |
try | |
{ | |
stream = File.Open(path, FileMode.Append); | |
return true; | |
} | |
catch (IOException e) | |
{ | |
if (!FileIsLocked(e)) | |
throw; | |
stream = null; | |
return false; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment