Saturday, March 15, 2014

A Limerick Standup: AngularJS, PhantomJS, and Jasmine

Logs

In honor of St. Patty's Day, I decided a limerick standup would be appropriate:

I've made some good progress with Angular
Though nothing of mention particular
     I have some good tests
     That pass in PhantomJS
And if Jasmine were here, I would strangle 'er
I really appreciate comments so please feel free to comment on my posts. Whether you agree or disagree, I'd love to hear from you. Also, feel free to link back to your own blog in your comments. You can even subscribe to an RSS feed of the comments on this thread.

© 2008 — , D. Patrick Caldwell, President, Autopilot Consulting, LLC

Friday, February 7, 2014

Sending Java Stack Traces to Loggly

Logs

Recently at my new Autopilot contract, I was tasked with configuring some of our applications to send log entries to Loggly. We're using log4j, rsyslog, and syslog4j.

The Loggly service produces nice data which are easily accessible through a pleasant interface. The configuration was simple, logging works from anything that can log with syslog, and Loggly support was generally responsive (it definitely helps to have a corporate relationship).

Once we had the environment configured, we configured log4j following Loggly's Log4j Setup instructions. We were getting log messages into loggly in no time. The only problem was that we weren't getting stack traces.

This was a little bit confusing because the setup document reads:

You can send your Java logs using Log4j. The method shown supports multi-line events such as a Java stacktraces over Syslog.

I dug through the my usual channels looking for someone else who had the same experience. All I was able to find really was this one Stack Overflow question.

Not really being a Java guy, I assumed I was just missing something. In fact, the folks at Loggly told me I was missing something too. :) I first started looking into the rsyslog documentation regarding multi-line log entries (like stack traces). I noticed that the file input module supports a ReadMode attribute, but the udp input module does not.

I added a rolling file appender to my log4j.properties file, I configured the imfile in our rsyslog config file, and we were off to the races. Good looking easy to read stack traces. It's worth noting I'm using the paragraph ReadMode because not all lines in a Java stack trace are indented. Thus, I needed a full blank line between log entries. This is more or less our log4j.properties configuration for the rolling file appender:

  log4j.appender.ROLLING=org.apache.log4j.rolling.RollingFileAppender
  log4j.appender.ROLLING.File=/tmp/log4j.log
  log4j.appender.ROLLING.rollingPolicy=org.apache.log4j.rolling.TimeBasedRollingPolicy
  log4j.appender.ROLLING.rollingPolicy.FileNamePattern=/tmp/log4j.log.%d{yyyy-MM}.gz
  log4j.appender.ROLLING.layout=org.apache.log4j.PatternLayout
  log4j.appender.ROLLING.layout.ConversionPattern=%n%d{yyMMdd.HHmmss,SSS} %t %C{1}.%M %p: %m%n

This was well and good but I wanted to make sure I wasn't missing a simpler alternative. The docs said it would work via rsyslog udp and I wanted to know why I was having such trouble. I looked at the network traffic coming from my dev instances. I noticed that my messages didn't include any stack trace information at all. It wasn't just a multiline issue really. It was that stack traces were never being logged by syslog4j. I looked into the ConsoleAppender and found that it gets its append behavior from WriterAppender.subAppend. That method is how the console gets stack traces after the message has been written using the layout.

if(layout.ignoresThrowable()) {
    String[] s = event.getThrowableStrRep();
    if (s != null) {
        int len = s.length;
        for(int i = 0; i < len; i++) {
            this.qw.write(s[i]);
            this.qw.write(Layout.LINE_SEP);
        }
    }
}

I started looking at the Syslog4jAppenderSkeleton.append method. I noticed that this appender never logs stack traces at all.

protected void append(LoggingEvent event) {
 if (!this.initialized) {
  _initialize();
 }
  
 if (this.initialized) {
  int level = event.getLevel().getSyslogEquivalent();
   
  if (this.layout != null) {
   String message = this.layout.format(event);
   
   this.syslog.log(level,message);
   
  } else {
   String message = event.getRenderedMessage();
   
   this.syslog.log(level,message);
  }
 }
}

