• Stars
    star
    222
  • Rank 179,123 (Top 4 %)
  • Language
    Objective-C
  • License
    MIT License
  • Created about 11 years ago
  • Updated almost 11 years ago

Reviews

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

Repository Details

NSLog replacement for coders!

MTLog: an NSLog replacement for coders!

Logging is essential part of debugging and I was often irritated that NSLog is not as flexible as I'd like it to be. Therefore I came around writing MTLog - the flexible logging tool that I need.


Including MTLog in your project

Get it either as: 1) source files

  1. Download the MTLog repository as a zip file or clone it
  2. Copy the MTLog sub-folder into your Xcode project
  3. #import "MTLog.h" in the project .pch file to have MTLog working throughout all your classes

or 2) via Cocoa pods

  1. In your project's Podfile add the MTLog pod:
pod 'MTLog'
  1. From the Terminal, inside the project folder, run:
pod install
  1. #import "MTLog.h" in the project .pch file to have MTLog working throughout all your classes

If you want to read more about CocoaPods, have a look at this short tutorial.


Using MTLog

MTLog works by adding scripting abilities to your log messages. You use NSLog as normally but you can include certain commands or add your own commands that will be executed in the text of your log messages.

It's best if you just read through the examples below.

NB! Always make sure you've imported MTLog.h

Prefix command


By default fresh MTLog instances come with a default prefix for your messages. The default prefix includes the file name of the current class and the current line number (example output below):

NSLog(@"My log message!");
MyClass.m:20 > My log message!
_prefix:set(…)

If you want a different prefix than the default you can use the prefix command to change it:

NSLog(@"_prefix:set($method $line) My log message!");
NSLog(@"My second message.");
myMethod 20 > My log message!
myMethod 21 > My second message!

NB!: _prefix:set(…) sets the prefix for the current log message AND for all the messages you log afterwards.

prefix variables

Besides text you can also use variables in your prefix that will get replaced with their values for every log message. Here's the list of available variables:

$file The name of the current file
$class The current class name
$method The current method name
$line The current line number
$counter A counter starting from 1. It increases every time you log from the same line in the same file.
_prefix:set()

If you don't want a special prefix to your log messages, just call set with no arguments:

NSLog(@"_prefix:set()");
_prefix:use(…)

If you want to change the prefix for the current log message ONLY you use _prefix:use(…)

NSLog(@"My log message!");
NSLog(@"_prefix:use($method $line) My log message!");
NSLog(@"My second message.");
MyClass.m:20 > My log message!
myMethod 21 > My log message!
MyClass.m:22 > My second message!
_prefix:set(default)

If you change the prefix and want to go back to the default, pass the "default" constant to the set method of the prefix command:

NSLog(@"_prefix:set(default)");

Filter command


You can use the filter command to temporarily filter the log output, i.e. if you are debugging a certain method at the moment you don't want to see the output of all other log statements outside this method until you are finished.

You can use filter in several different ways.

_filter:MyClass.m

If you pass a file name to _filter after this log statement you will see only the output from this file.

YourClass.m
...
+(void)message
{
  NSLog(@"Your message!");
}

MyClass.m 
...
NSLog(@"_filter:MyClass.m");
NSLog(@"My message");
[YourClass message];
MyClass.m:20 > My message
// "Your message" will be filtered and not show up in the output console
_filter:MyClass.m(10,200)

You can filter the output by line number. Pass in a file name to _filter and as arguments provide the range of lines that should generate output to the console.

NSLog(@"_filter:MyClass.m(10,100)");

This command will allow only log statements from the lines between line 10 and line 100 to generate output to the console.

_filter:myMethod:withString:

You can also pass a method signature to the _filter command - then it will allow output only from the method matching this signature.

combined _filter commands

_filter commands you can stack up. I.e. you want to see only the output from the init method, though you have an init method in each of your classes. You can combine a filter by file name and a filter by method name.

NSLog(@"_filter:MyClass.m");
NSLog(@"_filter:init");

