Thursday, 28 August 2008

Logging in Flex/Air: Filters behaving badly...

I'm a firm believer in diagnostics - in .NET land I just can't live without log4net. The mx.logging.* namespace in Flex 3 appears to give us some similar functionality (I'm talking about restricting loggers to certain "categories" here) but without the lovely configuration framework of log4net.

In an app we're writing at the moment, I was seeing some wierd behaviour. Basically, the logging targets were just not obeying their filters, meaning every message was getting written everywhere. Not good. At all.

So I did some digging and found two things that surprised me:

1. Setting a level on a target will call Log.addTarget() - probably prematurely


When stepping through the source code of the Flex logging framework, I found this bit of code in AbstractTarget:

public function set level(value:int):void
{
// A change of level may impact
// the target level for Log.
Log.removeTarget(this);
_level = value;
Log.addTarget(this);
}

This highlighted line is the culprit. The contents of the addTarget method set up logging restrictions based on the value of the filters array. This has two consequences. Firstly, if you follow Adobe's guidelines you'll end up running through the addTarget code twice, once when you set the level, and once when you actually call Log.addTarget yourself - not awful, but not great either - lots of loops, which gets bad when you've got a lot of Loggers.

The other consequence was the cause of my pain...

2. You have to set your filters before you set your level


Yep. That's right, the following code is bad:
//BAD CODE, DON'T USE
var target : TraceTarget = new TraceTarget();
target.level = level; //This should not come first.
target.filters = filters; //This is pretty much ignored

Here's why:
  1. The default value of target.filters is ["*"], which will means the target listens to log messages from every logger.
  2. When you set target.level, Log.addTarget is called, and all the filter magic happens. Except that you haven't set the filters yet. So it uses the default value ["*"], and your filter listens to everything. This was what was happening in our code.
So, the correct code should be:
var target : TraceTarget = new TraceTarget();
target.filters = [com.mydomain.myproject.MyClass,mx.rpc.*]; //Set first
target.level = level; // set second.

In this example, a TraceTarget is created which will only listen to Loggers that have a category that is equal to "com.mydomain.myproject.MyClass" or begins with "mx.rpc". It is not at all obvious, but you the above code will also start Logging, as setting target.level also calls Log.addTarget().

The observant among you may have noticed that if Adobe improve the behaviour of setting target.level in the future, you may find that your logging stops because addTarget never gets called. If you're worried about that, you can do the following:
var target : TraceTarget = new TraceTarget();
target.filters = [com.mydomain.myproject.MyClass,mx.rpc.*];
Log.addTarget(target); //honours the filters
// setting target.level will
// remove the target, then add it again.
target.level = level;
Still inefficient, but at least it is future proof. I hope that helps somebody, somewhere...

Friday, 25 July 2008

Ballmer gets that search thing wrong...

Microsoft are rather obviously obsessed with getting their search revenues up. That's no surprise - pretty much everyone knows about their [twice] failed Yahoo bid. Then there is their cashback offer, and now the announcement of a plan to provide search facilities (and therefore advertising revenue) on Facebook. It makes sense. Search is big business.

It seems, however, that Steve Ballmer has forgotten why people actually use search engines. Apparently he thinks that:

"advertisers don’t want to sell on Live Search unless there’s more people using the site, and people don’t want to search on the site unless there are more relevant ads."
Source: CNN
Oh dear.

Surely Ballmer cannot believe that Google's near domination of the global search market was down to relevant ads? Can he? That would make him slightly crazy...

Tuesday, 15 July 2008

OutOfMemoryError while using mxmlc and ant

My first blog post. Very practical too...

Basically, I'm working on a fancy Adobe AIR app right now. As part of that, we are implementing an automated build process using CruiseControl and such. I'm using ant, and the flexTasks provided by Adobe. All was going pretty well, until this happened:
[mxmlc] Loading configuration file C:\Program Files\Adobe\Flex3SDK\frameworks\air-config.xml
[mxmlc] Error: null
[mxmlc]
[mxmlc] java.lang.OutOfMemoryError

After a bit of googling, there was no obvious solution. I did see mentions of setting the ANT_OPTS environment variable. So this is what I did (I'm running Windows Server 2003 btw...):

  1. Hit Windows-Break to open the "System Properties" dialog.
  2. Go to the advanced tab.
  3. Click "environment variables"
  4. In the System variables section, click New.
  5. Enter the variable name as ANT_OPTS, and the value as -Xmx512m.
  6. Click "OK".
If you were using a command prompt, you'll need to close it down and re-open it to use the new settings. Try running your ant script again, and your error should be gone.

You can find a much nicer visualisation of that process here. If you are on a UNIX box, then you'll need to:
set ANT_OPTS=-Xmx512M; export ANT_OPTS
from the shell.

If you want to know more, you should read about the Java virtual machine's memory tuning options. I couldn't find any documentation about the ANT_OPTS environment variable.