It was no wonder I wasn't getting stack traces into loggly. I looked back at the ConsoleAppender and wondered what the layout.ignoresThrowable was. I looked at the docs and found that the PatternLayout.ignoresThrowable method always returns true because it can't handle throwables. In fact, the same page reads:

A flexible layout configurable with pattern string. This code is known to have synchronization and other issues which are not present in org.apache.log4j.EnhancedPatternLayout. EnhancedPatternLayout should be used in preference to PatternLayout. EnhancedPatternLayout is distributed in the log4j extras companion.

Easily convinced, I switched to the EnhancedPatternLayout. I configured the ConsoleAppender to use the enhanced pattern layout and I added the %throwable configuration item to it. Viola! Stack traces . . . by my own doing. I copied the configuration to the Syslog4jAppender section, started the long deployment process, and watched in disappointment as I still had no stack traces.

I started messing with the layout on my Syslog4j appender and noticed nothing I did changed the resulting log message. That seemed a little odd, so I dug into that a little. I dug back into the Syslog4jAppenderSkeleton.append again. I set a breakpoint on this.layout.format(event); and attached the debugger. My breakpoint was never hit. The layout was always null!

I set out in search of where that field was getting set. We're using the log4j.properties file so our configuration is parsed by the PropertyConfigurator parseAppender method. This is a pretty big method but here's what I noticed:

if(appender.requiresLayout()) {
    Layout layout = (Layout) OptionConverter.instantiateByKey(props, layoutPrefix, Layout.class, null);
    if(layout != null) {
        appender.setLayout(layout);
        LogLog.debug("Parsing layout options for \"" + appenderName +"\".");
        //configureOptionHandler(layout, layoutPrefix + ".", props);
        PropertySetter.setProperties(layout, props, layoutPrefix + ".");
        LogLog.debug("End of parsing for \"" + appenderName +"\".");
    }
}

So again I looked at the implementation of Syslog4JAppenderSkeleton and found that requiresLayout always returns false!

public boolean requiresLayout() {
    return false;
}

This struck me as odd and it made me wonder how anyone ever was able to use the layout property with syslog4j. It turns out that DOMConfigurator.parseAppender ignores requiresLayout and sets the layout any time there's a layout tag in the log4j.xml configuration file.

// Set appender layout
else if (currentElement.getTagName().equals(LAYOUT_TAG)) {
    appender.setLayout(parseLayout(currentElement));
}

Having a fundamental opposition to switching to XML, I needed another way to get the Syslog4jAppender to respect my layout desires. To my chagrin, the best approach I could come up with was to subclass the Syslog4jAppender and override the requiresLayout method:

public class Syslog4jLayoutAppender extends Syslog4jAppender {
   @Override
   public boolean requiresLayout() {
      return true;
   }
}

I switched my configuration to use this appender, redeployed, and my stack traces started to appear in loggly. Unfortunately, they were all on one line with character codes in place of linefeeds. Preferring easy to read stack traces, I flipped the rolling file appender back on and considered this the best alternative. In short, here's how I get stack traces into loggly:

  1. Configure a RollingFileAppender
  2. Configure rsyslog to use the imfile module to monitor the file with paragraph read mode
  3. Profit
I really appreciate comments so please feel free to comment on my posts. Whether you agree or disagree, I'd love to hear from you. Also, feel free to link back to your own blog in your comments. You can even subscribe to an RSS feed of the comments on this thread.

© 2008 — , D. Patrick Caldwell, President, Autopilot Consulting, LLC

Saturday, January 25, 2014

How to Market a Mobile App: Knowledge is Power

Data!!

If you're going to understand how changes you make to your application affect your users or how effective your marketing strategy is, you're going to need data. All of the app markets provide decent data about downloads, active users, ad impressions, etc., but these are no match for deliberate data collection and usage statistics.

In the early days of This to That for iOS, we didn't include any analytics. We relied heavily on iTunesConnect to provide the statistics we needed to determine the effects of our changes. Google Play provides much better analytics out of the box including active installs, device statistics, OS versions, etc., so we had to make a lot more inferences.