After this NSLog statements only logging from "init" in MyClass.m will generate output.

_filter:$this

Sometimes you want to see the output only of a certain line in your code and nothing more (for example if the NSLog statement is in a for loop).

NSLog(@"begin counting");
for (int i=1;i<=3;i++) {
  NSLog(@"_filter:$this i=%i", i);
  NSLog(@"more loop output");
}
NSLog(@"_filter:$this loop ended");
NSLog(@"last log message");

Once you use _filter:$this all log messages afterwards get filtered out. Except for the ones that also use _filter:$this. The output of the code above is:

MyClass.m:20 > begin counting
MyClass.m:22 > i=1
MyClass.m:22 > i=2
MyClass.m:22 > i=3
MyClass.m:25 > loop ended

Remove command


Since commands like filter affect all log messages afterwards you need a way to also deactivate them.

_remove:<command to remove>

Use "_remove:" followed by the exact command you used in first place. If we take the example from above and combine it with remove the code could look like this:

NSLog(@"_filter:$this Message 1");
NSLog(@"Message 2");
NSLog(@"_remove:_filter:$this");  //remove: + the command including arguments
NSLog(@"Message 3");
}

Since the remove command will remove the filter you will also see the last log in the output console.

MyClass.m:20 > Message 1
MyClass.m:23 > Message 3

In general all commands affect all logs after they get executed. Therefore you need to use _remove to deactivate them.

Some commands like _prefix:use(…) are one-shot commands therefore you don't need to use _remove for them.

Route command


The route command allows you to clone the output you see in the console to a log file. That's handy if you'd like to run the app many times and then analyze the log contents for example.

_route:file(log.txt)

The command creates a "log.txt" in your app's Documents folder and saves all the output (while the command is active) to this file.

_route:file(/<full path>/log.txt)

If you pass in a full blown path to a file, the command will create (or overwrite) it at the location you specify.

_route:file(log.txt,append)

If you pass the "append" constant as a second parameter your app will keep adding content to the file (instead of overwriting it at every run).

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  NSLog(@"message one");
  NSLog(@"message two");
  return YES;
}
--- 2013-09-14 08:36:21 +0000 ---- New log entry ----
[application:didFinishLaunchingWithOptions: 19] message one
[application:didFinishLaunchingWithOptions: 20] message two

--- 2013-09-14 08:42:01 +0000 ---- New log entry ----
[application:didFinishLaunchingWithOptions: 19] message one
[application:didFinishLaunchingWithOptions: 20] message two

Search command


Sometimes there's so much output in the console that you can't find the one line you really want to see. And further it's difficult to find where in the code is the line that produces that output and break there. The search command helps you spot certain log messages.

_search:clear(<search term>)

This command simply adds 20 empty lines before every line that contains the search term. It kind of "clears" the console when the search term appears so you can spot it easier.

NSLog(@"_search:clear(vacation)");
…
NSLog(@"Yupee!");
NSLog(@"I'm going on vacation!");
MyClass.m:20 > Yupee!

… //19 more empty lines
MyClass.m:22 > I'm going on vacation!
_search:throw(<search term>)

Whenever the search term appears in a log message the app throws an exception and catches it so the execution is not interrupted. If you are having a breakpoint for all exceptions Xcode will break inside the search plugin, so you can debug your code up the stack.

Register command


Now comes the best of all commands - the one that allows you to register new commands with MTLog. Have an idea for a command that will really help you while debuggin? Add it!

_register:command(CommandClassName)

You need to subclass MTLogPlugin and override some or all of the following methods (in the order of invocation):

// to tell MTLog how many arguments you are expecting
// for example return [0,2] - for zero, one or two args
+(NSRange)expectedNumberOfArgumentsForCommand:(NSString*)command;

// the custom plugin init- name contains the command, value is the part after the colon
// args is an array of the arguments (not trimmed of spaces)
-(instancetype)initWithName:(NSString*)name value:(NSString*)value args:(NSArray*)args;

