How to get ELMAH to include session values?

后端 未结 3 809
伪装坚强ぢ
伪装坚强ぢ 2021-02-15 01:24

NOTE: I know the various reasons to avoid using the session, but this is a project I\'ve inherited, so please skip that part of any replies :)

Since it\'s a solved probl

相关标签:
3条回答
  • 2021-02-15 02:09

    Atif replied on twitter to say there's no known patch:

    http://twitter.com/raboof/statuses/7229453423

    So I created a patch that does so:

    http://twitter.com/manningj/statuses/7231616905

    http://blog.sublogic.com/2009/12/patch-to-enable-session-variable-logging-with-elmah/

    0 讨论(0)
  • 2021-02-15 02:12

    The old patch that can be dug up is unfortunately a little outdated with Elmah now. Here's what I did to log session variables in version 2.0.15523.27 Based on an older patch found here: https://storage.googleapis.com/google-code-attachments/elmah/issue-12/comment-5/elmah-sessionVariables.patch

    In Error.cs

    Import System.Web.SessionState

    using System.Web.SessionState;
    

    Find:

    private NameValueCollection _serverVariables;
    private NameValueCollection _queryString;
    private NameValueCollection _form;
    private NameValueCollection _cookies;
    

    Add below:

    private NameValueCollection _sessionVariables;
    

    Find:

    _serverVariables = CopyCollection(request.ServerVariables);
    _queryString = CopyCollection(qsfc.QueryString);
    _form = CopyCollection(qsfc.Form);
    _cookies = CopyCollection(qsfc.Cookies);
    

    Add below:

    _sessionVariables = CopyCollection(context.Session);
    

    Find:

    public NameValueCollection Cookies 
    {
        get { return FaultIn(ref _cookies); }
    }
    

    Add below:

    /// <summary>
    /// Gets a collection representing the session variables captured as part of the diagnostic data
    /// </summary>
    
    public NameValueCollection SessionVariables
    {
        get { return FaultIn(ref _sessionVariables); }
    }
    

    Find:

    copy._serverVariables = CopyCollection(_serverVariables);
    copy._queryString = CopyCollection(_queryString);
    copy._form = CopyCollection(_form);
    copy._cookies = CopyCollection(_cookies);
    

    Add below:

    copy._sessionVariables = CopyCollection(_sessionVariables);
    

    Find:

    private static NameValueCollection CopyCollection(NameValueCollection collection)
    

    Add above:

    private static NameValueCollection CopyCollection(HttpSessionStateBase sessionVariables)
    {
        if (sessionVariables == null || sessionVariables.Count == 0)
            return null;
    
        var copy = new NameValueCollection(sessionVariables.Count);
    
        for (int i = 0; i < sessionVariables.Count; i++)
            copy.Add(sessionVariables.Keys[i], sessionVariables[i].ToString());
    
        return copy;
    }
    

    In ErrorJson.cs

    Find:

    Member(writer, "queryString", error.QueryString);
    Member(writer, "form", error.Form);
    Member(writer, "cookies", error.Cookies);
    

    Add below:

    Member(writer, "sessionVariables", error.SessionVariables);
    

    In ErrorXml.cs

    Find:

    case "form"            : collection = error.Form; break;
    case "cookies"         : collection = error.Cookies; break;
    

    Add below:

    case "sessionVariables": collection = error.SessionVariables; break;
    

    Find:

    WriteCollection(writer, "form", error.Form);
    WriteCollection(writer, "cookies", error.Cookies);
    

    Add below:

    WriteCollection(writer, "sessionVariables", error.SessionVariables);
    

    In ErrorMailHtmlPage.cshtml

    Find:

    <p>@(RenderPartial<PoweredBy>())</p>
    

    Add above:

    @foreach (var collection in 
        from collection in new[] 
        {
            new
            {
                Id    = "SessionVariables",
                Title = "Session Variables",
                Items = error.SessionVariables,
            }
        }
        let data = collection.Items
        where data != null && data.Count > 0
        let items = from i in Enumerable.Range(0, data.Count)
            select KeyValuePair.Create(data.GetKey(i), data[i])
        select new
        {
            collection.Id, 
            collection.Title,
            Items = items.OrderBy(e => e.Key, StringComparer.OrdinalIgnoreCase)
        }
        )
    {
        <div id="@collection.Id">
            <h1>@collection.Title</h1>
            <table class="collection">
                <tr><th>Name</th>            
                    <th>Value</th></tr>
                @foreach (var item in collection.Items)
                {
                    <tr><td>@item.Key</td>
                        <td>@item.Value</td></tr>
                }
            </table>
        </div>
    }
    

    After making changes to ErrorMailHtmlPage.cshtml in Visual Studio, right click on the file and "Run Custom Tool" to generate the code for ErrorMailHtmlPage.generated.cs


    In ErrorDetailPage.cshtml

    Find (at the end of the file):

    @*
    }
    *@
    

    Add above:

    @{
        var sessioncollection = new
        {
            Data = error.SessionVariables,
            Id = "SessionVariables",
            Title = "Session Variables",
        };
    
        //
        // If the collection isn't there or it's empty, then bail out.
        //
    
        if (sessioncollection.Data != null && sessioncollection.Data.Count > 0)
        {
            var items =
                from i in Enumerable.Range(0, sessioncollection.Data.Count)
                select new
                {
                    Index = i,
                    Key = sessioncollection.Data.GetKey(i),
                    Value = sessioncollection.Data[i],
                };
    
            items = items.OrderBy(e => e.Key, StringComparer.OrdinalIgnoreCase);
    
            <div id="@sessioncollection.Id">
    
                <h2>@sessioncollection.Title</h2>
                @*
                    // Some values can be large and add scroll bars to the page
                    // as well as ruin some formatting. So we encapsulate the
                    // table into a scrollable view that is controlled via the
                    // style sheet.
                *@
    
                <div class="scroll-view">
    
                    <table cellspacing="0" style="border-collapse:collapse;" class="table table-condensed table-striped">
                        <tr>
                            <th class="name-col" style="white-space:nowrap;">Name</th>
                            <th class="value-col" style="white-space:nowrap;">Value</th>
                        </tr>
    
                        @foreach (var item in items)
                        {
                            <tr class="@(item.Index % 2 == 0 ? "even" : "odd")">
                                <td class="key-col">@item.Key</td>
                                <td class="value-col">@item.Value</td>
                            </tr>
                        }
    
                    </table>
                </div>
            </div>
        }
    }
    

    After making changes to ErrorDetailPage.cshtml in Visual Studio, right click on the file and "Run Custom Tool" to generate the code for ErrorDetailPage.generated.cs


    Now you can build (I just used the build.cmd file that was included with the project) and grab the ddl files from bin that are needed.

    • AntiXssLibrary.dll
    • Elmah.AspNet.dll
    • Elmah.dll

    You may also have to modify the web.config in your project now to include the version on any references to Elmah. If you're using Resharper you can just click on each of these and fix them. (There's probably a different way this is supposed to be done to avoid this but I'm not sure and I wasn't too worried about figuring it out)

    An example of one of them though would be changing

    <section name="security" requirePermission="false" type="Elmah.SecuritySectionHandler, Elmah" />
    

    to

    <section name="security" requirePermission="false" type="Elmah.SecuritySectionHandler, Elmah.AspNet, Version=2.0.15523.27, Culture=neutral, PublicKeyToken=null" />
    
    0 讨论(0)
  • 2021-02-15 02:19

    Rather than patching Elmah, I did this with Exception data. In Global.asax I inserted the extra data into the exception on Application_Error(). "HistoryStack" is my own class for recording user history, including button and tab clicks:

    void Application_Error(object sender, EventArgs e)
    {
        Exception ex = Server.GetLastError().GetBaseException();
        var stack = HistoryStack.Dump(); // essentially grabs data from the session
        ex.Data.Add("historyStack", stack);
    }
    

    Then, in ErrorMail_Mailing() I grabbed the data back and appended it in the email:

    void ErrorMail_Mailing(object sender, Elmah.ErrorMailEventArgs e)
    {
        var stack = e.Error.Exception.Data["historyStack"] as Stack<string>;
        if (stack == null && e.Error.Exception.InnerException != null)
        {
            // could probably skip the first try and go straight to this assignment:
            stack = e.Error.Exception.InnerException.Data["historyStack"] as Stack<string>;
        }
    
        if (stack != null && stack.Count > 0)
        {
            e.Mail.Body = e.Mail.Body + "<h1>Browsing History</h1>" + System.Environment.NewLine;
            while (stack.Count > 0)
            {
                e.Mail.Body = e.Mail.Body + stack.Pop() + "<br />" + System.Environment.NewLine;
            }
        }
    }
    

    Now this data is appended to the bottom of the email. No patches or extensions necessary.

    0 讨论(0)
提交回复
热议问题