CocoaPods trunk is moving to be read-only. Read more on the blog, there are 19 months to go.

RSLogger 1.6.5

RSLogger 1.6.5

TestsTested
LangLanguage Obj-CObjective C
License Custom
ReleasedLast Release Feb 2018

Maintained by Georgiy Malyukov.



RSLogger 1.6.5

Simple iOS managed logging library in Objective-C.

Adding RSLogger to your project

Source files

Alternatively you can directly add all library's source files to your project.

  1. Download the latest code version or add the repository as a git submodule to your git-tracked project.
  2. Open your project in Xcode, then drag and drop all source files onto your project (use the "Product Navigator view"). Make sure to select Copy items when asked if you extracted the code archive outside of your project.
  3. Include RSLogger library wherever you need it with #import "RSLogger.h".

Quick Start

Let's imagine you're developing client-server application. You want to log network activity, cache management and app events, for example. We will create necessary loggers, then log some messages and then flush several messages groups to physical file.

// initialize loggers
RSLog *logNetwork = [[RSLog alloc] initWithGroup:@"network"];
RSLog *logCache = [[RSLog alloc] initWithGroup:@"cache"];

// attach them to the singleton logging hub
[[RSLogHub instance] addLog:logNetwork];
[[RSLogHub instance] addLog:logCache];

// ...

// log some messages, for example, after app's start
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    RSLOGFUNC; // log current method
    RSLOG(@"app started"); // each NSObject descendant obtains its own logger automatically
    [logNetwork log:@"network activity: %d", self.isNetworkActive];
    [logCache log:@"cache system activated."];
    // ...
}

// then flush your messages into file, for example, when your app is going to background
- (void)applicationDidEnterBackground:(UIApplication *)application {
    RSLOGFUNC;
    // what if we want to store network/cache and app's logs in different files?
    // no problem - use two different recorders:
    RSLogRecorder *recNetwork = [[RSLogRecorder alloc] initWithName:@"networkLogs"
                                                    recordingGroups:@[@"network", @"cache"]];
    // another one for your app delegate
    // logger injected by default has group name which equals to containing class name
    NSString *groupApp = NSStringFromClass([UIApplication sharedApplication].delegate.class);
    RSLogRecorder *recApp = [[RSLogRecorder alloc] initWithName:@"appLogs"
                                                recordingGroups:@[groupApp]];               
    // then flush to files
    [recNetwork flush];
    [recApp flush];
}

That's it! Check your device documents directory for the following files:

- networkLogs.log
- appLogs.log

More Detailed Explanation

1. Messages Logging

All your classes where RSLogger.h is imported will automatically obtain their own logger. You can access this logger at any time by calling the property declared as

@property (readonly, nonatomic) RSLog *logger;

You are free to use this RSLog class instance for logging your messages. Also for your convenience there are RSLOG and RSLOGT macroses (described below).

By default this logger will have name equals to class name. You can change this by overriding property in your class like this:

- (NSString *)loggerGroupName {
    return @"MyCustomLoggerGroup";
}

2. Recording Logged Messages

All loggers exist only while your application is alive, because they stored just in device's operating memory. If you want to store your logs in physical files, you can use class called RSLogRecorder.

This class can record specific logging groups and store them into physical file. RSLogRecorder object can be instantiated whenever you need by using several initializers:

- (instancetype)initWithName:(NSString *)name
             recordingGroups:(NSArray<NSString *> *)loggerGroups;
- (instancetype)initWithName:(NSString *)name
             recordingGroups:(NSArray<NSString *> *)loggerGroups
                    filePath:(NSString * _Nullable)path;
- (instancetype)initWithName:(NSString *)name
             recordingGroups:(NSArray<NSString *> *)loggerGroups
                   uploadURL:(NSString * _Nullable)url;

The difference is only in specifying target physical file's path. If you will not specify it then default file path will be used:

/<DOCUMENTS_DIR>/<RSLogRecorder_Instance_Name>.log

After instantiating RSLogRecorder you can use single method to overwrite target file and store all messages:

- (void)flush;

This method executes in a separated thread so you should not worry about performance issues.

3. Output Messages Analysis

Also you can access RSLogHub singleton directly for acquiring all logged messages from all objects in your application, or only loggers with specific group names, by using some properties and methods like these:

// contains all logged messages from all loggers inside your app
@property (readonly, nonatomic) NSArray<RSLogMessage *> *allMessages;

// returns only loggers with specified group names
- (NSArray<RSLog *> *)loggersWithGroups:(NSArray<NSString *> *)groups;

Please see well-documented header files and example project for detailed information.

4. Loggers Configuration

All RSLog class instances have specific property called configuration of type RSLogConfiguration. It contains configuration which uses by this concrete logger on incoming messages. This property is usually set to nil and uses defaultConfiguration property declared in RSLogHub class. Default rules are:

  1. Logging is enabled. All new incoming messages will be saved in memory.
  2. Incoming messages will be displayed in a system console in asynchronous queue.

If you want to use another configuration for all loggers at one moment - just reset defaultConfiguration property in RSLogHub class instance. Also you can configure any concrete logger by resetting its own configuration property with new logging settings.

Please note that if you reset configuration property for a concrete RSLog class instance then it will not be affected by RSLogHub global configuration even if you disable logging in it. If you want to remove configuration from concrete logger to use defaultConfiguration instead then just set configuration property for this logger to nil.