// the method is invoked just before the plugin is added to enabled plugins list
-(void)willEnableForLog:(MTLog*)log;

// text contains the log message, you can alter it in any way and return it
// look up "env" below
-(NSString*)preProcessLogMessage:(NSString*)text env:(NSArray*)env;

// use this method to react to a command after the message is logged
// look up "env" below
-(void)postProcessLogMessage:(NSString*)text env:(NSArray*)env;

The "env" array contains as follows:

  1. File name
  2. Class name
  3. Method name
  4. Line number
  5. List of all enabled plugins
  6. List of all registered plugins

You can alter the enabled and registered plugins lists if you need to.

Implementing the new "smilie" command

Let's see the code for a new command called "smilie" that adds a smilie to each log message.

PluginSmilie.h

#import "MTLogPlugin.h"
@interface PluginSmilie : MTLogPlugin
@end

PluginSmilie.m

#import "PluginSmilie.h"
@implementation PluginSmilie
+(NSRange)expectedNumberOfArgumentsForCommand:(NSString*)command
{
  if ([command isEqualToString:@"extended"]) {
    return NSMakeRange(1, 1);
  }
  return NSMakeRange(0, 0);
}

-(NSString*)preProcessLogMessage:(NSString *)text env:(NSArray *)env
{
  if ([self.value isEqualToString:@"classic"]) {
    return [NSString stringWithFormat:@"%@ :-)", text];
  }
    
  if ([self.value isEqualToString:@"extended"]) {
    return [NSString stringWithFormat:@"%@ :%@)", text, self.args.firstObject];
  }
    
  return text;
}

@end

That's a complete, working command for MTLog.

In expectedNumberOfArgumentsForCommand: you set that you expect between 1 and 1 arguments for the "_smilie:extend" command, and no arguments for the "_smilie:classic" command.

Then in preProcessLogMessage:env: you add a smilie to the text argument, which alters the message being logged.

Let's see how you can use the new command in your code:

#import "PluginSmilie.h"
NSLog(@"_register:smilie(PluginSmilie)");
… 
NSLog(@"Message One");
NSLog(@"_smilie:classic Message Two");
NSLog(@"Message Three");

NSLog(@"_remove:_smilie:classic");
NSLog(@"Message Four");

NSLog(@"_smilie:extended(-{)");
NSLog(@"A hipster message");
MyClass.m:20 > Message One
MyClass.m:21 > Message Two :-)
MyClass.m:22 > Message Three :-)
MyClass.m:25 > Message Four
MyClass.m:28 > A hipster message :-{)

Misc

Author: Marin Todorov


License

This code is distributed under the terms and conditions of the MIT license.


Contribution guidelines

NB! If you are fixing a bug you discovered or adding a feature, please add also a unit test so I know how exactly to reproduce the bug before merging.

More Repositories

1

EasyAnimation

A Swift library to take the power of UIView.animateWithDuration(_:, animations:...) to a whole new level - layers, springs, chain-able animations and mixing view and layer animations together!
Swift
2,960
star
2

SwiftSpinner

A beautiful activity indicator and modal alert written in Swift (originally developed for my app DoodleDoodle) Using blur effects, translucency, flat and bold design - all iOS 8 latest and greatest
Swift
2,184
star
3

Timelane

Timelane
Swift
706
star
4

TaskQueue

A Task Queue Class developed in Swift (by Marin Todorov)
Swift
681
star
5

UIEffectDesignerView

A native Particle Systems effect view for iOS and OSX powered by QuartzCore
Objective-C
617
star
6

Retry

Haven't you wished for `try` to sometimes try a little harder? Meet `retry`
Swift
498
star
7

EventBlankApp

A free open source iOS app for events or conferences. Read more on the app's webpage:
Swift
291
star
8

Breadcrumbs

Bugtracker working off source code
Swift
193
star
9

TimelaneCombine