For example, we could see daily downloads but we had no idea how many users we were retaining. At that point, we were releasing often so we could compare our download statistics to our upgrade statistics to get a feel for that. We further refined our estimates when we added Game Center support. Comparing the new user statistics to the Game Center names I didn't recognize (seriously), I estimated about half of the users use Game Center. Thus, we could infer that for every player who reported a score to the leaderboard, there was a roughly equivalent user without a Game Center account.

We also planned to be ad supported out of the gate, so we were able to get some usage information from ad impressions. Our app has a roughly 98% fill rate and filled ads cycle about every 3 minutes. Combining that information with estimated users per day, we could guess how much time people spent playing our game.

Several versions back, we started using Google Analytics to collect anonymous (we're not the NSA after all) usage statistics. Now we get a much more detailed view of our users and how they play This to That. I recommend developers consider analytics to be an inextricable part of any minimum viable product. Without analytics, there's no way I could write the How to Market a Mobile App series as I'd have no way of knowing how these experiments impact our application's performance in the app market.

Starting Point as of Version 4.2

  • 910 sessions lasting an average 3.5 minutes per month
  • 108 of these sessions are new users (about half are iOS 7)
  • About 80% of the sessions end on the first screen and last less than 10 seconds
  • About half of the users play at least 10 times per month
  • About 90% of users play This to That at least every other day

I think these statistics tell us a few meaningful things about This to That. The first and I think most obvious inference, which is the reason I'm writing this series (and is likely the reason you're reading it): people can't find and don't hear about This to That. To resolve this, we've added social features, game center, multiplayer gameplay, and have tried advertising. As of yet, to no avail.

Second, I think, is that many users never get past the first screen. I think that's likely because the look and feel of the default game theme may not be appealing (I'm not a designer after all). iOS 7 users are less likely to engage than iOS 6 users; it might be worth updating the default look and feel of the application for iOS 7 users.

I think there are some positive observations in these data too. Users who play the game tend to play it often (at least for a while). They tend to play several times a day for short periods. This gives me insight that the game should be quick to get into. Perhaps, for example, the game should start in the game screen and have a menu option for going to what's now the home screen.

Things to do in Version 5.0

  • Create an iOS 7 default theme
  • Clean up the default color scheme for all new users
  • Make a better app description for iTunes
  • Make more compelling graphics for the images in iTunes
  • Clean up the icon

I'm hoping we'll see a larger number of new users by having more compelling marketing in our This to That iTunes profile. I'm also hoping that the improved icon and cleaner default theme will make new users more likely to complete their first game (most users who complete the first game complete many subsequent games). I'm hoping having more users and retaining a higher percentage of them will also result in a higher likelihood that users will share the game with friends.

There was a slight drop in retention after we released the multiplayer version of This to That. I think that users were frustrated when they tried to connect to a multiplayer game, but nobody else was playing. That frustration overpowered the interest in playing the original single player version. When we released that version, we were hoping it would encourage users to invite friends and family to play. Ideally, the new design and higher retention rate would also result in more available online users for random game center match creation.

What's it Going to Take?

Well, one thing I like to do as an independent developer is work on the cheap. I think that I've managed, on my own, to make a fun and entertaining application; however, I admit it lacks a certain je ne sais quoi. Perhaps it lacks the polish of the kinds of games people make when they have designers on staff. Perhaps it feels unprofessional. Of all of the negative feedback I've ever gotten, I don't think anyone has said, "the game isn't fun." So, I'm going to have some artwork done professionally. The game, in its current state (with the extensive theming) would be difficult to overhaul, so I'm going to focus on 3 components: the icon, the screenshots in iTunes, and the default theme. I'm hoping to get all of this done for about $500 - $750. I have a few quotes in and I think this is a reasonable expectation.

I really appreciate comments so please feel free to comment on my posts. Whether you agree or disagree, I'd love to hear from you. Also, feel free to link back to your own blog in your comments. You can even subscribe to an RSS feed of the comments on this thread.

© 2008 — , D. Patrick Caldwell, President, Autopilot Consulting, LLC

