• This repository has been archived on 05/Jan/2024
  • Stars
    star
    518
  • Rank 85,414 (Top 2 %)
  • Language
    Swift
  • License
    Apache License 2.0
  • Created almost 8 years ago
  • Updated 11 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

JustLog brings logging on iOS to the next level. It supports console, file and remote Logstash logging via TCP socket with no effort. Support for logz.io available.

JustLog Banner

JustLog

Build Status Version License Platform

JustLog takes logging on iOS to the next level. It supports console, file and remote Logstash logging via TCP socket with no effort. Support for logz.io available.

Overview

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 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.

Installation

CocoaPods

Add the following to your podfile:

pod "JustLog"

You can find the latest version on cocoapods.org

Swift Package Manager

Copy the URL for this repo, and add the package in your project settings.

Usage

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()

The 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:

[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];

Please note that metadata such as filename and line number are unavailable in Objective-C.

The message is the only required argument for each log type, while userInfo and error are optional. The Logger unifies the information from message, error, error.userInfo, userInfo, defaultUserInfo and call-site info/metadata in a single dictionary with the following schema form of type [String : Any] (we call this 'aggregated form'). E.g. in JSON representation:

{
  "message": "the log message",
  "user_info": {
    "app": "my iOS App",
    "environment": "production",
    "custom_key": "some custom value",
    ...
  },
  "errors": [
  {
    "error_domain" : "com.domain",
    "error_code" : "1234",
    "NSLocalizedDescription": ...,
    "NSLocalizedFailureReasonError": ...,
    ...
  },
  {
    "errorDomain" : "com.domain.inner",
    "errorCode" : "5678",
    "NSLocalizedDescription": ...,
    "NSLocalizedFailureReasonError": ...,
    ...
  }],  
  "metadata": {
    "file": ...,
    "function": ...,
    "line": ...,
    ...
  }
}

All destinations (console, file, logstash) are enabled by default but they can be disabled at configuration time like so:

logger.enableConsoleLogging = false
logger.enableFileLogging = false
logger.enableLogstashLogging = false

The above 5 logs are treated and showed differently on the each destination:

Console

The console prints only the message.

Console

File

On file we store all the log info in the 'aggregated form'.

2016-12-24 12:31:02.734  📣 VERBOSE: {"metadata":{"file":"ViewController.swift","app_version":"1.0 (1)","version":"10.1","function":"verbose()","device":"x86_64","line":"15"},"user_info":{"environment":"production","app":"my iOS App","log_type":"verbose","tenant":"UK"},"message":"not so important"}
2016-12-24 12:31:36.777  📝 DEBUG: {"metadata":{"file":"ViewController.swift","app_version":"1.0 (1)","version":"10.1","function":"debug()","device":"x86_64","line":"19"},"user_info":{"environment":"production","app":"my iOS App","log_type":"debug","tenant":"UK"},"message":"something to debug"}
2016-12-24 12:31:37.368  ℹ️ INFO: {"metadata":{"file":"ViewController.swift","app_version":"1.0 (1)","version":"10.1","function":"info()","device":"x86_64","line":"23"},"user_info":{"environment":"production","app":"my iOS App","log_type":"info","tenant":"UK","some key":"some extra info"},"message":"a nice information"}
2016-12-24 12:31:37.884  ⚠️ WARNING: {"metadata":{"file":"ViewController.swift","app_version":"1.0 (1)","version":"10.1","function":"warning()","device":"x86_64","line":"27"},"user_info":{"environment":"production","app":"my iOS App","log_type":"warning","tenant":"UK","some key":"some extra info"},"message":"oh no, that won’t be good"}
2016-12-24 12:31:38.475  ☠️ ERROR: {"metadata":{"file":"ViewController.swift","app_version":"1.0 (1)","version":"10.1","function":"error()","device":"x86_64","line":"47"},"user_info":{"environment":"production","log_type":"error","some key":"some extra info","app":"my iOS App","tenant":"UK","NSLocalizedFailureReason":"error value"},"errors":[{"error_code":1234,"error_domain":"com.just-eat.test","NSLocalizedDescription":"description","NSLocalizedRecoverySuggestion":"recovery suggestion"}],"message":"ouch, an error did occur!"}

Logstash