Timelane + Combine
Swift
168
star
10

RxTimelane

Timelane + RxSwift
Swift
132
star
11

PowerUpYourAnimations

Sample code from talks on advanced animations
Swift
118
star
12

DoNilDisturbPlugin

A plugin for your Xcode project that stops you from working outside work hours
Swift
109
star
13

timeui

Profile apps from the command line β€” duration, cpu & memory usage.
Swift
107
star
14

OneShotLocationManager

A replacement class for CLLocationManager for when you want to easily fetch the current device location
Swift
101
star
15

MTPopupWindow

Popup-window style view for Objective-C, which loads contents of an HTML file. Easy one-line usage. Check the readme for example
Objective-C
84
star
16

TimelaneCore

Timelane + Core
Swift
66
star
17

OperationTimelane

Timelane + Operations
Swift
45
star
18

RxSwiftoniOS

Sample code from my dotSwift 2017 talk in Paris
Swift
43
star
19

PackageView

An app that displays Package.swift info
Swift
42
star
20

RealmGitHubSearchRxDemo

The demo app for RxRealm's post on realm.io
Swift
36
star
21

RealmMultiplatformDemo

Demo that shares its model layer across Apple's four platforms
Swift
36
star
22

Advanced-RSS-reader

An example of an RSS reader app for iPhone, full source and comments
Objective-C
34
star
23

MTTestSemaphore

A class to help you create unit tests that test asynchronous methods. You will need this to unit test any class that fetch data from the network, use location, camera, etc.
Objective-C
33
star
24

Unxippity

Unxips quickly Xcode downloads
Swift
32
star
25

Cancellor

Bind multiple cancellables to the lifetime of another object like a view controller.
Swift
32
star
26

RealmNotificationExample

The project for the post demonstrating fine grained notifications on realm.io
Swift
30
star
27

MarkWalkthrough

A SwiftUI package to quickly build app walkthroughs
Swift
27
star
28

HUD

Beautiful alert message/ progress hud component for iOS Objective-C
Objective-C
25
star
29

SafariDownload

Swift package to read Safari's download packages
Swift
8
star
30

CustomInstrument

A custom Xcode instrument based on Timelane
Swift
7
star
31

Fetch-and-parse-JSON

Fetch and parse JSON
Objective-C
7
star
32

ActorBench

Actor vs queue vs lock benchmark
Swift
6
star
33

TimelaneInstrument

Timelane Tools Instrument
5
star
34

StylesDemoApp

A little experiment on styling SwiftUI views
Swift
4
star
35

powerups

cli for dynamic XML includes
Swift
3
star
36

nsspain2020

Demo projects from my talk at NSSpain 2020
Swift
3
star
37

Tracker

Google Analytics iOS shortcut wrapper
3
star
38

TimerApp

Timer App v1
Swift
3
star
39

LogRider

Logs viewer app for mac
Swift
3
star
40

snippetty.io

snippetty.io
HTML
3
star
41

HTTPKit

Task based, promise like syntax, RESTful, HTTP library for iOS and OS X. Built off ConcurrentKit and NSURLSession.
Objective-C
2
star
42

react-native-console-oslog

React Native package to log to Apple's unified log
Objective-C++
2
star
43

rx-marin

rx blog:
HTML
2
star
44

combinebook.com

HTML
2
star
45

ios-animations-by-emails

iOS Animations by Emails newsletter archive
HTML
2
star
46

snippety.io

snippetty.io
1
star
47

swiftconcurrencybook

swiftconcurrencybook.com
CSS
1
star
48

AnagramsGameiPad

The completed source code to "How to create an awesome Anagrams game with UIKit"
Objective-C
1
star
49

Baffle

Ruby
1
star
50

www.timelane.tools

www.timelane.tools
1
star
51

macro-bad-access

Swift
1
star
52

tryCombine

tryCombine blog by Marin Todorov
HTML
1
star
53

Languages

RWDevCon tutorial code
Swift
1
star