Friday, January 24, 2014

How to Market a Mobile App: Where to Start

confused emoticon O.o

Mobile Magic Developers has several applications on various mobile marketplaces now. My 2 year old daughter loves Cow Says Moo. My mother in law has been playing This to That for years. Our friends get goodnatured laughs with Said Obama. All in all, developing mobile applications has been a really positive experience.

In graduate school marketing classes, we spent a lot of energy discussing myriad ways large organization with massive marketing budgets could broaden their customer bases. I'm sure that information is still somehow useful but it surely doesn't help independent developers like Mobile Magic Developers who don't have a marketing budget, let alone enough to hire an MBA marketing consultant. In fact, our marketing campaigns like the rest of our expenses, are still being paid out of our pockets.

So, where do you start on the quest to release a popular mobile application?

I Have No Clue O.o

I sincerely have no idea. I've read a lot of good blog posts about marketing mobile applications. I've purchase some really interesting books on the topic. My brother has a BA in marketing. I did get some marketing exposure in college. Despite all of this, I still have some really great apps nobody uses.

That's why I'm starting the How to Market a Mobile App series (well, that and I haven't blogged in almost 2 years and it's high time I get back to it). I'm going to start by introducing, with complete candor, where we are with This to That for iOS today and how we spent the last few years getting to what is effectively a good starting point.

I have a new plan for spreading the word about This to That with a limited budget over the next few months. I'll blog about every strategy I try and how each idea fails and succeeds (including all of the nitty-gritty and sometimes embarrassing details). Every time I add a new post, I'll include it in the index below so bookmark this page for a convenient starting point.

