Skip to content

Instantly share code, notes, and snippets.

@smarenich
Created September 17, 2015 11:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save smarenich/fa0e95fe64036d5fdddf to your computer and use it in GitHub Desktop.
Save smarenich/fa0e95fe64036d5fdddf to your computer and use it in GitHub Desktop.
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