Before sending a log to Logstash, the 'aggregated form' is flattened to a simpler `[String : Any] dictionary, easily understood by Logstash and handy to be displayed on Kibana. E.g. in JSON representation:

{
  "message": "ouch, an error did occur!",

  "environment": "production",
  "log_type": "error",
  "version": "10.1",
  "app": "iOS UK app",
  "tenant": "UK",
  "app_version": "1.0 (1)",
  "device": "x86_64",

  "file": "ViewController.swift",
  "function": "error()",
  "line": "47",
  "errors": [{
    "error_domain": "com.just-eat.test",
    "error_code": "1234",
    "NSLocalizedDescription": "description",
    "NSLocalizedFailureReason": "error value"
  }]
}

Which would be shown in Kibana as follows:

Kibana

A note on Logstash destination

The logstash destination is configured via properties exposed by the Logger. E.g.:

let logger = Logger.shared
logger.logstashHost = "my.logstash.endpoint.com"
logger.logstashPort = 3515
logger.logstashTimeout = 5
logger.logLogstashSocketActivity = true

When the logLogstashSocketActivity is set to true, socket activity is printed to the console:

Socket Activity

This destination is the only asynchronous destination that comes with JustLog. This means that logs to Logstash are batched and sent at some point in future when the timer fires. The logstashTimeout property can be set to the number of seconds for the dispatch. In some cases, it might be important to dispatch the logs immediately after an event occurs like so:

Logger.shared.forceSend()

or, more generally, in the applicationDidEnterBackground and applicationWillTerminate methods in the AppDelegate like so:

func applicationDidEnterBackground(_ application: UIApplication) {
  forceSendLogs(application)
}

func applicationWillTerminate(_ application: UIApplication) {
  forceSendLogs(application)
}

private func forceSendLogs(_ application: UIApplication) {

  var identifier = UIBackgroundTaskIdentifier(rawValue: 0)

  identifier = application.beginBackgroundTask(expirationHandler: {
    application.endBackgroundTask(identifier)
    identifier = UIBackgroundTaskIdentifier.invalid
  })

  Logger.shared.forceSend { completionHandler in
    application.endBackgroundTask(identifier)
    identifier = UIBackgroundTaskIdentifier.invalid
  }
}

Sending logs to logz.io

JustLog supports sending logs to logz.io.

At the time of writing, logz.io uses the following host and port (please refer to the official documentation):

logger.logstashHost = "listener.logz.io"
logger.logstashPort = 5052

When configuring the Logger (before calling setup()), simply set the token like so:

logger.logzioToken = <logzio_token>

Custom

From 3.2.0 onward JustLog supports a custom destination that can be defined by you. To implement a custom logger, all you need to do is provide a type that conforms to the CustomDestinationSender protocol to the new overloaded setupWithCustomLogSender() method.

Don't forget to set the enableCustomLogging property to true before calling setupWithCustomLogSender.

class MyCustomDestinationSender: CustomDestinationSender {
    func log(_ string: String) {
        // send the log somewhere
    }
}

let customSender = MyCustomDestinationSender()
let logger = Logger.shared
logger.enableCustomLogging = true
logger.setupWithCustomLogSender(customSender)

Log Sanitization

JustLog supports the implementation of a sanitize method that can be set within the client. This method accepts two placeholder variables:

  • Message: This variable concerns the log message that you wish to sanitize.
  • Log Type: This variable concerns the log level applied to the given log message.
public var sanitize: (_ message: String, _ minimumLogType: LogType) -> String = { message, minimumLogType in

    return message
}

This closure method is set up and called within Logger.Swift. If this method not expanded upon within the client it will simply return the original message, as expected. An example of how we have adopted this sanitize method can be seen within AppDelegate.swift in which we redact certain values based upon an input list. Clicking on 'Sanitized Log Message' in the example app will provide an example of the santizer method in action.

Conclusion

JustLog aims to be an easy-to-use working solution with minimal setup. It covers the most basic logging needs (console and file logging) via the great foundations given by SwiftBeaver, but also provides an advanced remote logging solution for Logstash (which is usually paired with Elasticsearch and Kibana in an ELK stack). JustLog integrates with logz.io, one of the most widely used ELK SaaS, placing itself as the only solution in the market (at the time of writing) to leverage such stack on iOS.

We hope this library will ease the process of setting up the logging for your team and help you find solutions to the issues you didn't know you had.

  • Just Eat iOS team

More Repositories

1

ScrollingStackViewController

A view controller that uses root views of child view controllers as views in a UIStackView.
Swift
646
star
2

httpclient-interception

A .NET library for intercepting server-side HTTP requests
C#
246
star
3

JustTweak

JustTweak is a feature flagging framework for iOS apps.
Swift
203
star
4

JustPersist

JustPersist is the easiest and safest way to do persistence on iOS with Core Data support out of the box. It also allows you to migrate to any other persistence framework with minimal effort.
Swift
165
star
5

NavigationEngineDemo

A scalable and robust solution to navigation, deep linking and universal links on iOS 🚀
Swift
125
star
6

Shock

A HTTP mocking framework written in Swift.
Swift
101
star
7

JustPeek

JustPeek is an iOS Library that adds support for Force Touch-like Peek and Pop interactions on devices that do not natively support this kind of interaction.
Shell
68
star
8

Topshelf.Nancy

Nancy endpoint for the Topshelf service host providing additional support around URL reservations. — Edit
C#
65
star
9

ZendeskApiClient

C# Client for working with the Zendesk API
C#
64
star
10

JustEat.RecruitmentTest

The recruitment test to apply for an engineering role at Just Eat
Scala
62
star
11

AutomationTools

iOS UI testing framework and guidelines
Swift
52
star
12

NLog.StructuredLogging.Json

Structured logging for NLog using Json (formerly known as JsonFields)
C#
51
star
13

JustTrack

The Just Eat solution to better manage the analytics tracking and improve the relationship with your BI team.
Swift
42
star
14

JustFakeIt

HTTP server faking library
C#
31
star
15

jubako

A small API to help display rich content in a RecyclerView such as a wall of carousels
Kotlin
30
star
16

JustSaying

A light-weight message bus on top of AWS services (SNS and SQS).
C#
30
star
17

JustBehave

A BDD-influenced C# testing library cooked up by Just Eat
C#
29
star
18

PRAssigner

Swift AWS Lambda to automatically assign engineers to pull requests with a Slack integration
Swift
29
star
19

ApplePayJSSample

A sample implementation of Apple Pay JS using ASP.NET Core
C#
23
star
20

kongverge

A desired state configuration tool for Kong
C#
23
star
21

ProcessManager

C#
21
star
22

Android.Samples.Deeplinks

Sample app demonstrating an effective and unit-testable way to handle deep links in Android
Java
20
star
23

JustEat.StatsD

Our library for publishing metrics to statsd
C#
19
star
24

iOS.ErrorUtilities

Just Eat collection of iOS error-related utilities
Swift
19
star
25

JustSupport

JavaScript
18
star
26

AwsWatchman

Because unmonitored infrastructure will bite you
C#
17
star
27

fozzie-components

JavaScript
13
star
28

kubernetes-windows-aws-ovs

Kubernetes on windows in aws using ovn
HCL
13
star
29

applepayjs-polyfill

A polyfill for the Apple Pay JS for use in non-supported browsers
JavaScript
12
star
30

fozzie

Web UI Base Library
SCSS
12
star
31

LocalDynamoDb

C#
11
star
32

JE.IdentityServer.Security

Recaptcha for OpenIdConnect
C#
7
star
33

JustEat.Recruitment.UI

UI Test for interview candidates
7
star
34

OpenRastaSwagger

Swagger / Swagger-UI implementation for OpenRasta
JavaScript
7
star
35

ts-jsonschema-builder

Fluent TypeScript JSON Schema builder.
TypeScript
6
star
36

JustEat.InfoSecRecruitmentTest

C#
4
star
37

gulp-build-fozzie

Gulp build tasks for use across Fozzie modules
JavaScript
4
star
38

JustEat.CWA.RecruitmentTest

Consumer Web Applications recruitment test for roles at JUST EAT
4
star
39

AngularJSWebAPI.Experiment

HTML
3
star
40

JE.EmbeddedChecks

Micro-framework for embedding checks inside applications, so they tell you when they're healthy and how.
C#
3
star
41

JE.TurningPages

C#
3
star
42

JE.ApiValidation

API validation helpers - A standard error response contract and FluentValidation support in .NET web frameworks
C#
3
star
43

JE.ApiExceptions

C#
2
star
44

f-footer

Common footer component for Just Eat websites
SCSS
2
star
45

JustEat.EntryLevel.RecruitmentTest

Recruitment Test for Entry Level Engineers
2
star
46

global-component-library

Global Component Library and Documentation for sharing across UK and International
CSS
2
star
47

f-templates

Locate, compile, and serve HTML from templates.
JavaScript
1
star
48

f-dom

Fozzie JS DOM queries library.
JavaScript
1
star
49

f-toggle

Fozzie vanilla JS toggle library.
JavaScript
1
star
50

PowerShellDSCUtils

Just Eat PowerShell DSC Utilities
PowerShell
1
star
51

f-header

Common header component for Just Eat websites
JavaScript
1
star
52

f-recruit-message

Adds a recruitment message to the browser console
JavaScript
1
star
53

ruby_bootcamp

Code samples for ruby bootcamp show & tells
Ruby
1
star
54

f-serviceworker

JavaScript
1
star
55

mickeydb

Android sqlite db super tool, for migrations and data access code generation
Java
1
star
56

f-copy-assets

NPM package to copy assets from node_modules to a specified directory
JavaScript
1
star
57

JustEat.PublicApi.TestApp

JavaScript
1
star
58

browserslist-config-fozzie

Just Eat's Browserslist Config used in UI packages
JavaScript
1
star
59

openrasta-hosting-owin

OWIN host for OpenRasta
C#
1
star