<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>ivern.org</title>
	
	<link>http://ivern.org</link>
	<description>Things about software that interest me.</description>
	<lastBuildDate>Mon, 06 Feb 2012 15:33:43 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/ivern-org" /><feedburner:info uri="ivern-org" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><feedburner:browserFriendly></feedburner:browserFriendly><item>
		<title>Logging Google App Engine Queries</title>
		<link>http://ivern.org/2012/01/logging-google-app-engine-queries/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=logging-google-app-engine-queries</link>
		<comments>http://ivern.org/2012/01/logging-google-app-engine-queries/#comments</comments>
		<pubDate>Tue, 31 Jan 2012 02:29:12 +0000</pubDate>
		<dc:creator>Javier Fernandez-Ivern</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[AOP]]></category>
		<category><![CDATA[GAE]]></category>
		<category><![CDATA[Guice]]></category>
		<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://ivern.org/?p=6</guid>
		<description><![CDATA[I&#8217;ve recently started developing for Google App Engine again after spending a few months doing heavy development for a .NET application using NHibernate.  One of the things I&#8217;ve been missing the most is having clear visibility into what&#8217;s going on &#8230; <a href="http://ivern.org/2012/01/logging-google-app-engine-queries/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve recently started developing for Google App Engine again after spending a few months doing heavy development for a .NET application using NHibernate.  One of the things I&#8217;ve been missing the most is having clear visibility into what&#8217;s going on in the datastore.  Going from NHProf to the GAE development mode console is, to put it mildly, <em>slumming it.</em></p>
<p>After digging around a bit, I couldn&#8217;t find any way to peek into a query&#8217;s execution plan.  What I found, though, was a single entry point for Query object execution which would allow me to at least create a log of all executed queries.  This is different than logging queries in application code because a lot of the querying done in GAE (at least if you&#8217;re using Objectify, which I am) is lazy and therefore only interesting to me if and when it actually gets executed.  There&#8217;s some bad news, though: this is a private method, and therefore can&#8217;t be intercepted with Guice.  Time to bring out the AspectJ bazooka:</p>
<pre class="brush: java; title: ; notranslate">
public aspect QueryLogger {
    @Inject Logger logger;

    @Before(&quot;execution(* com.google.appengine.api.datastore.PreparedQueryImpl.runQuery(Query, ..))&quot;)
    public void logQuery(JoinPoint joinPoint) {
        Query query = (Query) joinPoint.getArgs()[0];
        logger.info(QueryToString.queryToString(query));
    }
}
</pre>
<p>The implementation of queryToString uses a syntax that&#8217;s much like Objectify&#8217;s, since that is what I&#8217;m used to. However, it&#8217;d be relatively simple to change the output format to match a different syntax.</p>
<pre class="brush: java; title: ; notranslate">
static String queryToString(Query query) {
    final StringBuilder builder = new StringBuilder();

    final String kind = query.getKind();
    builder.append(&quot;query(&quot;);
    builder.append(kind);
    builder.append(&quot;.class)&quot;);

    final Key ancestor = query.getAncestor();
    if (ancestor != null) {
        builder.append(&quot;.ancestor(&quot;);
        builder.append(ancestor.toString());
        builder.append(&quot;)&quot;);
    }

    final List&lt;Query.FilterPredicate&gt; filterPredicateList = query.getFilterPredicates();
    for (Query.FilterPredicate filterPredicate : filterPredicateList) {
        builder.append(&quot;.filter(\&quot;&quot;);
        builder.append(filterPredicate.getPropertyName());
        builder.append(&quot; &quot;);
        builder.append(filterPredicate.getOperator().toString());
        builder.append(&quot;\&quot;, \&quot;&quot;);
        builder.append(filterPredicate.getValue());
        builder.append(&quot;\&quot;)&quot;);
    }

    final List&lt;Query.SortPredicate&gt; sortPredicateList = query.getSortPredicates();
    for (Query.SortPredicate sortPredicate : sortPredicateList) {
        builder.append(&quot;.order(\&quot;&quot;);
        if (sortPredicate.getDirection() == Query.SortDirection.DESCENDING) {
            builder.append(&quot;-&quot;);
        }
        builder.append(sortPredicate.getPropertyName());
        builder.append(&quot;\&quot;)&quot;);
    }

    return builder.toString();
}
</pre>
<p>I had to include PreparedQueryImpl.java (unmodified) from the GAE distribution to get this to weave properly. AspectJ will actually let you weave a compiled library, but I could not get this to work with GAE. I suspect this has to do with the fact that portions of GAE don&#8217;t have source available, but since the workaround was so easy decided not to dig any further. The next step is to provide an easy way for a user to inject the aspect&#8217;s Logger dependency. This is pretty easy to do with Guice:</p>
<pre class="brush: java; title: ; notranslate">
public class QueryLoggerModule extends AbstractModule {
    @Override
    protected void configure() {
        requestInjection(Aspects.aspectOf(QueryLogger.class));
    }
}
</pre>
<p>Once compiled into a library, using it in an application is very simple: just install QueryLoggerModule into your application&#8217;s Guice module and any queries issued by your application that go through the Query interface will get logged at execution time.</p>
<pre class="brush: java; light: true; title: ; notranslate">
install(new QueryLoggerModule());
</pre>
<p>For example, the following query:</p>
<pre class="brush: java; light: true; title: ; notranslate">
ofy().query(Sprocket.class).filter(&quot;name&quot;, &quot;Foobar&quot;).order(&quot;-name&quot;);
</pre>
<p>Produces the following output in the log:</p>
<pre class="brush: plain; light: true; title: ; notranslate">
INFO: query(Sprocket.class).filter(&quot;name =&quot;, &quot;Foobar&quot;).order(&quot;-name&quot;)
</pre>
<p>I&#8217;ve posted the implementation along with a simple demo on <a title="GitHub" href="https://github.com/ArcBees/gae-studio">GitHub</a>. I plan to refine it when I have more time, but it&#8217;s already coming in handy. Some things I&#8217;d like to do, time permitting:</p>
<ul>
<li>Add GQL support (currently only the Query interface is supported)</li>
<li>Add features to help me see where things are being done inefficiently, such as detecting N+1 queries automatically</li>
<li>Switch the build to Maven</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://ivern.org/2012/01/logging-google-app-engine-queries/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss><!-- Dynamic page generated in 0.161 seconds. --><!-- Cached page generated by WP-Super-Cache on 2012-09-10 18:21:42 --><!-- Compression = gzip -->