5. Uploading Logs

You can upload your logs as text/plain value inside HTTP request body by setting up uploadURL property and calling upload method. Your server must support plain text receiving inside HTTP body in this case.

Usage in Details

RSLogHub class

Centralized manager of all loggers registered in your application. Very convenient access point to receive partial or full information about all logged messages from loggers in all objects within your application. You can acquire all messages or all loggers used by your app at the moment, also you can access loggers or messages with specific groups only to filter your output results. See RSLogHub header file to get detailed information.

RSLog Class

RSLogger library contains main class that encapsulates all logging methods called RSLog. It is completely documented with Apple HeaderDoc notation and is very easy to use.

At first please note that there is an RSLog category for base NSObject class (imports automatically with RSLogger.h header file). After importing RSLogger library all your classes receive readonly public property declared as

@property (readonly, nonatomic) RSLog *logger;

This property will be instantiated after its first call. RSLog class instance will have a group name equals to your own class name where this logger is injected.

Also this category contains two macroses for convenient logging text-only messages without formatting arguments:

RSLOGT(target, message, ...) // logs your message into target's logger
RSLOGF(message, ...) // logs your message into self logger with arguments
RSLOG(message) // calls RSLOGF macro with nil arguments
RSLOGFUNC // logs current function

Of course, you are free to create and instantiate your own RSLog class instances in addition to internal objects' loggers.

The RSLog class has two initializers declared as follows:

- (instancetype)init; // empty group name
- (instancetype)initWithGroup:(NSString * _Nullable)group;

All loggers can have they own logical group. For example, if you want to have a distinct loggers for your network, storage, database management classes and so on, then you can instantiate separate loggers for each of these modules with human-readable group names.

Default logging method can receive variable format arguments lists:

- (void)log:(NSString *)format, ...; // logs a message into logger

Usage example:

RSLOGT(myObject, @"Object updated.", nil); // equals to [myObject.logger log:@"Object updated."]
RSLOG(@"Models processed."); // equals to [self.logger log:@"Models processed."]

NSString *host = "127.0.0.1";
RSLog *netLogger = [[RSLog alloc] initWithGroup:@"network"];
[netLogger log:@"Connected to host: %@", host];

Output results will look like this:

[MyObjectClass] Object updated.
[MyRootClass] Models processed.
[network] Connected to host: 127.0.0.1

Please note that these methods will not do anything if logger's configuration isLoggingEnabled flag set to NO.

All logged messages can be accessed through the two public properties inside RSLog declared as

@property (readonly, nonatomic) NSArray<RXLogMessage *> *messages;
@property (readonly, nonatomic) NSString *messagesString;

Note that the messagesString property will return all messages joined by the new line character \n.

Joining Loggers

Let's imagine you want to join these loggers into a new one after some event happened. You can do it by calling this method:

+ (RSLog *)loggerByJoining:(NSArray<RSLog *> *)loggers group:(NSString * _Nullable)group;

This one will instantiate a new RSLog class instance with optional group value, and all messages from loggers taken with the first argument will be joined in a new one. Example (using the code above):

RSLog *commonLogger = [RSLog loggerByJoining:@[myObject.logger, self.logger, netLogger] group:@"common"];
[commonLogger log:@"New message 1"];
[commonLogger log:@"New message 2"];

All messages of incoming loggers are automatically sorted by date in ascending order. Output logs from commonLogger will look like this:

[MyObjectClass] Object updated.
[MyRootClass] Models processed.
[network] Connected to host: 127.0.0.1
[common] New message 1
[common] New message 2

Also you can inject any other list of logged messages right inside your logger by calling this method:

- (void)injectMessages:(NSArray<RSLogMessage *> *)messages;

These messages also will be automatically joined with your logger's messages and sorted by their dates in ascending order.

RSLogMessage Class

This class represents a single logging message information, such as messageUID with unique message ID, date of this message creation, message text and optional group name for more deep logical grouping (for example, each of your loggers can have "error", "warning" and "notification" messages at the same time).

Each message can be displayed in a system console or sent to any other output stream by calling its NSObject's description property. Date of a message will be displayed automatically, including milliseconds and timezone.

RSLogConfiguration class

This class represents logging configuration that can be assigned to any concrete logger or the "global" RSLogHub defaultConfiguration property. Configuration class instance is immutable. You can configure properties with initializers. The properties are:

  1. isLoggingEnabled. Indicates whether this configuration allows to log any messages. If set to NO then no incoming messages will be saved while this configuration is in use.
  2. printMode. Determines how standard NSLog() should be called. Each logged message can be displayed in system console asynchronously (by default) and synchronously, also you can disable printing at all.

In most cases you may not use your own configurations, but if it's necessary please remember about several moments:

  1. RSLogHub class instance contains defaultConfiguration property. You can reset it with your own configuration. This configuration uses by default by the all loggers in your application, if they have no their own configurations setup.
  2. Each RSLog class instance can be configured with its own configuration property. By default it's set to nil and uses global defaultConfiguration instead. You can setup it with custom RSLogConfiguration class instance or set to nil again to reset settings to defaults.
  3. Global defaultConfiguration will NOT affect loggers that have been already configured with their own configurations.

License

Apache. See LICENSE for details.