views:

70

answers:

1

I have a custom control that is show only with a given set of config values.

I want to capture the trace.axd data and output it to this control.

web.config

writeToDiagnosticsTrace="true" 
...
<listeners>
 name="WebPageTraceListener"
    type="System.Web.WebPageTraceListener, System.Web, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" 
</listeners>

I want to be able to load the trace.axd file in a usercontrol. Then have that usercontrol be loaded whenever needed.

Cheers,

+1  A: 

I have a working solution, with two caveats:

First, it will always render the trace output too early, because it's too late to do that in a Page.ProcessRequest() override (the Response object has already been cleaned up), so we're forced to do it during the Render phase, which means we'll miss some messages (most notably EndRender).

Implementing that behavior in a control exacerbates the problem, since we'd have to ensure our control is the last thing to render on the page in order to avoid missing more messages. For that reason, I chose to implement a custom page class instead of a custom control class. If you absolutely need a control class, it should be easy to convert (but leave me a word here if you need help).

Second, the profiler object that owns the data, HttpRuntime.Profile, is internal to the System.Web assembly, and of course the trace rendering routine is private to the Page class. So we have to abuse reflection, break encapsulation, and basically be evil in order to do what you want. If the ASP.NET trace implementation changes in the slightest, we're SOL.

That said, here's the traceable page class:

using System;
using System.Reflection;
using System.Web;
using System.Web.UI;

namespace StackOverflow.Bounties.Web.UI
{
    public class TraceablePage : Page
    {
        /// <summary>
        /// Gets or sets whether to render trace output.
        /// </summary>
        public bool EnableTraceOutput
        {
            get;
            set;
        }

        /// <summary>
        /// Abuses reflection to force the profiler's page output flag
        /// to true during a call to the page's trace rendering routine.
        /// </summary>
        protected override void Render(HtmlTextWriter writer)
        {
            base.Render(writer);
            if (!EnableTraceOutput) {
                return;
            }

            // Allow access to private and internal members.
            BindingFlags evilFlags
                = BindingFlags.Instance | BindingFlags.Static
                | BindingFlags.Public | BindingFlags.NonPublic;

            // Profiler profiler = HttpRuntime.Profile;
            object profiler = typeof(HttpRuntime)
                .GetProperty("Profile", evilFlags).GetGetMethod(true)
                .Invoke(null, null);

            // profiler.PageOutput = true;
            profiler.GetType().GetProperty("PageOutput", evilFlags)
                .GetSetMethod(true).Invoke(profiler, new object[] { true });

            // this.ProcessRequestEndTrace();
            typeof(Page).GetMethod("ProcessRequestEndTrace", evilFlags)
                .Invoke(this, null);

            // profiler.PageOutput = false;
            profiler.GetType().GetProperty("PageOutput", evilFlags)
                .GetSetMethod(true).Invoke(profiler, new object[] { false });
        }
    }
}

And here's its test page, which uses an AutoPostBack check box to demonstrate its behavior across postbacks:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="TestTracePage.aspx.cs"
    Inherits="StackOverflow.Bounties.Web.UI.TestTracePage" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
<html xmlns="http://www.w3.org/1999/xhtml"&gt;
<head runat="server">
    <title>TraceablePage Test</title>
</head>
<body>
    <form id="form" runat="server">
    <h2>TraceablePage Test</h2>
    <p>
        <asp:CheckBox id="enableTrace" runat="server"
            AutoPostBack="True" Text="Enable trace output"
            OnCheckedChanged="enableTrace_CheckedChanged" />
    </p>
    </form>
</body>
</html>

And the code behind:

using System;
using System.Web.UI;

namespace StackOverflow.Bounties.Web.UI
{
    public partial class TestTracePage : TraceablePage
    {
        protected void enableTrace_CheckedChanged(object sender, EventArgs e)
        {
            EnableTraceOutput = enableTrace.Checked;
        }
    }
}

The test page renders like this on first load:

Trace disabled

Checking the box posts back and renders the trace output:

Trace enabled

Clearing the check box again suppresses the trace output, as expected.

Frédéric Hamidi
Wow, thank you for putting all this effort into this. This is work just fine. I tweaked it to fit my code structure. Excellent.
Arnej65
@Arnej65, you're welcome. That was a nice challenge, and who knows, maybe I'll need that too somewhere along the way :)
Frédéric Hamidi