In this post I’d like to go even
further with JavaScript error logging
and tell you how we – at Namshi –
are trying to ease frontend debugging
across multiple browsers.
User agents, the gotchas
With JavaScript, it’s pretty easy to detect
the user agent from a client ( you just need
to access navigator.userAgent), the problem
is that user agents are one of the most incredible
gotchas in web development.
For example, how would you guess that
123
Mozilla/5.0
(Windows; U; Windows NT 6.1; WOW64; en-US; rv:2.0.4)
Gecko/20120718 AskTbAVR-IDW/3.12.5.17700 Firefox/14.0.1
represents Firefox 14 on Windows 7?
Let me tell you, you wouldn’t, that’s why
you should convert user agents in a human-readable
format for the people who are going to debug
the frontend.
Converting user agents with remote calls
Luckily, UserAgentString is a service which allows
you to query them whenever you need to retrieve useful
and meaningful informations from a user agent string;
combined with Guzzle, you can directly have meaningful
JavaScript errors’ reports with a few lines of code.
For example, this is a simple class which retrieves the informations from
the service:
<?phpnamespaceVendor\Service;useGuzzle\Http\ClientInterface;classUserAgentConverter{constURL_USERAGENT_API='http://www.useragentstring.com/';constBROWSER_INFO='%s %s on %s';protected$client;/** * Instantiates the service and injects the HTTP client that will be used * to perform requests. * * @param ClientInterface $client */publicfunction__construct(ClientInterface$client){$this->client=$client;}/** * Retrieves a human-readable string identifying the $userAgent for error * reporting (ie Internet Explorer 8 on Windows 7). * * @param string $userAgent * @return string|null */publicfunctionlookup($userAgent){$request=$this->client->post(self::URL_USERAGENT_API,null,sprintf('uas=%s&getJSON=all',$userAgent));$response=$request->send();if($response->getStatusCode()===200){returnjson_decode($response->getBody(true),true);}returnnull;}}
and you can use it like this:
123456789101112131415
<?php$ua='Mozilla/5.0 (Windows; U; Windows NT 6.1; WOW64; en-US; rv:2.0.4) Gecko/20120718 AskTbAVR-IDW/3.12.5.17700 Firefox/14.0.1';$uaService=newVendor\Service\UserAgentConverter(newGuzzle\Http\Client());$userAgent=$uaService->lookup($ua);if($userAgent){// outputs "Firefox 14.0.1 on Windows 7"echosprintf("%s %d on %s",$userAgent['agent_name'],$userAgent['agent_version'],$userAgent['os_name']);}
At this point it becomes obvious that you should put
a caching layer in front of the UserAgentConverter
since you don’t want to always query a remote service
to retrieve informations that you already have:
something like Redis should perfectly do the job,
as a cache – in this scenario – is essential, needs
to be as fast as hell and you don’t need a SLA with it,
so if the Redis server is down you are gracefully
degradating: at the same time, Memcache can be a good candidate
to substitute Redis, but remember that you will
renounce to persistence, since you won’t be able to
store informations on the disk as you would
do with Redis.
The implementations is very trivial:
Adding a caching layer to our code
12345678910111213141516171819202122
<?php$ua='Mozilla/5.0 (Windows; U; Windows NT 6.1; WOW64; en-US; rv:2.0.4) Gecko/20120718 AskTbAVR-IDW/3.12.5.17700 Firefox/14.0.1';$cache=newCacheProvider();$userAgent=$cache->lookup($ua)if(!$userAgent){$uaService=newVendor\Service\UserAgentConverter(newGuzzle\Http\Client());$userAgent=$uaService->lookup($ua);}if($userAgent){$cache->store($ua,$userAgent);// outputs "Firefox 14.0.1 on Windows 7"echosprintf("%s %d on %s",$userAgent['agent_name'],$userAgent['agent_version'],$userAgent['os_name']);}