Worst Case Scenario? :*(

Well, as I've shared, I don't really know much about successfully marketing a mobile application, but I have learned a handful of things not to do. That tells me that the worst case scenario of writing this series both for me the writer and you the reader (and subscriber hopefully) is that together we learn a few more things not to do. But, who knows? Perhaps we'll figure out the right formula for getting our wonderful apps into the hands of the target audience who can get the most value out of them.

How to Successfully Market a Mobile Application

I really appreciate comments so please feel free to comment on my posts. Whether you agree or disagree, I'd love to hear from you. Also, feel free to link back to your own blog in your comments. You can even subscribe to an RSS feed of the comments on this thread.

© 2008 — , D. Patrick Caldwell, President, Autopilot Consulting, LLC

Wednesday, March 21, 2012

First Small Business, Big Difference for the NBCF a Success!

National Breast Cancer Foundation Banner

If you followed our Small Business, Big Difference program, you know that our first Small Business, Big Difference to support the National Breast Cancer Foundation ended Februrary 18th, 2012.

Thus, there are two pressing topics:

First, I'd like to apologize for taking this long to get these results out there. I know it's been a while, but I've been a little busy. On February 17th, 2012 my wife delivered our first child and we've been enjoying some family time.

Second, when we set out to start the Small Business, Big Difference program, we knew it'd take a few tries for it to catch on. We figured our first attempt would produce modest results; however, we were so excited that we were able to generate such interest in the program that we rounded our donation up to $100.00.

We'd like to thank everyone who donated or participated in the program and we'd like to thank the NBCF for letting us raise funds for them with the Small Business, Big Difference program. We're looking forward to working with them again in a future promotion.

I really appreciate comments so please feel free to comment on my posts. Whether you agree or disagree, I'd love to hear from you. Also, feel free to link back to your own blog in your comments. You can even subscribe to an RSS feed of the comments on this thread.

© 2008 — , D. Patrick Caldwell, President, Autopilot Consulting, LLC

Friday, February 10, 2012

JavaScript for Photoshop - When Macros Aren't Enough

This2That Tile Sample

A few years ago, a fellow named Brian Dorn got in touch with me. He was working on his doctoral dissertation at Georgia Tech and he needed participants. I met him in Atlanta and found out that his study was about scripting with Photoshop. That was the first I'd ever heard of Adobe Photoshop Scripting.

A few weeks ago when I started working on a new word game This2That for Mobile Magic Developers, I needed to generate 38 tiles with letters, numbers, and punctuation. The task was tedious and the macros just couldn't make it any easier. Then I realized I had the embossing all wrong and I had to start over!

Frustrated, I tried to think of a better way. I remembered Brian Dorn and I started looking into this Adobe Photoshop JavaScript thing. I was very pleased when 15 minutes of script became an easily reusable tile generating utility. Now, I can take any PSD, open it up, select any text layer, and have the script generate a PNG for each letter I need

I thought this little known feature would make for interesting reading for both programmers and designers so here's my script:

// call the method that does all of the work
main();

// wrap the code in a method to make it easier to debug
function main() {

 // make sure you're working in a document and have a text layer selected
 if (!activeDocument || !activeDocument.activeLayer || activeDocument.activeLayer.kind != LayerKind.TEXT)
 {
   alert("Please select a document and a target text layer.");
   return;
 }
 
 // set up some information about the current file
 var textLayer = activeDocument.activeLayer;
 var path = activeDocument.path;
 var fileName = activeDocument.name;
 
 // remove the extension on the file name
 var extensionPosition;
 if (extensionPosition = fileName.lastIndexOf('.'))
  fileName = fileName.substr(0, extensionPosition);

 // get a good place to put the file
 var outputFolder = Folder.selectDialog("Select a target folder.", path);
 
 // set up the letters we want images for
 var characterMap = [
  ["question", "?"]
 ];
 
 // and add the lowercase alphabet and numbers
 characterMap = characterMap.concat(getAsciiRange(97, 26), getAsciiRange(48, 10));

 // for each character, update the selected text layer and save a file
 for (var i = 0; i < characterMap.length; i++)
 {
   var character = characterMap[i][1];
   var fileSuffix = characterMap[i][0];
   
   textLayer.textItem.contents = character;
   
   var file = new File(outputFolder + "/" + fileName + "_" + fileSuffix + ".png");
   var options = new PNGSaveOptions();
   options.interlaced = false;

   activeDocument.saveAs(file, options, true, Extension.LOWERCASE);
 }
};

// a little helper method to make a range of letters and their
// filename extensions
function getAsciiRange(from, count) {
 var result = [];

 for (var i = 0; i < count; i++)
 {
  var character = String.fromCharCode(i + from);
  result.push([character, character.toUpperCase()]);
 } 
 
 return result;
}
 

I really appreciate comments so please feel free to comment on my posts. Whether you agree or disagree, I'd love to hear from you. Also, feel free to link back to your own blog in your comments. You can even subscribe to an RSS feed of the comments on this thread.

© 2008 — , D. Patrick Caldwell, President, Autopilot Consulting, LLC

Wednesday, February 8, 2012

Small Business, Big Difference - NBCF Approved

National Breast Cancer Foundation Banner

Last week I wrote a blog post introducing our Small Business, Big Difference program to raise money for the National Breast Cancer Foundation. This week, I got in touch with them and they gave us the go-ahead to run the fundraiser. We're very excited about the news!

So, we want to make sure you know how you can help! There are three ways that you can help us raise money to give to the NBCF.

Spread the Word


Get the App

  • Download the Jane Austen Quote of the Day Widget application.
  • Enjoy the application! We'll donate all ad revenue from the Jane Austen widget during the promotion period of Sunday, February 12th, 2012 and ending Saturday, February 18th, 2012.
  • If you love the app, buy the Jane Austen Widget Upgrade for less than a buck and we'll donate that too!

Want to Give More?

You're more than welcome to give as much as you want! You can visit the Mobile Magic Developer's Small Business, Big Difference for NBCF fundraising site powered by StayClassy.

If you have any questions at all, please feel free to contact us through our facebook page or twitter account. We'd love to address any concerns you may have.

I really appreciate comments so please feel free to comment on my posts. Whether you agree or disagree, I'd love to hear from you. Also, feel free to link back to your own blog in your comments. You can even subscribe to an RSS feed of the comments on this thread.

© 2008 — , D. Patrick Caldwell, President, Autopilot Consulting, LLC