The original post is published on the JUST EAT tech blog at the following URL http://tech.just-eat.com/2017/01/18/a-better-local-and-remote-logging-on-ios-with-justlog/
In this blog post we introduce the solution for local and remote logging we developed for the Just Eat iOS app. It’s named JustLog and it’s available open source on Github at https://github.com/justeat/JustLog.
At Just Eat, logging and monitoring are fundamental parts of our job as engineers. Whether you are a back-end engineer or a front-end one, you’ll often find yourself in the situation where understanding how your software behaves in production is important, if not critical. The ELK stack for real-time logging has gained great adoption over recent years, mainly in the back-end world where multiple microservices often interact with each other.
In the mobile world, the common approach to investigating issues is gathering logs from devices or trying to reproduce the issue by following a sequence of reported steps. Mobile developers are mostly familiar with tools such as Google Analytics or Fabric.io but they are tracking systems, not fully fledged logging solutions.
We believe tracking is different in nature from logging and that mobile apps should take advantage of ELK too in order to take their monitoring and analysis to another level. Remote logging the right set of information could provide valuable information that would be difficult to gather otherwise, unveil unexpected behaviours and bugs, and even if the data was properly anonymized, identify the sequences of actions of singular users.
JustLog takes logging on iOS to the next level. It supports console, file and remote Logstash logging via TCP socket out of the box. You can also setup JustLog to use logz.io with no effort. JustLog relies on CocoaAsyncSocket and SwiftyBeaver, exposes a simple swifty API but it also plays just fine with Objective-C.
JustLog sets the focus on remote logging, but fully covers the basic needs of local console and file logging.
JustLog, is available through CocoaPods. To install it, simply add the following line to your Podfile:
Import it into your files like so:
// swift import JustLog // Objective-C @import JustLog;
This logging system strongly relies on SwiftyBeaver. We decided to adopt SwiftyBeaver due to the following reasons:
- good and extensible design
- ability to upload logs to the cloud
- macOS app to analyze logs
A log can be of one of 5 different types, to be used according to the specific need. A reasonable adopted convention on mobile could be the following:
- 📣 verbose: Use to trace the code, trying to find one part of a function specifically, sort of debugging with extensive information.
- 📝 debug: Information that is helpful to developers to diagnose an issue.
- ℹ️ info: Generally useful information to log (service start/stop, configuration assumptions, etc). Info to always have available but usually don’t care about under normal circumstances. Out-of-the-box config level.
- ⚠️ warning: Anything that can potentially cause application oddities but an automatic recovery is possible (such as retrying an operation, missing data, etc.)
- ☠️ error: Any error which is fatal to the operation, but not the service or application (can’t open a required file, missing data, etc.). These errors will force user intervention. These are usually reserved for failed API calls, missing services, etc.
When using JustLog, the only object to interact with is the shared instance of the
Logger class, which supports 3 destinations:
- sync writing to Console (custom destination)
- sync writing to File (custom destination)
- async sending logs to Logstash (usually part of an ELK stack)
Following is a code sample to configure and setup the Logger. It should be done at app startup time, in the
applicationDidFinishLaunchingWithOptions method in the AppDelegate.
let logger = Logger.shared // file destination logger.logFilename = "justeat-demo.log" // logstash destination logger.logstashHost = "my.logstash.endpoint.com" logger.logstashPort = 3515 logger.logstashTimeout = 5 logger.logLogstashSocketActivity = true // default info logger.defaultUserInfo = ["app": "my iOS App", "environment": "production", "tenant": "UK", "sessionID": someSessionID] logger.setup()
defaultUserInfo dictionary contains a set of basic information to add to every log.
The Logger class exposes 5 functions for the different types of logs. The only required parameter is the message, optional error and userInfo can be provided. Here are some examples of sending logs to JustLog:
Logger.shared.verbose("not so important") Logger.shared.debug("something to debug") Logger.shared.info("a nice information", userInfo: ["some key": "some extra info"]) Logger.shared.warning("oh no, that won’t be good", userInfo: ["some key": "some extra info"]) Logger.shared.error("ouch, an error did occur!", error: someError, userInfo: ["some key": "some extra info"])
It plays nicely with Objective-C too:
Read on →
[Logger.shared debug_objc:@"some message"]; [Logger.shared info_objc:@"some message" userInfo:someUserInfo]; [Logger.shared error_objc:@"some message" error:someError]; [Logger.shared error_objc:@"some message" error:someError userInfo:someUserInfo];