├── MTLogDemo ├── en.lproj │ └── InfoPlist.strings ├── Images.xcassets │ ├── AppIcon.appiconset │ │ ├── Icon-80.png │ │ ├── Icon-120.png │ │ ├── Icon-Small@2x.png │ │ └── Contents.json │ └── LaunchImage.launchimage │ │ ├── Default@2x.png │ │ ├── Default-568h@2x.png │ │ └── Contents.json ├── ViewController.h ├── AppDelegate.h ├── AppDelegate.m ├── main.m ├── MTLogDemo-Prefix.pch ├── MTLogDemo-Info.plist ├── ViewController.m └── Base.lproj │ └── Main.storyboard ├── MTLogDemoTests ├── en.lproj │ └── InfoPlist.strings ├── vendor │ └── JRSwizzle │ │ ├── JRSwizzle.h │ │ ├── README.markdown │ │ └── JRSwizzle.m ├── MTLogDemoTests-Info.plist ├── class │ ├── MTLogPluginSmilie.h │ ├── MTLogPluginOutputCatcher.h │ ├── MTLogFilterTestsHelper.h │ ├── MTLogFilterTestsHelper.m │ ├── MTLogPluginSmilie.m │ └── MTLogPluginOutputCatcher.m ├── Mocks │ ├── MTLog+Testing.h │ └── MTLog+Testing.m └── tests │ ├── MTLogSearchTests.m │ ├── MTLogRegisterTests.m │ ├── MTLogRouteTests.m │ ├── MTLogPrefixTests.m │ └── MTLogFilterTests.m ├── docs └── html │ ├── img │ ├── disclosure.png │ ├── disclosure_open.png │ ├── title_background.png │ ├── library_background.png │ └── button_bar_background.png │ ├── css │ ├── stylesPrint.css │ └── styles.css │ ├── hierarchy.html │ ├── index.html │ ├── Protocols │ └── MTLogPluginProtocol.html │ └── Classes │ ├── MTLog.html │ └── MTLogPlugin.html ├── gendoc ├── .gitignore ├── LICENSE ├── MTLog ├── plugins │ ├── MTLogPluginFilter.h │ ├── MTLogPluginRemove.h │ ├── MTLogPluginRegister.h │ ├── MTLogPluginRoute.h │ ├── MTLogPluginSearch.h │ ├── MTLogPluginPrefix.h │ ├── MTLogPluginRegister.m │ ├── MTLogPluginRemove.m │ ├── MTLogPluginSearch.m │ ├── MTLogPluginFilter.m │ ├── MTLogPluginPrefix.m │ └── MTLogPluginRoute.m ├── NSArray+FirstObject.h ├── NSArray+FirstObject.m ├── MTLogPlugin.m ├── MTLog.h ├── MTLogPlugin.h └── MTLog.m └── README.md /MTLogDemo/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /MTLogDemoTests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /docs/html/img/disclosure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icanzilb/MTLog/HEAD/docs/html/img/disclosure.png -------------------------------------------------------------------------------- /docs/html/img/disclosure_open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icanzilb/MTLog/HEAD/docs/html/img/disclosure_open.png -------------------------------------------------------------------------------- /docs/html/img/title_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icanzilb/MTLog/HEAD/docs/html/img/title_background.png -------------------------------------------------------------------------------- /docs/html/img/library_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icanzilb/MTLog/HEAD/docs/html/img/library_background.png -------------------------------------------------------------------------------- /docs/html/img/button_bar_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icanzilb/MTLog/HEAD/docs/html/img/button_bar_background.png -------------------------------------------------------------------------------- /MTLogDemo/Images.xcassets/AppIcon.appiconset/Icon-80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icanzilb/MTLog/HEAD/MTLogDemo/Images.xcassets/AppIcon.appiconset/Icon-80.png -------------------------------------------------------------------------------- /MTLogDemo/Images.xcassets/AppIcon.appiconset/Icon-120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icanzilb/MTLog/HEAD/MTLogDemo/Images.xcassets/AppIcon.appiconset/Icon-120.png -------------------------------------------------------------------------------- /MTLogDemo/Images.xcassets/AppIcon.appiconset/Icon-Small@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icanzilb/MTLog/HEAD/MTLogDemo/Images.xcassets/AppIcon.appiconset/Icon-Small@2x.png -------------------------------------------------------------------------------- /MTLogDemo/Images.xcassets/LaunchImage.launchimage/Default@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icanzilb/MTLog/HEAD/MTLogDemo/Images.xcassets/LaunchImage.launchimage/Default@2x.png -------------------------------------------------------------------------------- /MTLogDemo/Images.xcassets/LaunchImage.launchimage/Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icanzilb/MTLog/HEAD/MTLogDemo/Images.xcassets/LaunchImage.launchimage/Default-568h@2x.png -------------------------------------------------------------------------------- /gendoc: -------------------------------------------------------------------------------- 1 | appledoc --project-name "MTLog" --project-version "0.6" --project-company "Marin Todorov, Underplot" --company-id com.underplot --output docs --exclude-output "MTLog/plugins" --no-create-docset --no-repeat-first-par MTLog -------------------------------------------------------------------------------- /MTLogDemo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // MTLogDemo 4 | // 5 | // Created by Marin Todorov on 7/9/13. 6 | // Copyright (c) 2013 Underplot ltd. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UITableViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | .DS_Store 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | *.xcworkspace 13 | !default.xcworkspace 14 | xcuserdata 15 | profile 16 | *.moved-aside 17 | DerivedData 18 | .idea/ 19 | -------------------------------------------------------------------------------- /MTLogDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // MTLogDemo 4 | // 5 | // Created by Marin Todorov on 7/9/13. 6 | // Copyright (c) 2013 Underplot ltd. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /docs/html/css/stylesPrint.css: -------------------------------------------------------------------------------- 1 | 2 | header { 3 | display: none; 4 | } 5 | 6 | div.main-navigation, div.navigation-top { 7 | display: none; 8 | } 9 | 10 | div#overview_contents, div#contents.isShowingTOC, div#contents { 11 | overflow: visible; 12 | position: relative; 13 | top: 0px; 14 | border: none; 15 | left: 0; 16 | } 17 | #tocContainer.isShowingTOC { 18 | display: none; 19 | } 20 | nav { 21 | display: none; 22 | } -------------------------------------------------------------------------------- /MTLogDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // MTLogDemo 4 | // 5 | // Created by Marin Todorov on 7/9/13. 6 | // Copyright (c) 2013 Underplot ltd. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @implementation AppDelegate 12 | 13 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 14 | { 15 | return YES; 16 | } 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /MTLogDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // MTLogDemo 4 | // 5 | // Created by Marin Todorov on 7/9/13. 6 | // Copyright (c) 2013 Underplot ltd. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "AppDelegate.h" 12 | 13 | int main(int argc, char * argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /MTLogDemo/MTLogDemo-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #import 8 | 9 | #ifndef __IPHONE_5_0 10 | #warning "This project uses features only available in iOS SDK 5.0 and later." 11 | #endif 12 | 13 | #ifdef __OBJC__ 14 | #import 15 | #import 16 | 17 | #import "MTLog.h" 18 | #endif 19 | -------------------------------------------------------------------------------- /MTLogDemoTests/vendor/JRSwizzle/JRSwizzle.h: -------------------------------------------------------------------------------- 1 | // JRSwizzle.h semver:1.0 2 | // Copyright (c) 2007-2011 Jonathan 'Wolf' Rentzsch: http://rentzsch.com 3 | // Some rights reserved: http://opensource.org/licenses/MIT 4 | // https://github.com/rentzsch/jrswizzle 5 | 6 | #import 7 | 8 | @interface NSObject (JRSwizzle) 9 | 10 | + (BOOL)jr_swizzleMethod:(SEL)origSel_ withMethod:(SEL)altSel_ error:(NSError**)error_; 11 | + (BOOL)jr_swizzleClassMethod:(SEL)origSel_ withClassMethod:(SEL)altSel_ error:(NSError**)error_; 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /MTLogDemo/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "29x29", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-Small@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "40x40", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-80.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "60x60", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-120.png", 19 | "scale" : "2x" 20 | } 21 | ], 22 | "info" : { 23 | "version" : 1, 24 | "author" : "xcode" 25 | } 26 | } -------------------------------------------------------------------------------- /MTLogDemo/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "filename" : "Default@2x.png", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "extent" : "full-screen", 13 | "idiom" : "iphone", 14 | "subtype" : "retina4", 15 | "filename" : "Default-568h@2x.png", 16 | "minimum-system-version" : "7.0", 17 | "orientation" : "portrait", 18 | "scale" : "2x" 19 | } 20 | ], 21 | "info" : { 22 | "version" : 1, 23 | "author" : "xcode" 24 | } 25 | } -------------------------------------------------------------------------------- /MTLogDemoTests/MTLogDemoTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.underplot.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Marin Todorov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /MTLogDemo/MTLogDemo-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | MTLog Demo 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | com.underplot.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /MTLog/plugins/MTLogPluginFilter.h: -------------------------------------------------------------------------------- 1 | // 2 | // MTLogPluginFilter.h 3 | // 4 | // @author Marin Todorov, http://www.touch-code-magazine.com 5 | // 6 | 7 | // Copyright (c) 2013 Marin Todorov, Underplot ltd. 8 | // This code is distributed under the terms and conditions of the MIT license. 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 11 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | // 14 | // The MIT License in plain English: http://www.touch-code-magazine.com/JSONModel/MITLicense 15 | 16 | #import "MTLogPlugin.h" 17 | 18 | @interface MTLogPluginFilter : MTLogPlugin 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /MTLog/plugins/MTLogPluginRemove.h: -------------------------------------------------------------------------------- 1 | // 2 | // MTLogPluginRemove.h 3 | // 4 | // @author Marin Todorov, http://www.touch-code-magazine.com 5 | // 6 | 7 | // Copyright (c) 2013 Marin Todorov, Underplot ltd. 8 | // This code is distributed under the terms and conditions of the MIT license. 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 11 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | // 14 | // The MIT License in plain English: http://www.touch-code-magazine.com/JSONModel/MITLicense 15 | 16 | #import "MTLogPlugin.h" 17 | 18 | @interface MTLogPluginRemove : MTLogPlugin 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /MTLog/plugins/MTLogPluginRegister.h: -------------------------------------------------------------------------------- 1 | // 2 | // MTLogPluginRegister.h 3 | // 4 | // @author Marin Todorov, http://www.touch-code-magazine.com 5 | // 6 | 7 | // Copyright (c) 2013 Marin Todorov, Underplot ltd. 8 | // This code is distributed under the terms and conditions of the MIT license. 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 11 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | // 14 | // The MIT License in plain English: http://www.touch-code-magazine.com/JSONModel/MITLicense 15 | 16 | #import "MTLogPlugin.h" 17 | 18 | @interface MTLogPluginRegister : MTLogPlugin 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /MTLogDemoTests/class/MTLogPluginSmilie.h: -------------------------------------------------------------------------------- 1 | // 2 | // MTLogPluginSmilie.h 3 | // 4 | // @author Marin Todorov, http://www.touch-code-magazine.com 5 | // 6 | 7 | // Copyright (c) 2013 Marin Todorov, Underplot ltd. 8 | // This code is distributed under the terms and conditions of the MIT license. 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 11 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | // 14 | // The MIT License in plain English: http://www.touch-code-magazine.com/JSONModel/MITLicense 15 | 16 | #import "MTLogPlugin.h" 17 | 18 | @interface MTLogPluginSmilie : MTLogPlugin 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /MTLog/NSArray+FirstObject.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSArray+FirstObject.h 3 | // 4 | // @author Marin Todorov, http://www.touch-code-magazine.com 5 | // 6 | 7 | // via: http://troybrant.net/blog/2010/02/adding-firstobject-to-nsarray/ 8 | // This code is distributed under the terms and conditions of the MIT license. 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 11 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | // 14 | // The MIT License in plain English: http://www.touch-code-magazine.com/JSONModel/MITLicense 15 | 16 | #import 17 | 18 | @interface NSArray (FirstObject) 19 | 20 | - (id)firstObject; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /MTLog/plugins/MTLogPluginRoute.h: -------------------------------------------------------------------------------- 1 | // 2 | // MTLogPluginRoute.h 3 | // 4 | // @author Marin Todorov, http://www.touch-code-magazine.com 5 | // 6 | 7 | // Copyright (c) 2013 Marin Todorov, Underplot ltd. 8 | // This code is distributed under the terms and conditions of the MIT license. 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 11 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | // 14 | // The MIT License in plain English: http://www.touch-code-magazine.com/JSONModel/MITLicense 15 | 16 | #import "MTLogPlugin.h" 17 | 18 | @interface MTLogPluginRoute : MTLogPlugin 19 | 20 | @property (strong, nonatomic) NSString* logFilePath; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /MTLog/plugins/MTLogPluginSearch.h: -------------------------------------------------------------------------------- 1 | // 2 | // MTLogPluginSearch.h 3 | // 4 | // @author Marin Todorov, http://www.touch-code-magazine.com 5 | // 6 | 7 | // Copyright (c) 2013 Marin Todorov, Underplot ltd. 8 | // This code is distributed under the terms and conditions of the MIT license. 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 11 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | // 14 | // The MIT License in plain English: http://www.touch-code-magazine.com/JSONModel/MITLicense 15 | 16 | #import "MTLogPlugin.h" 17 | 18 | @interface MTLogPluginSearch : MTLogPlugin 19 | 20 | @property (assign, nonatomic) int resultsCount; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /MTLog/plugins/MTLogPluginPrefix.h: -------------------------------------------------------------------------------- 1 | // 2 | // MTLogPluginPrefix.h 3 | // 4 | // @author Marin Todorov, http://www.touch-code-magazine.com 5 | // 6 | 7 | // Copyright (c) 2013 Marin Todorov, Underplot ltd. 8 | // This code is distributed under the terms and conditions of the MIT license. 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 11 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | // 14 | // The MIT License in plain English: http://www.touch-code-magazine.com/JSONModel/MITLicense 15 | 16 | #import "MTLogPlugin.h" 17 | 18 | @interface MTLogPluginPrefix : MTLogPlugin 19 | 20 | @property (assign, nonatomic) BOOL shouldSkipCurrentMessage; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /MTLogDemoTests/Mocks/MTLog+Testing.h: -------------------------------------------------------------------------------- 1 | // 2 | // MTLog+Testing.h 3 | // 4 | // @author Marin Todorov, http://www.touch-code-magazine.com 5 | // 6 | 7 | // Copyright (c) 2013 Marin Todorov, Underplot ltd. 8 | // This code is distributed under the terms and conditions of the MIT license. 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 11 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | // 14 | // The MIT License in plain English: http://www.touch-code-magazine.com/JSONModel/MITLicense 15 | 16 | #import "MTLog.h" 17 | #import "MTLogPluginOutputCatcher.h" 18 | 19 | @interface MTLog (Testing) 20 | 21 | +(MTLogPluginOutputCatcher*)lastMessageCatcher; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /MTLogDemoTests/class/MTLogPluginOutputCatcher.h: -------------------------------------------------------------------------------- 1 | // 2 | // MTLogPluginOutputCatcher.h 3 | // 4 | // @author Marin Todorov, http://www.touch-code-magazine.com 5 | // 6 | 7 | // Copyright (c) 2013 Marin Todorov, Underplot ltd. 8 | // This code is distributed under the terms and conditions of the MIT license. 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 11 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | // 14 | // The MIT License in plain English: http://www.touch-code-magazine.com/JSONModel/MITLicense 15 | 16 | #import "MTLogPlugin.h" 17 | 18 | @interface MTLogPluginOutputCatcher : MTLogPlugin 19 | 20 | -(NSString*)logMessage; 21 | -(NSArray*)env; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /MTLogDemoTests/class/MTLogFilterTestsHelper.h: -------------------------------------------------------------------------------- 1 | // 2 | // MTLogFilterTestsHelper.h 3 | // 4 | // @author Marin Todorov, http://www.touch-code-magazine.com 5 | // 6 | 7 | // Copyright (c) 2013 Marin Todorov, Underplot ltd. 8 | // This code is distributed under the terms and conditions of the MIT license. 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 11 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | // 14 | // The MIT License in plain English: http://www.touch-code-magazine.com/JSONModel/MITLicense 15 | 16 | #import 17 | 18 | #import "MTLog+Testing.h" 19 | #import "MTLogPluginOutputCatcher.h" 20 | 21 | @interface MTLogFilterTestsHelper : NSObject 22 | 23 | -(void)log:(NSString*)message; 24 | -(void)log:(NSString*)message withNumber:(NSNumber*)nr; 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /MTLogDemoTests/class/MTLogFilterTestsHelper.m: -------------------------------------------------------------------------------- 1 | // 2 | // MTLogFilterTestsHelper.m 3 | // 4 | // @author Marin Todorov, http://www.touch-code-magazine.com 5 | // 6 | 7 | // Copyright (c) 2013 Marin Todorov, Underplot ltd. 8 | // This code is distributed under the terms and conditions of the MIT license. 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 11 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | // 14 | // The MIT License in plain English: http://www.touch-code-magazine.com/JSONModel/MITLicense 15 | 16 | #import "MTLogFilterTestsHelper.h" 17 | 18 | @implementation MTLogFilterTestsHelper 19 | 20 | -(void)log:(NSString*)message 21 | { 22 | NSLog(@"%@", message); 23 | } 24 | 25 | -(void)log:(NSString*)message withNumber:(NSNumber*)nr 26 | { 27 | NSLog(@"%@%@", message, nr); 28 | } 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /MTLogDemoTests/class/MTLogPluginSmilie.m: -------------------------------------------------------------------------------- 1 | // 2 | // MTLogPluginSmilie.m 3 | // 4 | // @author Marin Todorov, http://www.touch-code-magazine.com 5 | // 6 | 7 | // Copyright (c) 2013 Marin Todorov, Underplot ltd. 8 | // This code is distributed under the terms and conditions of the MIT license. 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 11 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | // 14 | // The MIT License in plain English: http://www.touch-code-magazine.com/JSONModel/MITLicense 15 | 16 | #import "MTLogPluginSmilie.h" 17 | 18 | @implementation MTLogPluginSmilie 19 | 20 | +(NSRange)expectedNumberOfArgumentsForCommand:(NSString*)command 21 | { 22 | return NSMakeRange(0, 0); 23 | } 24 | 25 | -(NSString*)preProcessLogMessage:(NSString *)text env:(NSArray *)env 26 | { 27 | return [NSString stringWithFormat:@":) %@", text]; 28 | } 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /MTLog/NSArray+FirstObject.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSArray+FirstObject.m 3 | // 4 | // @author Marin Todorov, http://www.touch-code-magazine.com 5 | // 6 | 7 | // via: http://troybrant.net/blog/2010/02/adding-firstobject-to-nsarray/ 8 | // This code is distributed under the terms and conditions of the MIT license. 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 11 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | // 14 | // The MIT License in plain English: http://www.touch-code-magazine.com/JSONModel/MITLicense 15 | 16 | #import "NSArray+FirstObject.h" 17 | 18 | /** 19 | * Mac OS X version of NSArray doesn't have firstObject 20 | * @return the first object in the array or nil 21 | */ 22 | @implementation NSArray (FirstObject) 23 | 24 | - (id)firstObject 25 | { 26 | if ([self count] > 0) { 27 | return [self objectAtIndex:0]; 28 | } 29 | return nil; 30 | } 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /MTLogDemoTests/class/MTLogPluginOutputCatcher.m: -------------------------------------------------------------------------------- 1 | // 2 | // MTLogPluginOutputCatcher.m 3 | // 4 | // @author Marin Todorov, http://www.touch-code-magazine.com 5 | // 6 | 7 | // Copyright (c) 2013 Marin Todorov, Underplot ltd. 8 | // This code is distributed under the terms and conditions of the MIT license. 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 11 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | // 14 | // The MIT License in plain English: http://www.touch-code-magazine.com/JSONModel/MITLicense 15 | 16 | #import "MTLogPluginOutputCatcher.h" 17 | 18 | @implementation MTLogPluginOutputCatcher 19 | { 20 | NSString* _logMessage; 21 | NSArray* _env; 22 | } 23 | 24 | +(NSRange)expectedNumberOfArgumentsForCommand:(NSString*)command 25 | { 26 | return NSMakeRange(0, 0); 27 | } 28 | 29 | -(void)postProcessLogMessage:(NSString *)text env:(NSArray *)env 30 | { 31 | if (text.length>0) { 32 | _logMessage = text; 33 | } 34 | _env = env; 35 | } 36 | 37 | -(NSString*)logMessage 38 | { 39 | return _logMessage; 40 | } 41 | 42 | -(NSArray*)env 43 | { 44 | return _env; 45 | } 46 | 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /MTLog/plugins/MTLogPluginRegister.m: -------------------------------------------------------------------------------- 1 | // 2 | // MTLogPluginRegister.m 3 | // 4 | // @author Marin Todorov, http://www.touch-code-magazine.com 5 | // 6 | 7 | // Copyright (c) 2013 Marin Todorov, Underplot ltd. 8 | // This code is distributed under the terms and conditions of the MIT license. 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 11 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | // 14 | // The MIT License in plain English: http://www.touch-code-magazine.com/JSONModel/MITLicense 15 | 16 | #import "MTLogPluginRegister.h" 17 | 18 | @implementation MTLogPluginRegister 19 | 20 | +(NSRange)expectedNumberOfArgumentsForCommand:(NSString*)command 21 | { 22 | return NSMakeRange(1, 1); 23 | } 24 | 25 | -(NSString*)preProcessLogMessage:(NSString *)text env:(NSArray *)env 26 | { 27 | //register the new plugin 28 | NSMutableDictionary* regPlugins = env[MTLogRegisteredPlugins]; 29 | Class pluginClass = NSClassFromString(self.args.firstObject); 30 | if (pluginClass==nil) { 31 | return [NSString stringWithFormat:@"MTLog: Class '%@' not found", self.args.firstObject]; 32 | } 33 | regPlugins[self.value] = pluginClass; 34 | 35 | //delete self 36 | NSMutableArray* enabledPlugins = env[MTLogEnabledPlugins]; 37 | [enabledPlugins removeObject: self]; 38 | 39 | //return the message 40 | return text; 41 | } 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /MTLogDemo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // MTLogDemo 4 | // 5 | // Created by Marin Todorov on 7/9/13. 6 | // Copyright (c) 2013 Underplot ltd. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "MTLogPluginRoute.h" 11 | 12 | @interface ViewController () 13 | @end 14 | 15 | @implementation ViewController 16 | 17 | -(IBAction)actionBasicLogMessages 18 | { 19 | NSLog(@"Simple Message 1"); 20 | NSLog(@"Current date: %@",[NSDate date]); 21 | } 22 | 23 | -(IBAction)actionCustomPrefix:(id)sender 24 | { 25 | NSLog(@"_prefix:set([custom prefix!]) Message Log 1"); 26 | NSLog(@"_prefix:use([$file $line]) Message Log 2"); 27 | NSLog(@"Message Log 3"); 28 | NSLog(@"_prefix:use()--------------------------------"); 29 | for (int i=0;i<5;i++) { 30 | NSLog(@"_prefix:set($counter.) Count count count ..."); 31 | } 32 | NSLog(@"_prefix:set(default)"); 33 | } 34 | 35 | -(void)extraLogMessages 36 | { 37 | NSLog(@"Message 3"); 38 | NSLog(@"Message 4"); 39 | } 40 | 41 | -(IBAction)actionFilter:(id)sender 42 | { 43 | NSLog(@"Filter all messages outside actionFilter: method"); 44 | 45 | NSLog(@"_filter:actionFilter:"); 46 | NSLog(@"Message 1"); 47 | NSLog(@"Message 2"); 48 | [self extraLogMessages]; 49 | NSLog(@"Message 5"); 50 | NSLog(@"_remove:_filter:actionFilter:"); 51 | } 52 | 53 | -(IBAction)actionClearOnSearchResult:(id)sender 54 | { 55 | NSLog(@"_search:clear(vacation)"); 56 | NSLog(@"Go in vacation."); 57 | NSLog(@"I go home."); 58 | NSLog(@"_remove:_search:clear(vacation)"); 59 | } 60 | 61 | -(IBAction)actionSaveToFile:(id)sender 62 | { 63 | NSLog(@"_route:file(log.txt)"); 64 | 65 | NSLog(@"Message 1"); 66 | NSLog(@"Message 2"); 67 | 68 | NSString* logPath = [(MTLogPluginRoute*)[MTLog pluginsByName:@"route"].firstObject logFilePath]; 69 | 70 | [[[UIAlertView alloc] initWithTitle:@"Log file saved" 71 | message:[NSString stringWithFormat:@"The log file is located at %@", logPath] 72 | delegate:nil 73 | cancelButtonTitle:@"Close" 74 | otherButtonTitles:nil] show]; 75 | 76 | NSLog(@"_remove:_route:file(log.txt)"); 77 | } 78 | 79 | -(IBAction)actionOpenGitHub:(id)sender 80 | { 81 | [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"http://github.com/icanzilb/MTLog"]]; 82 | } 83 | 84 | 85 | @end 86 | -------------------------------------------------------------------------------- /docs/html/hierarchy.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MTLog Hierarchy 6 | 7 | 8 | 9 | 10 | 11 |
12 | 16 | 17 | 20 | 21 |
22 |
23 |
24 | 27 | 32 |
33 | 34 |
35 |

Class Hierarchy

36 | 37 |
    38 | 39 |
  • NSObject 40 | 47 |
  • 48 | 49 |
50 | 51 |
52 | 53 | 54 | 55 |
56 | 57 |

Protocol References

58 | 63 | 64 | 65 |
66 | 67 |
68 | 71 | 81 |
82 |
83 | 84 | -------------------------------------------------------------------------------- /docs/html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MTLog Reference 6 | 7 | 8 | 9 | 10 | 11 |
12 | 16 | 17 | 20 | 21 |
22 |
23 |
24 | 27 | 32 |
33 | 34 | 35 | 36 | 37 | 38 |
39 |

Class References

40 | 47 |
48 | 49 | 50 | 51 |
52 | 53 |

Protocol References

54 | 59 | 60 | 61 |
62 | 63 |
64 | 67 | 77 |
78 |
79 | 80 | -------------------------------------------------------------------------------- /MTLog/plugins/MTLogPluginRemove.m: -------------------------------------------------------------------------------- 1 | // 2 | // MTLogPluginRemove.m 3 | // 4 | // @author Marin Todorov, http://www.touch-code-magazine.com 5 | // 6 | 7 | // Copyright (c) 2013 Marin Todorov, Underplot ltd. 8 | // This code is distributed under the terms and conditions of the MIT license. 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 11 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | // 14 | // The MIT License in plain English: http://www.touch-code-magazine.com/JSONModel/MITLicense 15 | 16 | #import "MTLogPluginRemove.h" 17 | 18 | @implementation MTLogPluginRemove 19 | 20 | +(NSRange)expectedNumberOfArgumentsForCommand:(NSString*)command 21 | { 22 | return NSMakeRange(0, 100); 23 | } 24 | 25 | -(NSString*)preProcessLogMessage:(NSString *)text env:(NSArray *)env 26 | { 27 | //remove the target plugin 28 | MTLogPlugin* plugin; 29 | 30 | NSMutableArray* pluginParts = [[self.value componentsSeparatedByString:@":"] mutableCopy]; 31 | NSString* pluginName = [pluginParts.firstObject stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:@""]; 32 | [pluginParts removeObjectAtIndex:0]; 33 | NSString* pluginValue = [pluginParts componentsJoinedByString:@":"]; 34 | 35 | NSString* searchID = [NSString stringWithFormat:@"%@:%@:%@", pluginName, pluginValue, [self.args componentsJoinedByString:@","]]; 36 | 37 | for (plugin in env[MTLogEnabledPlugins]) { 38 | if ([[plugin pluginID] isEqualToString:searchID]) { 39 | break; 40 | } 41 | } 42 | [env[MTLogEnabledPlugins] removeObject: plugin]; 43 | 44 | //remove self 45 | NSMutableArray* enabledPlugins = env[MTLogEnabledPlugins]; 46 | [enabledPlugins removeObject: self]; 47 | 48 | //return the log message 49 | return text; 50 | } 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /MTLog/plugins/MTLogPluginSearch.m: -------------------------------------------------------------------------------- 1 | // 2 | // MTLogPluginSearch.m 3 | // 4 | // @author Marin Todorov, http://www.touch-code-magazine.com 5 | // 6 | 7 | // Copyright (c) 2013 Marin Todorov, Underplot ltd. 8 | // This code is distributed under the terms and conditions of the MIT license. 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 11 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | // 14 | // The MIT License in plain English: http://www.touch-code-magazine.com/JSONModel/MITLicense 15 | 16 | #import "MTLogPluginSearch.h" 17 | 18 | @implementation MTLogPluginSearch 19 | 20 | +(NSRange)expectedNumberOfArgumentsForCommand:(NSString*)command 21 | { 22 | return NSMakeRange(1, 1); 23 | } 24 | 25 | -(NSString*)preProcessLogMessage:(NSString *)text env:(NSArray *)env 26 | { 27 | if ([text rangeOfString: self.args.firstObject].location != NSNotFound) { 28 | if ([@"clear" isEqualToString:self.value]) { 29 | NSMutableString* string = [@"" mutableCopy]; 30 | for (int i=0;i<20;i++) { 31 | [string appendString:@" \n"]; 32 | } 33 | return [NSString stringWithFormat:@"%@%@", [string copy], text]; 34 | } 35 | } 36 | return text; 37 | } 38 | 39 | -(void)postProcessLogMessage:(NSString *)text env:(NSArray *)env 40 | { 41 | if ([text rangeOfString: self.args.firstObject].location != NSNotFound) { 42 | if ([@"throw" isEqualToString:self.value]) { 43 | @try { 44 | _resultsCount++; 45 | @throw [NSException exceptionWithName:self.value reason:text userInfo:nil]; 46 | } 47 | @catch (NSException *exception) { 48 | //catch exceptions in Xcode to break on this line and debug your code 49 | } 50 | } 51 | } 52 | } 53 | 54 | @end 55 | -------------------------------------------------------------------------------- /MTLogDemoTests/Mocks/MTLog+Testing.m: -------------------------------------------------------------------------------- 1 | // 2 | // MTLog+Testing.m 3 | // 4 | // @author Marin Todorov, http://www.touch-code-magazine.com 5 | // 6 | 7 | // Copyright (c) 2013 Marin Todorov, Underplot ltd. 8 | // This code is distributed under the terms and conditions of the MIT license. 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 11 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | // 14 | // The MIT License in plain English: http://www.touch-code-magazine.com/JSONModel/MITLicense 15 | 16 | #import "MTLog+Testing.h" 17 | #import "JRSwizzle.h" 18 | #import "MTLogPluginOutputCatcher.h" 19 | 20 | @implementation MTLog (Testing) 21 | 22 | static MTLogPluginOutputCatcher* __lastMessageCatcher = nil; 23 | 24 | +(void)load 25 | { 26 | //super implementation 27 | [super load]; 28 | 29 | //initialize the class 30 | static dispatch_once_t onceToken; 31 | dispatch_once(&onceToken, ^{ 32 | 33 | //swizzle the original MTLog implementation 34 | NSError* err = nil; 35 | [MTLog jr_swizzleClassMethod: @selector(log:method:lineNr:text:) 36 | withClassMethod: @selector(grabOutputLog:method:lineNr:text:) 37 | error: &err]; 38 | }); 39 | } 40 | 41 | +(void)grabOutputLog:(NSString*)fileName method:(NSString*)method lineNr:(NSNumber*)lineNr text:(NSString *)format, ... 42 | { 43 | //add a catcher to the pluing chain 44 | __lastMessageCatcher = [[MTLogPluginOutputCatcher alloc] initWithName:@"catcher" value:@"output" args:nil]; 45 | [self addPlugin: __lastMessageCatcher]; 46 | 47 | va_list args; 48 | va_start(args, format); 49 | [self grabOutputLog:fileName method:(NSString*)method lineNr:lineNr text:format, args]; 50 | va_end(args); 51 | 52 | [self removePlugin: __lastMessageCatcher]; 53 | } 54 | 55 | +(MTLogPluginOutputCatcher*)lastMessageCatcher 56 | { 57 | return __lastMessageCatcher; 58 | } 59 | 60 | @end 61 | -------------------------------------------------------------------------------- /MTLog/MTLogPlugin.m: -------------------------------------------------------------------------------- 1 | // 2 | // MTLogPlugin.m 3 | // 4 | // @author Marin Todorov, http://www.touch-code-magazine.com 5 | // 6 | 7 | // Copyright (c) 2013 Marin Todorov, Underplot ltd. 8 | // This code is distributed under the terms and conditions of the MIT license. 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 11 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | // 14 | // The MIT License in plain English: http://www.touch-code-magazine.com/JSONModel/MITLicense 15 | 16 | #import "MTLogPlugin.h" 17 | 18 | @implementation MTLogPlugin 19 | 20 | -(instancetype)init 21 | { 22 | NSAssert(NO, @"Use initWithName:value:args:"); 23 | return nil; 24 | } 25 | 26 | +(NSRange)expectedNumberOfArgumentsForCommand:(NSString*)command 27 | { 28 | NSAssert(NO, @"Override +(NSRange)expectedNumberOfArguments in '%@'", [self class]); 29 | return NSMakeRange(0, 0); 30 | } 31 | 32 | -(instancetype)initWithName:(NSString*)name value:(NSString*)value args:(NSArray*)args 33 | { 34 | //validate 35 | if (value.length < 1 || 36 | args.count < [[self class] expectedNumberOfArgumentsForCommand: value].location || 37 | args.count > [[self class] expectedNumberOfArgumentsForCommand: value].length) { 38 | 39 | NSLog(@"MTLog: Wrong number of arguments provided for plugin: %@ value: %@ arguments: %@", name, value, args); 40 | 41 | return nil; 42 | } 43 | 44 | self = [super init]; 45 | if (self) { 46 | self.name = name; 47 | self.value = value; 48 | self.args = args; 49 | self.affectsFirstLogmessage = YES; 50 | } 51 | return self; 52 | } 53 | 54 | -(NSString*)pluginID 55 | { 56 | return [NSString stringWithFormat:@"%@:%@:%@", self.name, self.value, [self.args componentsJoinedByString:@","]]; 57 | } 58 | 59 | -(NSString*)description 60 | { 61 | return [NSString stringWithFormat:@"%@: %@(%@)", [[self class] description], _value, [_args componentsJoinedByString:@","]]; 62 | } 63 | 64 | @end 65 | -------------------------------------------------------------------------------- /MTLogDemoTests/tests/MTLogSearchTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // MTLogSearchTests.m 3 | // 4 | // @author Marin Todorov, http://www.touch-code-magazine.com 5 | // 6 | 7 | // Copyright (c) 2013 Marin Todorov, Underplot ltd. 8 | // This code is distributed under the terms and conditions of the MIT license. 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 11 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | // 14 | // The MIT License in plain English: http://www.touch-code-magazine.com/JSONModel/MITLicense 15 | 16 | #import 17 | 18 | #import "MTLog+Testing.h" 19 | #import "MTLogPluginOutputCatcher.h" 20 | 21 | #import "MTLogPluginSearch.h" 22 | 23 | @interface MTLogSearchTests : XCTestCase 24 | 25 | @end 26 | 27 | @implementation MTLogSearchTests 28 | 29 | - (void)setUp 30 | { 31 | [super setUp]; 32 | 33 | } 34 | 35 | - (void)tearDown 36 | { 37 | 38 | [super tearDown]; 39 | } 40 | 41 | - (void)testSearchThrow 42 | { 43 | NSLog(@"_prefix:set()"); 44 | 45 | NSLog(@"_search:throw(vacation)"); 46 | 47 | for (int i=0;i<5;i++) { 48 | NSLog(@"I go to vacation"); //should be a match 49 | NSLog(@"I come back home"); 50 | } 51 | 52 | NSMutableArray* enabled = [MTLog lastMessageCatcher].env[4]; 53 | MTLogPluginSearch* search = nil; 54 | for (id plugin in enabled) { 55 | if ([plugin isKindOfClass:[MTLogPluginSearch class]]) { 56 | search = plugin; 57 | } 58 | } 59 | 60 | XCTAssertEqual(5, search.resultsCount, @"Search did not find 5 times 'vacation'"); 61 | 62 | NSLog(@"_remove:_search:throw(vacation)"); 63 | } 64 | 65 | - (void)testSearchClear 66 | { 67 | NSLog(@"_prefix:set()"); 68 | 69 | NSLog(@"_search:clear(vacation)"); 70 | NSLog(@"Go in vacation."); 71 | XCTAssertTrue( [MTLog lastMessageCatcher].logMessage.length > 20 , @"The message was not altered, but is a search result"); 72 | 73 | NSLog(@"I go home."); 74 | XCTAssertTrue( [MTLog lastMessageCatcher].logMessage.length == 10 , @"The message was altered, but it's not a search result"); 75 | 76 | NSLog(@"_remove:_search:clear(vacation)"); 77 | } 78 | 79 | @end 80 | -------------------------------------------------------------------------------- /MTLogDemoTests/tests/MTLogRegisterTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // MTLogRegisterTests.m 3 | // 4 | // @author Marin Todorov, http://www.touch-code-magazine.com 5 | // 6 | 7 | // Copyright (c) 2013 Marin Todorov, Underplot ltd. 8 | // This code is distributed under the terms and conditions of the MIT license. 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 11 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | // 14 | // The MIT License in plain English: http://www.touch-code-magazine.com/JSONModel/MITLicense 15 | 16 | #import 17 | 18 | #import "MTLog+Testing.h" 19 | #import "MTLogPluginOutputCatcher.h" 20 | 21 | #import "MTLogPluginSmilie.h" 22 | 23 | @interface MTLogRegisterTests : XCTestCase 24 | 25 | @end 26 | 27 | @implementation MTLogRegisterTests 28 | 29 | - (void)setUp 30 | { 31 | [super setUp]; 32 | 33 | } 34 | 35 | - (void)tearDown 36 | { 37 | 38 | [super tearDown]; 39 | } 40 | 41 | - (void)testRegister 42 | { 43 | NSLog(@"_prefix:set()"); 44 | NSMutableDictionary* reg = [MTLog lastMessageCatcher].env[5]; 45 | [reg removeObjectForKey:@"MTLogPluginSmilie"]; 46 | 47 | NSLog(@"_register:smilie(MTLogPluginSmilie)"); 48 | NSMutableDictionary* reg1 = [MTLog lastMessageCatcher].env[5]; 49 | XCTAssertNotNil(reg1[@"smilie"], @"Smilie plugin not found after registration"); 50 | 51 | Class registeredPlugin = reg1[@"smilie"]; 52 | XCTAssertEqualObjects([registeredPlugin description], @"MTLogPluginSmilie", @"Smilie plugin registration didn't register the correct class"); 53 | 54 | NSLog(@"Simple message"); 55 | XCTAssertEqualObjects([MTLog lastMessageCatcher].logMessage, @"Simple message", @"Message was adjusted when expected no adjustments"); 56 | 57 | NSLog(@"_smilie:on Simple message"); 58 | XCTAssertEqualObjects([MTLog lastMessageCatcher].logMessage, @":) Simple message", @"Smilie is not addded when the pluing should be on"); 59 | 60 | //cleanup 61 | [reg1 removeObjectForKey:@"smilie"]; 62 | NSMutableArray* enabled = [MTLog lastMessageCatcher].env[4]; 63 | id match = nil; 64 | for (id plugin in enabled) { 65 | if ([plugin isKindOfClass:[MTLogPluginSmilie class]]) { 66 | match = plugin; 67 | } 68 | } 69 | 70 | if (match) { 71 | [enabled removeObject: match]; 72 | } 73 | 74 | } 75 | 76 | 77 | 78 | @end 79 | -------------------------------------------------------------------------------- /MTLog/MTLog.h: -------------------------------------------------------------------------------- 1 | // 2 | // MTLog.h 3 | // 4 | // @author Marin Todorov, http://www.touch-code-magazine.com 5 | // 6 | 7 | // Copyright (c) 2013 Marin Todorov, Underplot ltd. 8 | // This code is distributed under the terms and conditions of the MIT license. 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 11 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | // 14 | // The MIT License in plain English: http://www.touch-code-magazine.com/JSONModel/MITLicense 15 | 16 | #import 17 | 18 | //disable loggin on production 19 | #ifdef DEBUG 20 | 21 | #define NSLog( s, ... ) [MTLog log: \ 22 | [[NSString stringWithUTF8String:__FILE__] lastPathComponent] \ 23 | method: [NSString stringWithUTF8String:__PRETTY_FUNCTION__] \ 24 | lineNr: [NSNumber numberWithInt:__LINE__] \ 25 | text: [NSString stringWithFormat:(s), ##__VA_ARGS__] \ 26 | ] 27 | 28 | #endif 29 | 30 | /** 31 | * In most cases you would not need to interact with the MTLog class directly. Just keep using NSLog() as normal and 32 | * have a look at the list of available commands you can use from within the text you supply to NSLog. 33 | * 34 | * When you call NSLog(@"... message ...") it calls behind the scene [MTLog log:method:lineNr:text:]. 35 | * This is also the way you execute commands, for example: 36 | *
37 |  * NSLog(@"_filter:MyClass.m(10,125) Log message");
38 |  * 
39 | * Consult README.md for a list of the available commands and examples. 40 | * This documentation is provided only for the ones who would like to write classes for their own log commands. 41 | */ 42 | @interface MTLog : NSObject 43 | 44 | /** 45 | * @name Logging 46 | */ 47 | 48 | /** 49 | * Don't call this method directly, rather just use NSLog() directly and it will then 50 | * call this method passing the correct paramters. 51 | * @param fileName the file the message is logged from 52 | * @param method the method name where the message is logged from 53 | * @param lineNr the number of the line in the source code file 54 | * @param format the message to log, followed by a arbitrary number of arguments 55 | */ 56 | + (void)log:(NSString*)fileName method:(NSString*)method lineNr:(NSNumber*)lineNr text:(NSString *)format, ...; 57 | 58 | /** 59 | * @name Manipulating plugins 60 | */ 61 | 62 | /** 63 | * Adds an initialized plugin to the plugin chain 64 | * @param plugin an instance of a subclass of MTLogPlugin 65 | */ 66 | + (void)addPlugin:(id)plugin; 67 | 68 | /** 69 | * Removes a plugin instance of the plugin chain 70 | * @param plugin an instance of a subclass of MTLogPlugin 71 | */ 72 | + (void)removePlugin:(id)plugin; 73 | 74 | /** 75 | * Gets a list of all the plugins in the plugin chain that are of certain class (i.e. you can grab all filter plugins in the chain) 76 | * @param name the class name of the plugins to fetch 77 | * @return NSArray list of currently active plugins matching the provided class name 78 | */ 79 | + (NSArray*)pluginsByName:(NSString*)name; 80 | 81 | /** 82 | * A list of all currently active plugins 83 | */ 84 | @property (strong, readonly, nonatomic) NSMutableArray* enabledPlugins; 85 | 86 | @end -------------------------------------------------------------------------------- /MTLogDemoTests/tests/MTLogRouteTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // MTLogRouteTests.m 3 | // 4 | // @author Marin Todorov, http://www.touch-code-magazine.com 5 | // 6 | 7 | // Copyright (c) 2013 Marin Todorov, Underplot ltd. 8 | // This code is distributed under the terms and conditions of the MIT license. 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 11 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | // 14 | // The MIT License in plain English: http://www.touch-code-magazine.com/JSONModel/MITLicense 15 | 16 | #import 17 | 18 | #import "MTLog+Testing.h" 19 | #import "MTLogPluginOutputCatcher.h" 20 | 21 | #import "MTLogPluginRoute.h" 22 | 23 | @interface MTLogRouteTests : XCTestCase 24 | 25 | @end 26 | 27 | @implementation MTLogRouteTests 28 | 29 | - (void)setUp 30 | { 31 | [super setUp]; 32 | 33 | } 34 | 35 | - (void)tearDown 36 | { 37 | 38 | [super tearDown]; 39 | } 40 | 41 | - (void)testRouteFile 42 | { 43 | NSLog(@"_prefix:set()"); 44 | 45 | NSLog(@"_route:file(log.txt)"); 46 | 47 | NSLog(@"Message 1"); 48 | NSLog(@"Message 2"); 49 | 50 | NSString* logPath = [(MTLogPluginRoute*)[MTLog pluginsByName:@"route"].firstObject logFilePath]; 51 | 52 | XCTAssertNotNil(logPath, @"Route plugin log file path is nil"); 53 | 54 | NSString* logText = [NSString stringWithContentsOfFile:logPath encoding:NSUTF8StringEncoding error:nil]; 55 | 56 | XCTAssertNotNil(logText, @"Route log file contents is emtpy or file not found"); 57 | XCTAssertEqualObjects(logText, @"Message 1\nMessage 2\n", @"The log contents are not what was expected"); 58 | 59 | NSLog(@"_remove:_route:file(log.txt)"); 60 | } 61 | 62 | - (void)testRouteFileAppend 63 | { 64 | NSLog(@"_prefix:set()"); 65 | 66 | //start a new log file 67 | NSLog(@"_route:file(logAppend.txt)"); 68 | 69 | NSLog(@"Message 1"); 70 | NSLog(@"Message 2"); 71 | 72 | NSLog(@"_remove:_route:file(logAppend.txt)"); 73 | 74 | //append to the existing log file 75 | NSLog(@"_route:file(logAppend.txt,append)"); 76 | 77 | NSLog(@"Message 3"); 78 | NSLog(@"Message 4"); 79 | 80 | NSString* logPath = [(MTLogPluginRoute*)[MTLog pluginsByName:@"route"].firstObject logFilePath]; 81 | 82 | XCTAssertNotNil(logPath, @"Route plugin log file path is nil"); 83 | 84 | NSString* logText = [NSString stringWithContentsOfFile:logPath encoding:NSUTF8StringEncoding error:nil]; 85 | 86 | XCTAssertNotNil(logText, @"Route log file contents is emtpy or file not found"); 87 | 88 | //test the log file contents 89 | NSArray* logLines = [logText componentsSeparatedByString:@"\n"]; 90 | XCTAssertEqualObjects(logLines[0], @"Message 1", @"Line 1 is not 'Message 1'"); 91 | XCTAssertTrue( [logLines[2] hasPrefix:@"--- 20"] , @"Line 3 is not the append separator."); 92 | XCTAssertEqualObjects(logLines[3], @"Message 3", @"Line 4 is not 'Message 3'"); 93 | 94 | NSLog(@"_remove:_route:file(logAppend.txt)"); 95 | } 96 | 97 | - (void)testRouteFilePrefix 98 | { 99 | 100 | } 101 | 102 | @end 103 | -------------------------------------------------------------------------------- /MTLog/plugins/MTLogPluginFilter.m: -------------------------------------------------------------------------------- 1 | // 2 | // MTLogPluginFilter.m 3 | // 4 | // @author Marin Todorov, http://www.touch-code-magazine.com 5 | // 6 | 7 | // Copyright (c) 2013 Marin Todorov, Underplot ltd. 8 | // This code is distributed under the terms and conditions of the MIT license. 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 11 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | // 14 | // The MIT License in plain English: http://www.touch-code-magazine.com/JSONModel/MITLicense 15 | 16 | #import "MTLogPluginFilter.h" 17 | #import "MTLog.h" 18 | 19 | @implementation MTLogPluginFilter 20 | { 21 | BOOL _initialized; 22 | 23 | //filter by line numbers 24 | int _minLine; 25 | int _maxLine; 26 | 27 | //filter by file+line 28 | NSString* _fileName; 29 | int _fileLineNr; 30 | } 31 | 32 | +(NSRange)expectedNumberOfArgumentsForCommand:(NSString*)command 33 | { 34 | return NSMakeRange(0, 2); 35 | } 36 | 37 | -(instancetype)initWithName:(NSString*)name value:(NSString*)value args:(NSArray*)args 38 | { 39 | self = [super initWithName:name value:value args:args]; 40 | if (self) { 41 | self.affectsFirstLogmessage = NO; 42 | if ([self.value isEqualToString:@"$this"]) { 43 | self.affectsFirstLogmessage = YES; 44 | } 45 | if (args && args.count==2) { 46 | _minLine = [args[0] intValue]; 47 | _maxLine = [args[1] intValue]; 48 | } 49 | } 50 | return self; 51 | } 52 | 53 | -(void)willEnableForLog:(MTLog*)log 54 | { 55 | if ([self.value isEqualToString:@"$this"]) { 56 | 57 | //if a $this filter, remove all other filters 58 | NSMutableArray* matches = [@[] mutableCopy]; 59 | for (MTLogPlugin* plugin in log.enabledPlugins) { 60 | if ([plugin isKindOfClass:[MTLogPluginFilter class]]) { 61 | [matches addObject: plugin]; 62 | break; 63 | } 64 | } 65 | 66 | if (matches.count>0) { 67 | for (id plugin in matches) { 68 | [log.enabledPlugins removeObject: plugin]; 69 | } 70 | } 71 | } 72 | } 73 | 74 | -(NSString*)preProcessLogMessage:(NSString*)text env:(NSArray*)env 75 | { 76 | //initialize 77 | if (_initialized==NO) { 78 | if ([self.value isEqualToString:@"$this"]) { 79 | _fileName = env[MTLogFileName]; 80 | _fileLineNr = [env[MTLogLineNumber] intValue]; 81 | } 82 | 83 | _initialized=YES; 84 | } 85 | 86 | // 1. filter by file name 87 | if ([self.value hasSuffix:@".m"] || [self.value hasSuffix:@".mm"]) { 88 | if ([env[MTLogFileName] isEqualToString: self.value]==NO) { 89 | return @""; 90 | } else if (_maxLine>0) { 91 | 92 | //filter by code line number 93 | int curLine = [env[MTLogLineNumber] intValue]; 94 | if ( curLine < _minLine || curLine > _maxLine ) { 95 | return @""; 96 | } 97 | } 98 | 99 | } else { 100 | 101 | // 2. filter by $this 102 | if ([self.value isEqualToString:@"$this"]) { 103 | if ( [_fileName isEqualToString: env[MTLogFileName]]==NO || _fileLineNr != [env[MTLogLineNumber] intValue] ) { 104 | return @""; 105 | } 106 | } else { 107 | 108 | //3. filter by method name 109 | if ([env[MTLogMethodName] isEqualToString: self.value]==NO) { 110 | return @""; 111 | } 112 | 113 | } 114 | 115 | } 116 | 117 | return text; 118 | } 119 | 120 | @end 121 | -------------------------------------------------------------------------------- /MTLogDemoTests/tests/MTLogPrefixTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // MTLogDemoTests.m 3 | // 4 | // @author Marin Todorov, http://www.touch-code-magazine.com 5 | // 6 | 7 | // Copyright (c) 2013 Marin Todorov, Underplot ltd. 8 | // This code is distributed under the terms and conditions of the MIT license. 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 11 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | // 14 | // The MIT License in plain English: http://www.touch-code-magazine.com/JSONModel/MITLicense 15 | 16 | #import 17 | 18 | #import "MTLog+Testing.h" 19 | #import "MTLogPluginOutputCatcher.h" 20 | 21 | @interface MTLogPrefixTests : XCTestCase 22 | @end 23 | 24 | @implementation MTLogPrefixTests 25 | 26 | - (void)testEmtpyPrefix 27 | { 28 | //test no prefix 29 | NSLog(@"_prefix:set()"); 30 | NSLog(@"Simple message"); 31 | 32 | XCTAssertEqualObjects([MTLog lastMessageCatcher].logMessage, @"Simple message", @"The message logged is not equal to 'Simple message'"); 33 | } 34 | 35 | - (void)testMethod 36 | { 37 | //test method name 38 | NSLog(@"_prefix:set($method:)"); 39 | NSLog(@"Simple message"); 40 | 41 | XCTAssertEqualObjects([MTLog lastMessageCatcher].logMessage, @"testMethod: Simple message", @"The prefix is not set to method name"); 42 | } 43 | 44 | -(void)testFilename 45 | { 46 | //test file name + method name 47 | NSLog(@"_prefix:set([$file $method])"); 48 | NSLog(@"Simple message"); 49 | 50 | XCTAssertEqualObjects([MTLog lastMessageCatcher].logMessage, @"[MTLogPrefixTests.m testFilename] Simple message", @"The prefix is not set to file name + method name"); 51 | } 52 | 53 | -(void)testClassName 54 | { 55 | //test class name 56 | NSLog(@"_prefix:set($class)"); 57 | NSLog(@"Simple message"); 58 | XCTAssertEqualObjects([MTLog lastMessageCatcher].logMessage, @"MTLogPrefixTests Simple message", @"The prefix is not set to class name"); 59 | } 60 | 61 | -(void)testCombinedPrefix 62 | { 63 | 64 | //test file, class, method and line combined 65 | NSLog(@"_prefix:set($file $class $method $line)"); 66 | int nextLineNr = __LINE__ + 1; 67 | NSLog(@"Simple message"); 68 | 69 | NSString* expectedMessage = [NSString stringWithFormat:@"MTLogPrefixTests.m MTLogPrefixTests testCombinedPrefix %d Simple message", nextLineNr]; 70 | XCTAssertEqualObjects([MTLog lastMessageCatcher].logMessage, expectedMessage , @"The prefix did not match the expected value"); 71 | } 72 | 73 | -(void)testPrefixCounter 74 | { 75 | //test the prefix counter 76 | NSLog(@"_prefix:use($counter.) Simple message"); 77 | 78 | XCTAssertEqualObjects([MTLog lastMessageCatcher].logMessage, @"1. Simple message", @"The $counter did not initialize"); 79 | 80 | //test repeating prefix counter 81 | for (int i=1;i<11;i++) { 82 | NSLog(@"_prefix:use($counter.) Simple message"); 83 | NSString* message = [NSString stringWithFormat:@"%i. Simple message", i]; 84 | XCTAssertEqualObjects([MTLog lastMessageCatcher].logMessage, 85 | message, 86 | @"The $counter did not count"); 87 | } 88 | } 89 | 90 | -(void)testMessageCounter 91 | { 92 | //test counter inside the log message 93 | NSLog(@"_prefix:set()"); 94 | 95 | for (int i=1;i<11;i++) { 96 | NSLog(@"Simple message #$counter"); 97 | NSString* message = [NSString stringWithFormat:@"Simple message #%i", i]; 98 | XCTAssertEqualObjects([MTLog lastMessageCatcher].logMessage, 99 | message, 100 | @"The $counter did not count"); 101 | } 102 | } 103 | 104 | - (void)setUp 105 | { 106 | #ifndef DEBUG 107 | #define DEBUG 1 108 | #endif 109 | 110 | [super setUp]; 111 | } 112 | 113 | - (void)tearDown 114 | { 115 | 116 | [super tearDown]; 117 | } 118 | 119 | @end 120 | -------------------------------------------------------------------------------- /MTLog/plugins/MTLogPluginPrefix.m: -------------------------------------------------------------------------------- 1 | // 2 | // MTLogPluginPrefix.m 3 | // 4 | // @author Marin Todorov, http://www.touch-code-magazine.com 5 | // 6 | 7 | // Copyright (c) 2013 Marin Todorov, Underplot ltd. 8 | // This code is distributed under the terms and conditions of the MIT license. 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 11 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | // 14 | // The MIT License in plain English: http://www.touch-code-magazine.com/JSONModel/MITLicense 15 | 16 | #import "MTLogPluginPrefix.h" 17 | #import "MTLog.h" 18 | 19 | @implementation MTLogPluginPrefix 20 | { 21 | BOOL _initialized; 22 | } 23 | 24 | +(NSRange)expectedNumberOfArgumentsForCommand:(NSString*)command 25 | { 26 | return NSMakeRange(0, 1); 27 | } 28 | 29 | -(void)willEnableForLog:(MTLog*)log 30 | { 31 | if ([self.value isEqualToString:@"use"]) { 32 | for (MTLogPlugin* plugin in log.enabledPlugins) { 33 | if ([plugin isKindOfClass:[MTLogPluginPrefix class]]) { 34 | [(MTLogPluginPrefix*)plugin setShouldSkipCurrentMessage:YES]; 35 | } 36 | } 37 | } 38 | else if ([self.value isEqualToString:@"set"]) { 39 | NSArray* prefixes = [MTLog pluginsByName:@"prefix"]; 40 | if (prefixes.count>0) { 41 | for (id plugin in prefixes) { 42 | [MTLog removePlugin: plugin]; 43 | } 44 | } 45 | } 46 | } 47 | 48 | -(NSString*)preProcessLogMessage:(NSString *)text env:(NSArray *)env 49 | { 50 | static NSArray* variables; 51 | static NSMutableDictionary* counters; 52 | static dispatch_once_t onceToken; 53 | dispatch_once(&onceToken, ^{ 54 | variables = @[@"$file", @"$class", @"$method", @"$line", @"$counter", @"$timestamp"]; 55 | counters = [@{} mutableCopy]; 56 | }); 57 | 58 | //apply prefix to message 59 | if (self.shouldSkipCurrentMessage) { 60 | //skip current message in favor of a "set" command 61 | self.shouldSkipCurrentMessage=NO; 62 | } else { 63 | //apply prefix to the log message 64 | if (self.args.count>0 && text.length>0) { 65 | if ([@"default" isEqualToString: self.args.firstObject]) { 66 | text = [NSString stringWithFormat:@"%@:%@ > %@",env[MTLogFileName], env[MTLogLineNumber], text]; 67 | } else { 68 | 69 | //parse the variables 70 | NSString* prefix = [self.args.firstObject copy]; 71 | for (int i=0;i<4;i++) { 72 | prefix = [prefix stringByReplacingOccurrencesOfString:variables[i] withString:[env[i] description]]; 73 | } 74 | if ([prefix rangeOfString:@"$timestamp"].location!=NSNotFound) { 75 | //replace timestamps 76 | NSDateComponents *dc = [[NSCalendar currentCalendar] components:NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond fromDate:[NSDate date]]; 77 | NSString* timestamp = [NSString stringWithFormat:@"%i-%i-%i %i:%i:%i", dc.year, dc.month, dc.day, dc.hour, dc.minute, dc.second ]; 78 | prefix = [prefix stringByReplacingOccurrencesOfString:@"$timestamp" withString:timestamp]; 79 | } 80 | text = [NSString stringWithFormat:@"%@ %@", prefix, text]; 81 | } 82 | } 83 | 84 | if (text.length>0) { 85 | //handle any counters 86 | if ([text rangeOfString: @"$counter"].location != NSNotFound) { 87 | NSString* messageID = [NSString stringWithFormat:@"%@:%@:%@", env[MTLogFileName], env[MTLogMethodName], env[MTLogLineNumber]]; 88 | int count = [counters[messageID] intValue]+1; 89 | counters[messageID] = @(count); 90 | text = [text stringByReplacingOccurrencesOfString:@"$counter" withString: [@(count) stringValue]]; 91 | } 92 | } 93 | 94 | } 95 | 96 | return text; 97 | } 98 | 99 | -(void)postProcessLogMessage:(NSString *)text env:(NSArray *)env 100 | { 101 | //one time prefix 102 | if ([self.value isEqualToString:@"use"]) { 103 | [env[MTLogEnabledPlugins] removeObject: self]; 104 | } 105 | } 106 | 107 | @end 108 | -------------------------------------------------------------------------------- /MTLogDemoTests/vendor/JRSwizzle/README.markdown: -------------------------------------------------------------------------------- 1 | # JRSwizzle 2 | 3 | ## Description 4 | 5 | JRSwizzle is source code package that offers a single, easy, correct+consistent interface for exchanging Objective-C method implementations ("method swizzling") across many versions of Mac OS X, iOS, Objective-C and runtime architectures. 6 | 7 | More succinctly: *JRSwizzle wants to be your one-stop-shop for all your method swizzling needs.* 8 | 9 | ## Download 10 | 11 | $ cd /path/to/top/of/your/project 12 | $ git submodule add git://github.com/rentzsch/jrswizzle.git JRSwizzle 13 | $ git submodule init && git submodule update 14 | 15 | # OPTIONAL: Execute the following commands if you want to explicitly peg 16 | # to a certain version. Otherwise `git submodule update` will keep you 17 | # current with HEAD. 18 | 19 | $ cd JRSwizzle 20 | $ git checkout v1.0 21 | 22 | ## Reasons for Existence 23 | 24 | * **Easy:** Just do this: `[SomeClass jr_swizzle:@selector(foo) withMethod:@selector(my_foo) error:&error];` Voila. 25 | * **Correct:** There's a subtle interaction between method swizzling and method inheritance. Following in Kevin Ballard's footsteps, this package Does The Right Thing. 26 | * **Compatible:** JRSwizzle should Just Work on any version of Mac OS X and iOS you care about. Here's the exhaustive compatibility list: 27 | * Mac OS X v10.3/ppc (Ballard implementation) 28 | * Mac OS X v10.4/ppc (Ballard implementation) 29 | * Mac OS X v10.4/i386 (Ballard implementation) 30 | * Mac OS X v10.5/ppc (method_exchangeImplementations+Ballard implementation) 31 | * Mac OS X v10.5/i386 (method_exchangeImplementations+Ballard implementation) 32 | * Mac OS X v10.5/ppc64 (method_exchangeImplementations+Ballard implementation) 33 | * Mac OS X v10.5/x86_64 (method_exchangeImplementations+Ballard implementation) 34 | * iOS 2.0+ (method_exchangeImplementations+Ballard implementation) 35 | * **Robust:** All parameters are checked and JRSwizzle returns an optional `NSError` with high-quality diagnostics. 36 | 37 | ## Support 38 | 39 | Please use [JRSwizzle's GitHub Issues tab](https://github.com/rentzsch/jrswizzle/issues) to [file bugs or feature requests](https://github.com/rentzsch/jrswizzle/issues/new). 40 | 41 | To contribute, please fork this project, make+commit your changes and then send me a pull request. 42 | 43 | ## Comparison 44 | 45 | There's at least four swizzling implementations floating around. Here's a comparison chart to help you make sense of how they relate to each other and why JRSwizzle exists. 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 |
ScenarioSwizzle TechnologyMethod ImplementationCorrect Behavior10.464-bit
1ClassicDirectYESYESNO
2ClassicInheritedNOYESNO
3BallardDirectYESYESNO
4BallardInheritedYESYESNO
5AppleDirectYESNOYES
6AppleInheritedNONOYES
7JRSwizzleDirectYESYESYES
8JRSwizzleInheritedYESYESYES
121 | 122 | * *Classic* is the canonical `MethodSwizzle()` implementation as described in [CocoaDev's MethodSwizzling page](http://www.cocoadev.com/index.pl?MethodSwizzling). 123 | * *Ballard* is [Kevin Ballard's improved implementation](http://kevin.sb.org/2006/12/30/method-swizzling-reimplemented/) which solves the inherited method problem. 124 | * *Apple* is 10.5's new `method_exchangeImplementations` API. 125 | * *JRSwizzle* is this package. 126 | 127 | ## License 128 | 129 | The source code is distributed under the nonviral [MIT License](http://opensource.org/licenses/mit-license.php). It's the simplest most permissive license available. 130 | 131 | ## Version History 132 | 133 | * **v1.0:** Mar 2 2012 134 | 135 | * [NEW] iOS Support. ([Anton Serebryakov](https://github.com/rentzsch/jrswizzle/commit/60ccb350a3577e55d00d3fdfee8b3c0390b8e852])) 136 | 137 | * [NEW] Class method swizzling. ([outis](https://github.com/rentzsch/jrswizzle/pull/1)) 138 | 139 | * **v1.0d1:** May 31 2009 140 | 141 | * [FIX] Soothe valgrind by nulling out `hoisted_method_list->obsolete`, which it apparently reads. ([Daniel Jalkut](http://github.com/rentzsch/jrswizzle/commit/2f677d063202b443ca7a1c46e8b67d67ea6fc88e)) 142 | 143 | * [FIX] Xcode 3.2 apparently now needs `ARCHS` set explicitly for 10.3 targets. ([rentzsch](http://github.com/rentzsch/jrswizzle/commit/4478faa40e4fdb322201da20f24d3996193ea48b)) 144 | 145 | * **v1.0d0:** Apr 09 2009 146 | 147 | * Moved to github. 148 | 149 | * **v1.0d0:** Dec 28 2007 150 | 151 | * Under development. -------------------------------------------------------------------------------- /MTLogDemoTests/vendor/JRSwizzle/JRSwizzle.m: -------------------------------------------------------------------------------- 1 | // JRSwizzle.m semver:1.0 2 | // Copyright (c) 2007-2011 Jonathan 'Wolf' Rentzsch: http://rentzsch.com 3 | // Some rights reserved: http://opensource.org/licenses/MIT 4 | // https://github.com/rentzsch/jrswizzle 5 | 6 | #import "JRSwizzle.h" 7 | 8 | #if TARGET_OS_IPHONE 9 | #import 10 | #import 11 | #else 12 | #import 13 | #endif 14 | 15 | #define SetNSErrorFor(FUNC, ERROR_VAR, FORMAT,...) \ 16 | if (ERROR_VAR) { \ 17 | NSString *errStr = [NSString stringWithFormat:@"%s: " FORMAT,FUNC,##__VA_ARGS__]; \ 18 | *ERROR_VAR = [NSError errorWithDomain:@"NSCocoaErrorDomain" \ 19 | code:-1 \ 20 | userInfo:[NSDictionary dictionaryWithObject:errStr forKey:NSLocalizedDescriptionKey]]; \ 21 | } 22 | #define SetNSError(ERROR_VAR, FORMAT,...) SetNSErrorFor(__func__, ERROR_VAR, FORMAT, ##__VA_ARGS__) 23 | 24 | #if OBJC_API_VERSION >= 2 25 | #define GetClass(obj) object_getClass(obj) 26 | #else 27 | #define GetClass(obj) (obj ? obj->isa : Nil) 28 | #endif 29 | 30 | @implementation NSObject (JRSwizzle) 31 | 32 | + (BOOL)jr_swizzleMethod:(SEL)origSel_ withMethod:(SEL)altSel_ error:(NSError**)error_ { 33 | #if OBJC_API_VERSION >= 2 34 | Method origMethod = class_getInstanceMethod(self, origSel_); 35 | if (!origMethod) { 36 | #if TARGET_OS_IPHONE 37 | SetNSError(error_, @"original method %@ not found for class %@", NSStringFromSelector(origSel_), [self class]); 38 | #else 39 | SetNSError(error_, @"original method %@ not found for class %@", NSStringFromSelector(origSel_), [self className]); 40 | #endif 41 | return NO; 42 | } 43 | 44 | Method altMethod = class_getInstanceMethod(self, altSel_); 45 | if (!altMethod) { 46 | #if TARGET_OS_IPHONE 47 | SetNSError(error_, @"alternate method %@ not found for class %@", NSStringFromSelector(altSel_), [self class]); 48 | #else 49 | SetNSError(error_, @"alternate method %@ not found for class %@", NSStringFromSelector(altSel_), [self className]); 50 | #endif 51 | return NO; 52 | } 53 | 54 | class_addMethod(self, 55 | origSel_, 56 | class_getMethodImplementation(self, origSel_), 57 | method_getTypeEncoding(origMethod)); 58 | class_addMethod(self, 59 | altSel_, 60 | class_getMethodImplementation(self, altSel_), 61 | method_getTypeEncoding(altMethod)); 62 | 63 | method_exchangeImplementations(class_getInstanceMethod(self, origSel_), class_getInstanceMethod(self, altSel_)); 64 | return YES; 65 | #else 66 | // Scan for non-inherited methods. 67 | Method directOriginalMethod = NULL, directAlternateMethod = NULL; 68 | 69 | void *iterator = NULL; 70 | struct objc_method_list *mlist = class_nextMethodList(self, &iterator); 71 | while (mlist) { 72 | int method_index = 0; 73 | for (; method_index < mlist->method_count; method_index++) { 74 | if (mlist->method_list[method_index].method_name == origSel_) { 75 | assert(!directOriginalMethod); 76 | directOriginalMethod = &mlist->method_list[method_index]; 77 | } 78 | if (mlist->method_list[method_index].method_name == altSel_) { 79 | assert(!directAlternateMethod); 80 | directAlternateMethod = &mlist->method_list[method_index]; 81 | } 82 | } 83 | mlist = class_nextMethodList(self, &iterator); 84 | } 85 | 86 | // If either method is inherited, copy it up to the target class to make it non-inherited. 87 | if (!directOriginalMethod || !directAlternateMethod) { 88 | Method inheritedOriginalMethod = NULL, inheritedAlternateMethod = NULL; 89 | if (!directOriginalMethod) { 90 | inheritedOriginalMethod = class_getInstanceMethod(self, origSel_); 91 | if (!inheritedOriginalMethod) { 92 | SetNSError(error_, @"original method %@ not found for class %@", NSStringFromSelector(origSel_), [self className]); 93 | return NO; 94 | } 95 | } 96 | if (!directAlternateMethod) { 97 | inheritedAlternateMethod = class_getInstanceMethod(self, altSel_); 98 | if (!inheritedAlternateMethod) { 99 | SetNSError(error_, @"alternate method %@ not found for class %@", NSStringFromSelector(altSel_), [self className]); 100 | return NO; 101 | } 102 | } 103 | 104 | int hoisted_method_count = !directOriginalMethod && !directAlternateMethod ? 2 : 1; 105 | struct objc_method_list *hoisted_method_list = malloc(sizeof(struct objc_method_list) + (sizeof(struct objc_method)*(hoisted_method_count-1))); 106 | hoisted_method_list->obsolete = NULL; // soothe valgrind - apparently ObjC runtime accesses this value and it shows as uninitialized in valgrind 107 | hoisted_method_list->method_count = hoisted_method_count; 108 | Method hoisted_method = hoisted_method_list->method_list; 109 | 110 | if (!directOriginalMethod) { 111 | bcopy(inheritedOriginalMethod, hoisted_method, sizeof(struct objc_method)); 112 | directOriginalMethod = hoisted_method++; 113 | } 114 | if (!directAlternateMethod) { 115 | bcopy(inheritedAlternateMethod, hoisted_method, sizeof(struct objc_method)); 116 | directAlternateMethod = hoisted_method; 117 | } 118 | class_addMethods(self, hoisted_method_list); 119 | } 120 | 121 | // Swizzle. 122 | IMP temp = directOriginalMethod->method_imp; 123 | directOriginalMethod->method_imp = directAlternateMethod->method_imp; 124 | directAlternateMethod->method_imp = temp; 125 | 126 | return YES; 127 | #endif 128 | } 129 | 130 | + (BOOL)jr_swizzleClassMethod:(SEL)origSel_ withClassMethod:(SEL)altSel_ error:(NSError**)error_ { 131 | return [GetClass((id)self) jr_swizzleMethod:origSel_ withMethod:altSel_ error:error_]; 132 | } 133 | 134 | @end 135 | -------------------------------------------------------------------------------- /MTLogDemoTests/tests/MTLogFilterTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // MTLogFilterTests.m 3 | // 4 | // @author Marin Todorov, http://www.touch-code-magazine.com 5 | // 6 | 7 | // Copyright (c) 2013 Marin Todorov, Underplot ltd. 8 | // This code is distributed under the terms and conditions of the MIT license. 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 11 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | // 14 | // The MIT License in plain English: http://www.touch-code-magazine.com/JSONModel/MITLicense 15 | 16 | #import 17 | 18 | #import "MTLog+Testing.h" 19 | #import "MTLogPluginOutputCatcher.h" 20 | 21 | #import "MTLogFilterTestsHelper.h" 22 | 23 | @interface MTLogFilterTests : XCTestCase 24 | 25 | @property (strong, nonatomic) MTLogFilterTestsHelper* helper; 26 | 27 | @end 28 | 29 | @implementation MTLogFilterTests 30 | 31 | - (void)setUp 32 | { 33 | [super setUp]; 34 | _helper = [[MTLogFilterTestsHelper alloc] init]; 35 | } 36 | 37 | - (void)tearDown 38 | { 39 | _helper = nil; 40 | [super tearDown]; 41 | } 42 | 43 | - (void)testByFileNameAndLine 44 | { 45 | NSLog(@"_prefix:set()"); 46 | 47 | NSString* filterCommand = [NSString stringWithFormat:@"_filter:MTLogFilterTests.m(%d,%d)", __LINE__, __LINE__ + 5]; 48 | 49 | NSLog(@"%@", filterCommand); 50 | 51 | NSLog(@"Message 1"); 52 | 53 | XCTAssertEqualObjects([MTLog lastMessageCatcher].logMessage, @"Message 1", @"Message 1 was not logged"); 54 | 55 | //test for filtered message 56 | 57 | NSLog(@"Message 2"); 58 | XCTAssertNil([MTLog lastMessageCatcher].logMessage, @"Message 2 was logged"); 59 | 60 | //remove 61 | NSLog(@"_remove:%@", filterCommand); 62 | 63 | } 64 | 65 | - (void)testByFilename 66 | { 67 | NSLog(@"_prefix:set()"); 68 | 69 | NSLog(@"Message 1"); 70 | XCTAssertEqualObjects([MTLog lastMessageCatcher].logMessage, @"Message 1", @"Message 1 was not logged"); 71 | 72 | NSLog(@"_filter:MTLogFilterTests.m"); 73 | 74 | //test message from the selected file 75 | NSLog(@"Message 2"); 76 | XCTAssertEqualObjects([MTLog lastMessageCatcher].logMessage, @"Message 2", @"Message 2 was not logged"); 77 | 78 | //test a filtered message 79 | [_helper log:@"Message 3"]; 80 | 81 | XCTAssertNil([MTLog lastMessageCatcher].logMessage, @"Message 3 was logged instead of being filtered"); 82 | 83 | //remove 84 | NSLog(@"_remove:_filter:MTLogFilterTests.m"); 85 | } 86 | 87 | - (void)testByMethodName 88 | { 89 | NSLog(@"_prefix:set()"); 90 | 91 | NSLog(@"_filter:testByMethodName"); 92 | 93 | //allow this method 94 | NSLog(@"Message 1"); 95 | XCTAssertEqualObjects([MTLog lastMessageCatcher].logMessage, @"Message 1", @"Message 1 was not logged"); 96 | 97 | [_helper log:@"Message 2"]; 98 | XCTAssertNil([MTLog lastMessageCatcher].logMessage, @"Message 2 was logged instead of being filtered"); 99 | 100 | //remove 101 | NSLog(@"_remove:_filter:testByMethodName"); 102 | 103 | //allow method from another class 104 | NSLog(@"_filter:log:"); 105 | 106 | NSLog(@"Message 1"); 107 | XCTAssertNil([MTLog lastMessageCatcher].logMessage, @"Message 1 was logged instead of being filtered"); 108 | 109 | //test external method with colon in the name 110 | [_helper log:@"Message 2"]; 111 | XCTAssertEqualObjects([MTLog lastMessageCatcher].logMessage, @"Message 2", @"Message 2 was not logged"); 112 | 113 | //remove 114 | NSLog(@"_remove:_filter:log:"); 115 | 116 | //allow method from another class 117 | NSLog(@"_filter:log:withNumber:"); 118 | 119 | //test external method with 2 colons in the name 120 | [_helper log:@"Message " withNumber:@3]; 121 | XCTAssertEqualObjects([MTLog lastMessageCatcher].logMessage, @"Message 3", @"Message 3 was not logged"); 122 | 123 | //remove 124 | NSLog(@"_remove:_filter:log:withNumber:"); 125 | } 126 | 127 | -(void)testByThis 128 | { 129 | NSLog(@"_prefix:set()"); 130 | 131 | NSLog(@"Message 1"); 132 | XCTAssertEqualObjects([MTLog lastMessageCatcher].logMessage, @"Message 1", @"Message 1 was not logged"); 133 | 134 | NSLog(@"_filter:$this Message 2"); 135 | XCTAssertEqualObjects([MTLog lastMessageCatcher].logMessage, @"Message 2", @"Message 2 was not logged"); 136 | 137 | NSLog(@"Message 3"); 138 | XCTAssertNil([MTLog lastMessageCatcher].logMessage, @"Message 3 was logged instead of being filtered"); 139 | 140 | NSLog(@"_filter:$this Message 4"); 141 | XCTAssertEqualObjects([MTLog lastMessageCatcher].logMessage, @"Message 4", @"Message 4 was not logged"); 142 | 143 | NSLog(@"Message 5"); 144 | XCTAssertNil([MTLog lastMessageCatcher].logMessage, @"Message 5 was logged instead of being filtered"); 145 | 146 | //remove 147 | NSLog(@"_remove:_filter:$this"); 148 | 149 | NSLog(@"Message 6"); 150 | XCTAssertEqualObjects([MTLog lastMessageCatcher].logMessage, @"Message 6", @"Message 6 was not logged"); 151 | 152 | } 153 | 154 | @end 155 | -------------------------------------------------------------------------------- /MTLog/plugins/MTLogPluginRoute.m: -------------------------------------------------------------------------------- 1 | // 2 | // MTLogPluginRoute.m 3 | // 4 | // @author Marin Todorov, http://www.touch-code-magazine.com 5 | // 6 | 7 | // Copyright (c) 2013 Marin Todorov, Underplot ltd. 8 | // This code is distributed under the terms and conditions of the MIT license. 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 11 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | // 14 | // The MIT License in plain English: http://www.touch-code-magazine.com/JSONModel/MITLicense 15 | 16 | #import "MTLogPluginRoute.h" 17 | 18 | @implementation MTLogPluginRoute 19 | { 20 | NSString* _documentsPath; 21 | BOOL _isAppendingToLog; 22 | 23 | NSURL* _urlToPostTo; 24 | NSString* _identifier; 25 | BOOL _isCreatingConnection; 26 | NSMutableArray* _pendingLines; 27 | } 28 | 29 | +(NSRange)expectedNumberOfArgumentsForCommand:(NSString*)command 30 | { 31 | return NSMakeRange(1, 2); 32 | } 33 | 34 | -(void)createLogFile:(NSString*)name 35 | { 36 | if ([name hasPrefix:@"/"]) { 37 | //absolute file path 38 | _logFilePath = name; 39 | 40 | } else { 41 | //relative path - create it inside the documents folder 42 | if (_documentsPath==nil) { 43 | _documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]; 44 | _logFilePath = [_documentsPath stringByAppendingPathComponent: self.args.firstObject]; 45 | } 46 | } 47 | 48 | if (_logFilePath) { 49 | if ([[NSFileManager defaultManager] fileExistsAtPath: _logFilePath]==NO || _isAppendingToLog==NO) { 50 | [@"" writeToFile:_logFilePath atomically:YES encoding:NSUTF8StringEncoding error:nil]; 51 | } 52 | } 53 | } 54 | 55 | -(void)writeToLogFile:(NSString*)message 56 | { 57 | NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath: _logFilePath]; 58 | 59 | [fileHandle seekToEndOfFile]; 60 | [fileHandle writeData:[[NSString stringWithFormat:@"%@\n", message] dataUsingEncoding:NSUTF8StringEncoding]]; 61 | [fileHandle closeFile]; 62 | } 63 | 64 | -(void)postToURL:(NSString*)message forceCreateConnection:(BOOL)shouldForce 65 | { 66 | if (!_urlToPostTo) return; 67 | 68 | static BOOL gotFirstResponseFromURL = NO; 69 | 70 | if (shouldForce==NO && gotFirstResponseFromURL==NO) { 71 | [_pendingLines addObject: message]; 72 | return; 73 | } 74 | 75 | //send the request off in a bg thread 76 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 77 | NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:_urlToPostTo]; 78 | [request setHTTPMethod: @"POST"]; 79 | [request setValue:@"text/plain; charset=utf-8" forHTTPHeaderField:@"Content-Type"]; 80 | 81 | NSString* postMessage = message; 82 | 83 | @synchronized (_pendingLines) { 84 | if (_pendingLines.count>0) { 85 | [_pendingLines addObject: message]; 86 | postMessage = [_pendingLines componentsJoinedByString:@"\n"]; 87 | [_pendingLines removeAllObjects]; 88 | } 89 | } 90 | 91 | NSData *requestBodyData = [postMessage dataUsingEncoding:NSUTF8StringEncoding]; 92 | request.HTTPBody = requestBodyData; 93 | request.timeoutInterval = 10.0; 94 | [request setValue:_identifier forHTTPHeaderField:@"X-MTLog-ID"]; 95 | 96 | NSError* err = nil; 97 | NSHTTPURLResponse * response = nil; 98 | [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&err]; 99 | 100 | if (response.statusCode==404 || err) { 101 | //disable url posting 102 | _urlToPostTo = nil; 103 | } else { 104 | //success 105 | gotFirstResponseFromURL = YES; 106 | if (shouldForce) { 107 | //flush the pending messages 108 | [self postToURL:@"" forceCreateConnection:NO]; 109 | } 110 | } 111 | }); 112 | } 113 | 114 | -(NSString*)preProcessLogMessage:(NSString *)text env:(NSArray *)env 115 | { 116 | if (_logFilePath==nil && [self.value isEqualToString:@"file"]) { 117 | //parse the arguments 118 | if (self.args.count>1 && [@"append" isEqualToString: self.args[1]]) { 119 | _isAppendingToLog = YES; 120 | } 121 | 122 | //create the log file 123 | [self createLogFile: self.args.firstObject]; 124 | 125 | //add the date of the log if appending 126 | if (_isAppendingToLog) { 127 | [self writeToLogFile:[NSString stringWithFormat:@"--- %@ ---- New log entry ----", [NSDate date]]]; 128 | } 129 | } 130 | 131 | if (_urlToPostTo==nil && [self.value isEqualToString:@"url"]) { 132 | if ([self.args.firstObject hasPrefix:@"http"]) { 133 | //url to post to 134 | _urlToPostTo = [NSURL URLWithString: self.args.firstObject]; 135 | _identifier = [[NSUUID UUID] UUIDString]; 136 | _pendingLines = [@[] mutableCopy]; 137 | _isCreatingConnection = YES; 138 | [self postToURL:@"" forceCreateConnection:YES]; 139 | } 140 | } 141 | 142 | return text; 143 | } 144 | 145 | -(void)postProcessLogMessage:(NSString *)text env:(NSArray *)env 146 | { 147 | //save the log message in a file 148 | if (text.length>0 && _logFilePath) { 149 | [self writeToLogFile: text]; 150 | } 151 | 152 | if (text.length>0 && _urlToPostTo) { 153 | [self postToURL:text forceCreateConnection:NO]; 154 | } 155 | } 156 | 157 | @end 158 | -------------------------------------------------------------------------------- /MTLog/MTLogPlugin.h: -------------------------------------------------------------------------------- 1 | // 2 | // MTLogPlugin.h 3 | // 4 | // @author Marin Todorov, http://www.touch-code-magazine.com 5 | // 6 | 7 | // Copyright (c) 2013 Marin Todorov, Underplot ltd. 8 | // This code is distributed under the terms and conditions of the MIT license. 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 11 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | // 14 | // The MIT License in plain English: http://www.touch-code-magazine.com/JSONModel/MITLicense 15 | 16 | #import 17 | 18 | #ifdef TARGET_OS_MAC 19 | #import "NSArray+FirstObject.h" 20 | #endif 21 | 22 | typedef NS_ENUM(NSInteger, MTLogEnvIndexes) { 23 | MTLogFileName = 0, 24 | MTLogClassName, 25 | MTLogMethodName, 26 | MTLogLineNumber, 27 | MTLogEnabledPlugins, 28 | MTLogRegisteredPlugins 29 | }; 30 | 31 | @class MTLog; 32 | 33 | /** 34 | * The MTLogPluginProtocol specifies the optional methods a plugin can implement in order 35 | * to process the output to the console. All methods are in fact optional. 36 | * 37 | * In a custom plugin any or all of these methods can be implemented and will be automatically 38 | * invoked by MTLog at the point of execution they refer to. 39 | */ 40 | @protocol MTLogPluginProtocol 41 | @optional 42 | 43 | /** 44 | * @name Reacting to the plugin being activated 45 | */ 46 | 47 | /** 48 | * Called just before the plugin is added to the list of active plugins. 49 | * @param log is an instance of MTLog 50 | */ 51 | -(void)willEnableForLog:(MTLog*)log; 52 | 53 | /** 54 | * @name Reacting to debug output 55 | */ 56 | 57 | /** 58 | * MTLog passes throug the plugin the log message before printing it out to the console. Here a custom plugin can alter the output 59 | * or react to it somehow, BEFORE the message shows up in the console. 60 | * @param text the log message 61 | * @param env the environment of the log call (lookup README.md) 62 | * @return the modified or unmodified log message 63 | */ 64 | -(NSString*)preProcessLogMessage:(NSString*)text env:(NSArray*)env; 65 | 66 | /** 67 | * MTLog calls this method on each plugn AFTER the message shows up in the console. 68 | * Therefore this method cannot alter anymore the message itself, but it could react to it in other ways. 69 | * @param text the log message 70 | * @param env the environment of the log call (lookup README.md) 71 | */ 72 | -(void)postProcessLogMessage:(NSString*)text env:(NSArray*)env; 73 | 74 | @end 75 | 76 | /** 77 | * MTLogPlugin is an abstract implementation of an MTLog plugin class. 78 | * 79 | * To build a custom plugin you need to subclass MTLogPlugin and implemented at least one method [MTLogPlugin expectedNumberOfArgumentsForCommand:] 80 | * 81 | * You can override the custom initializer method [MTLogPlugin initWithName:value:args:] if you want to customize the class initialization and/or you 82 | * can additionally implement all the methods from the MTLogPluginProtocol 83 | */ 84 | #pragma mark - MTLogPlugin 85 | @interface MTLogPlugin : NSObject 86 | 87 | /** 88 | * @name Plugin information 89 | */ 90 | 91 | /** The name of the plugin command. 92 | * 93 | * I.e. for "_filter:MyClass.m", value equals "filter". Set by [MTLogPlugin initWithName:value:args:] 94 | */ 95 | @property (strong, nonatomic) NSString* name; 96 | 97 | /** The specific call to the plugin command. 98 | * 99 | * I.e. for "_filter:MyClass.m", value equals "MyClass.m". Set by [MTLogPlugin initWithName:value:args:] 100 | */ 101 | @property (strong, nonatomic) NSString* value; 102 | 103 | /** The name of the plugin command. 104 | * 105 | * I.e. for "_filter:MyClass.m(10,20)", args is a 2 item array [10,20]. Set by [MTLogPlugin initWithName:value:args:] 106 | */ 107 | @property (strong, nonatomic) NSArray* args; 108 | 109 | /** 110 | * A string ID to uniquely identify a combination of plugin, command and an argument list. 111 | */ 112 | @property (strong, nonatomic, readonly) NSString* pluginID; 113 | 114 | /** 115 | * @name Custom behaviour 116 | */ 117 | 118 | /** 119 | * Whether the plugin should affect a message immediately following the command. 120 | * 121 | * For example: 122 | *
NSLog(@"_filter:MyClass.m My Message"); //there's a message immediately following a command
123 |  * ...
124 |  * NSLog(@"_filter:MyClass.m");
125 |  * NSLog(@"My other message"); //this message is separate from the command message
126 |  * 
127 | */ 128 | @property (assign, nonatomic) BOOL affectsFirstLogmessage; 129 | 130 | /** 131 | * @name Basic plugin methods 132 | */ 133 | 134 | /** 135 | * Returns the minimum and maximum number of expected arguments for a given command to the plugin. 136 | * 137 | * For a full example how to implement this feature lookup README.md, section "Implementing the new "smilie" command". 138 | * @param command the specific call part to a command. I.e. for "_filter:MyClass.m" command is "MyClass.m" 139 | * @return an NSRange with the minimum and maximum number of arguments expected. If the command can take only X number of arguments return NSMakeRange(X,X) 140 | */ 141 | +(NSRange)expectedNumberOfArgumentsForCommand:(NSString*)command; 142 | 143 | /** 144 | * @name Initialization 145 | */ 146 | 147 | /** 148 | * Creates a new instance of the plugin. Usually you do not call this method and create plugin instances yourself. 149 | * 150 | * When you log a command: 151 | *
152 |  * NSLog(@"_prefix:set($class $method) Log message");
153 |  * 
154 | * MTLog takes care to parse the plugin name ("prefix"), the value ("set") and argument list (1 list item - "$class $method") and create plugin instance and add it ot the plugin chain. Therefore in most cases you would never need to create a plugin instance yourself. 155 | */ 156 | -(instancetype)initWithName:(NSString*)name value:(NSString*)value args:(NSArray*)args; 157 | 158 | @end 159 | -------------------------------------------------------------------------------- /MTLog/MTLog.m: -------------------------------------------------------------------------------- 1 | // 2 | // MTLog.m 3 | // 4 | // @author Marin Todorov, http://www.touch-code-magazine.com 5 | // 6 | 7 | // Copyright (c) 2013 Marin Todorov, Underplot ltd. 8 | // This code is distributed under the terms and conditions of the MIT license. 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 11 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | // 14 | // The MIT License in plain English: http://www.touch-code-magazine.com/JSONModel/MITLicense 15 | 16 | #import "MTLog.h" 17 | #import "MTLogPlugin.h" 18 | 19 | //load all default plugins 20 | #import "MTLogPluginRegister.h" 21 | #import "MTLogPluginRemove.h" 22 | #import "MTLogPluginFilter.h" 23 | #import "MTLogPluginSearch.h" 24 | #import "MTLogPluginRoute.h" 25 | #import "MTLogPluginPrefix.h" 26 | 27 | @interface MTLog() 28 | { 29 | NSMutableDictionary* _registeredPlugins; 30 | NSMutableArray* _pendingPlugins; 31 | } 32 | 33 | @end 34 | 35 | #pragma mark - MTLog 36 | @implementation MTLog 37 | 38 | + (instancetype)sharedInstance { 39 | static id sharedInstance = nil; 40 | 41 | static dispatch_once_t onceToken; 42 | dispatch_once(&onceToken, ^{ 43 | sharedInstance = [[self alloc] init]; 44 | }); 45 | 46 | return sharedInstance; 47 | } 48 | 49 | -(instancetype)init 50 | { 51 | self = [super init]; 52 | if (self) { 53 | //initialize filters 54 | _registeredPlugins = [@{ 55 | @"prefix": [MTLogPluginPrefix class], 56 | @"register": [MTLogPluginRegister class], 57 | @"filter": [MTLogPluginFilter class], 58 | @"remove": [MTLogPluginRemove class], 59 | @"search": [MTLogPluginSearch class], 60 | @"route": [MTLogPluginRoute class] 61 | } mutableCopy]; 62 | _pendingPlugins = [@[] mutableCopy]; 63 | _enabledPlugins = [@[] mutableCopy]; 64 | 65 | //set the default prefix 66 | [_enabledPlugins addObject: 67 | [[MTLogPluginPrefix alloc] initWithName:@"prefix" value:@"set" args:@[@"default"]] 68 | ]; 69 | } 70 | return self; 71 | } 72 | 73 | -(NSString*)description 74 | { 75 | return [NSString stringWithFormat:@"MTLog: \n" 76 | "plugins: %@\n", _enabledPlugins 77 | ]; 78 | } 79 | 80 | +(void)log:(NSString*)fileName method:(NSString*)method lineNr:(NSNumber*)lineNr text:(NSString *)format, ... 81 | { 82 | va_list args; 83 | va_start(args, format); 84 | [[self sharedInstance] log:fileName method:(NSString*)method lineNr:lineNr text:format, args]; 85 | va_end(args); 86 | } 87 | 88 | -(NSString*)parseLogMessage:(NSString*)format 89 | { 90 | __block NSString* result = format; 91 | 92 | __block NSString* name = nil; 93 | __block NSString* value = nil; 94 | __block NSArray* args = nil; 95 | 96 | //find the filter command 97 | NSRegularExpression *regEx = [[NSRegularExpression alloc] initWithPattern:@"^_(\\w+):([^\( ]+)(\\((.+?)?\\))?[ ]?" 98 | options:kNilOptions 99 | error:nil]; 100 | 101 | [regEx enumerateMatchesInString:result options:kNilOptions range:NSMakeRange(0, result.length) 102 | usingBlock:^(NSTextCheckingResult *match, NSMatchingFlags flags, BOOL *stop){ 103 | 104 | //fetch the command 105 | name = [result substringWithRange: [match rangeAtIndex:1]]; 106 | 107 | //fetch the filter value 108 | value = [result substringWithRange: [match rangeAtIndex:2]]; 109 | 110 | NSString* argsString = nil; 111 | 112 | //fetch the filter arguments 113 | if ([match rangeAtIndex:4].location != NSNotFound) { 114 | argsString = [result substringWithRange: [match rangeAtIndex:4]]; 115 | args = [argsString componentsSeparatedByString:@","]; 116 | } 117 | 118 | //remove the filter command from the log message 119 | result = [result stringByReplacingCharactersInRange:match.range withString:@""]; 120 | 121 | *stop = YES; 122 | }]; 123 | 124 | if (name && value) { 125 | 126 | Class pluginClass = _registeredPlugins[name]; 127 | if (pluginClass==nil) { 128 | return [NSString stringWithFormat:@"MTLog: Could not find registered class for plugin '%@'! %@", name, format]; 129 | } 130 | 131 | id plugin = [(MTLogPlugin*)[pluginClass alloc] initWithName:name value:value args:args]; 132 | if (plugin==nil) { 133 | return [NSString stringWithFormat:@"MTLog: Could not create a plugin out of the command '%@(%@)'! %@", name, [args componentsJoinedByString:@","], format]; 134 | } 135 | 136 | if ([(MTLogPlugin*)plugin affectsFirstLogmessage]==NO) { 137 | [_pendingPlugins addObject: plugin]; 138 | } else { 139 | if ([plugin respondsToSelector:@selector(willEnableForLog:)]) { 140 | [plugin performSelector:@selector(willEnableForLog:) withObject:self]; 141 | } 142 | [_enabledPlugins addObject: plugin]; 143 | } 144 | 145 | } 146 | 147 | return result; 148 | } 149 | 150 | -(void)log:(NSString*)fileName method:(NSString*)method lineNr:(NSNumber*)lineNr text:(NSString *)format, ... 151 | { 152 | __block NSString* text = format?format:@""; 153 | 154 | //parse the command out of the log message 155 | if ([text hasPrefix:@"_"]) { 156 | text = [self parseLogMessage:text]; 157 | } 158 | 159 | NSArray* prettyFuncParts; 160 | NSArray* env; 161 | 162 | //build env variables 163 | if (_enabledPlugins.count>0) { 164 | prettyFuncParts = [[method substringWithRange:NSMakeRange(2, method.length-3)] componentsSeparatedByString:@" "]; 165 | env = @[ 166 | fileName, prettyFuncParts[0], prettyFuncParts.lastObject, lineNr, _enabledPlugins, _registeredPlugins 167 | ]; 168 | } 169 | 170 | [_enabledPlugins enumerateObjectsUsingBlock:^(MTLogPlugin* plugin, NSUInteger idx, BOOL *stop) { 171 | if ([plugin respondsToSelector:@selector(preProcessLogMessage:env:)]) { 172 | text = [plugin preProcessLogMessage: text env: env]; 173 | } 174 | NSAssert(text, @""); 175 | }]; 176 | 177 | //print to the console 178 | if (text.length>0) { 179 | va_list args; 180 | va_start(args, format); 181 | #ifdef DEBUG 182 | //NSLogv(text, args); 183 | vprintf([NSString stringWithFormat:@"%@\n", text].UTF8String, args); 184 | #endif 185 | va_end(args); 186 | } 187 | 188 | //post processing 189 | [_enabledPlugins enumerateObjectsUsingBlock:^(MTLogPlugin* plugin, NSUInteger idx, BOOL *stop) { 190 | if ([plugin respondsToSelector:@selector(postProcessLogMessage:env:)]) { 191 | [plugin postProcessLogMessage: text env: env]; 192 | } 193 | }]; 194 | 195 | //merge any pending plugins 196 | if ([_pendingPlugins count]>0) { 197 | for (id plugin in _pendingPlugins) { 198 | if ([plugin respondsToSelector:@selector(willEnableForLog:)]) { 199 | [plugin performSelector:@selector(willEnableForLog:) withObject:self]; 200 | } 201 | [_enabledPlugins addObject: plugin]; 202 | } 203 | 204 | [_pendingPlugins removeAllObjects]; 205 | } 206 | } 207 | 208 | +(void)addPlugin:(id)plugin 209 | { 210 | [[[self sharedInstance] enabledPlugins] addObject: plugin]; 211 | } 212 | 213 | + (void)removePlugin:(id)plugin 214 | { 215 | [[[self sharedInstance] enabledPlugins] removeObject: plugin]; 216 | } 217 | 218 | + (NSArray*)pluginsByName:(NSString*)name 219 | { 220 | NSMutableArray* result = [@[] mutableCopy]; 221 | 222 | for (MTLogPlugin* plugin in [MTLog sharedInstance].enabledPlugins) { 223 | if ([plugin.name isEqualToString: name]) { 224 | [result addObject: plugin]; 225 | } 226 | } 227 | 228 | return [result copy]; 229 | } 230 | 231 | @end 232 | -------------------------------------------------------------------------------- /docs/html/css/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'Lucida Grande',Geneva,Helvetica,Arial,sans-serif; 3 | font-size: 13px; 4 | } 5 | 6 | code { 7 | font-family: Courier, Consolas, monospace; 8 | font-size: 13px; 9 | color: #666; 10 | } 11 | 12 | pre { 13 | font-family: Courier, Consolas, monospace; 14 | font-size: 13px; 15 | line-height: 18px; 16 | tab-interval: 0.5em; 17 | border: 1px solid #C7CFD5; 18 | background-color: #F1F5F9; 19 | color: #666; 20 | padding: 0.3em 1em; 21 | } 22 | 23 | ul { 24 | list-style-type: square; 25 | } 26 | 27 | li { 28 | margin-bottom: 10px; 29 | } 30 | 31 | a, a code { 32 | text-decoration: none; 33 | color: #36C; 34 | } 35 | 36 | a:hover, a:hover code { 37 | text-decoration: underline; 38 | color: #36C; 39 | } 40 | 41 | h2 { 42 | border-bottom: 1px solid #8391A8; 43 | color: #3C4C6C; 44 | font-size: 187%; 45 | font-weight: normal; 46 | margin-top: 1.75em; 47 | padding-bottom: 2px; 48 | } 49 | 50 | table { 51 | margin-bottom: 4em; 52 | border-collapse:collapse; 53 | vertical-align: middle; 54 | } 55 | 56 | td { 57 | border: 1px solid #9BB3CD; 58 | padding: .667em; 59 | font-size: 100%; 60 | } 61 | 62 | th { 63 | border: 1px solid #9BB3CD; 64 | padding: .3em .667em .3em .667em; 65 | background: #93A5BB; 66 | font-size: 103%; 67 | font-weight: bold; 68 | color: white; 69 | text-align: left; 70 | } 71 | 72 | /* @group Common page elements */ 73 | 74 | #top_header { 75 | height: 91px; 76 | left: 0; 77 | min-width: 598px; 78 | position: absolute; 79 | right: 0; 80 | top: 0; 81 | z-index: 900; 82 | } 83 | 84 | #footer { 85 | clear: both; 86 | padding-top: 20px; 87 | text-align: center; 88 | } 89 | 90 | #contents, #overview_contents { 91 | -webkit-overflow-scrolling: touch; 92 | border-top: 1px solid #2B334F; 93 | position: absolute; 94 | top: 91px; 95 | left: 0; 96 | right: 0; 97 | bottom: 0; 98 | overflow-x: hidden; 99 | overflow-y: auto; 100 | padding-left: 2em; 101 | padding-right: 2em; 102 | padding-top: 1em; 103 | min-width: 550px; 104 | } 105 | 106 | #contents.isShowingTOC { 107 | left: 230px; 108 | min-width: 320px; 109 | } 110 | 111 | .copyright { 112 | font-size: 12px; 113 | } 114 | 115 | .generator { 116 | font-size: 11px; 117 | } 118 | 119 | .main-navigation ul li { 120 | display: inline; 121 | margin-left: 15px; 122 | list-style: none; 123 | } 124 | 125 | .navigation-top { 126 | clear: both; 127 | float: right; 128 | } 129 | 130 | .navigation-bottom { 131 | clear: both; 132 | float: right; 133 | margin-top: 20px; 134 | margin-bottom: -10px; 135 | } 136 | 137 | .open > .disclosure { 138 | background-image: url("../img/disclosure_open.png"); 139 | } 140 | 141 | .disclosure { 142 | background: url("../img/disclosure.png") no-repeat scroll 0 0; 143 | } 144 | 145 | .disclosure, .nodisclosure { 146 | display: inline-block; 147 | height: 8px; 148 | margin-right: 5px; 149 | position: relative; 150 | width: 9px; 151 | } 152 | 153 | /* @end */ 154 | 155 | /* @group Header */ 156 | 157 | #top_header #library { 158 | background: url("../img/library_background.png") repeat-x 0 0 #485E78; 159 | background-color: #ccc; 160 | height: 35px; 161 | font-size: 115%; 162 | } 163 | 164 | #top_header #library #libraryTitle { 165 | color: #FFFFFF; 166 | margin-left: 15px; 167 | text-shadow: 0 -1px 0 #485E78; 168 | top: 8px; 169 | position: absolute; 170 | } 171 | 172 | #top_header #library #developerHome { 173 | color: #92979E; 174 | right: 15px; 175 | top: 8px; 176 | position: absolute; 177 | } 178 | 179 | #top_header #library a:hover { 180 | text-decoration: none; 181 | } 182 | 183 | #top_header #title { 184 | background: url("../img/title_background.png") repeat-x 0 0 #8A98A9; 185 | border-bottom: 1px solid #B6B6B6; 186 | height: 25px; 187 | overflow: hidden; 188 | } 189 | 190 | #top_header h1 { 191 | font-size: 115%; 192 | font-weight: normal; 193 | margin: 0; 194 | padding: 3px 0 2px; 195 | text-align: center; 196 | text-shadow: 0 1px 0 #D5D5D5; 197 | white-space: nowrap; 198 | } 199 | 200 | #headerButtons { 201 | background-color: #D8D8D8; 202 | background-image: url("../img/button_bar_background.png"); 203 | border-bottom: 1px solid #EDEDED; 204 | border-top: 1px solid #2B334F; 205 | font-size: 8pt; 206 | height: 28px; 207 | left: 0; 208 | list-style: none outside none; 209 | margin: 0; 210 | overflow: hidden; 211 | padding: 0; 212 | position: absolute; 213 | right: 0; 214 | top: 61px; 215 | } 216 | 217 | #headerButtons li { 218 | background-repeat: no-repeat; 219 | display: inline; 220 | margin-top: 0; 221 | margin-bottom: 0; 222 | padding: 0; 223 | } 224 | 225 | #toc_button button { 226 | border-color: #ACACAC; 227 | border-style: none solid none none; 228 | border-width: 0 1px 0 0; 229 | height: 28px; 230 | margin: 0; 231 | padding-left: 30px; 232 | text-align: left; 233 | width: 230px; 234 | } 235 | 236 | li#jumpto_button { 237 | left: 230px; 238 | margin-left: 0; 239 | position: absolute; 240 | } 241 | 242 | li#jumpto_button select { 243 | height: 22px; 244 | margin: 5px 2px 0 10px; 245 | max-width: 300px; 246 | } 247 | 248 | /* @end */ 249 | 250 | /* @group Table of contents */ 251 | 252 | #tocContainer.isShowingTOC { 253 | border-right: 1px solid #ACACAC; 254 | display: block; 255 | overflow-x: hidden; 256 | overflow-y: auto; 257 | padding: 0; 258 | } 259 | 260 | #tocContainer { 261 | background-color: #E4EBF7; 262 | border-top: 1px solid #2B334F; 263 | bottom: 0; 264 | display: none; 265 | left: 0; 266 | overflow: hidden; 267 | position: absolute; 268 | top: 91px; 269 | width: 229px; 270 | } 271 | 272 | #tocContainer > ul#toc { 273 | font-size: 11px; 274 | margin: 0; 275 | padding: 12px 0 18px; 276 | width: 209px; 277 | -moz-user-select: none; 278 | -webkit-user-select: none; 279 | user-select: none; 280 | } 281 | 282 | #tocContainer > ul#toc > li { 283 | margin: 0; 284 | padding: 0 0 7px 30px; 285 | text-indent: -15px; 286 | } 287 | 288 | #tocContainer > ul#toc > li > .sectionName a { 289 | color: #000000; 290 | font-weight: bold; 291 | } 292 | 293 | #tocContainer > ul#toc > li > .sectionName a:hover { 294 | text-decoration: none; 295 | } 296 | 297 | #tocContainer > ul#toc li.children > ul { 298 | display: none; 299 | height: 0; 300 | } 301 | 302 | #tocContainer > ul#toc > li > ul { 303 | margin: 0; 304 | padding: 0; 305 | } 306 | 307 | #tocContainer > ul#toc > li > ul, ul#toc > li > ul > li { 308 | margin-left: 0; 309 | margin-bottom: 0; 310 | padding-left: 15px; 311 | } 312 | 313 | #tocContainer > ul#toc > li ul { 314 | list-style: none; 315 | margin-right: 0; 316 | padding-right: 0; 317 | } 318 | 319 | #tocContainer > ul#toc li.children.open > ul { 320 | display: block; 321 | height: auto; 322 | margin-left: -15px; 323 | padding-left: 0; 324 | } 325 | 326 | #tocContainer > ul#toc > li > ul, ul#toc > li > ul > li { 327 | margin-left: 0; 328 | padding-left: 15px; 329 | } 330 | 331 | #tocContainer li ul li { 332 | margin-top: 0.583em; 333 | overflow: hidden; 334 | text-overflow: ellipsis; 335 | white-space: nowrap; 336 | } 337 | 338 | #tocContainer li ul li span.sectionName { 339 | white-space: normal; 340 | } 341 | 342 | #tocContainer > ul#toc > li > ul > li > .sectionName a { 343 | font-weight: bold; 344 | } 345 | 346 | #tocContainer > ul#toc > li > ul a { 347 | color: #4F4F4F; 348 | } 349 | 350 | /* @end */ 351 | 352 | /* @group Index formatting */ 353 | 354 | .index-title { 355 | font-size: 13px; 356 | font-weight: normal; 357 | } 358 | 359 | .index-column { 360 | float: left; 361 | width: 30%; 362 | min-width: 200px; 363 | font-size: 11px; 364 | } 365 | 366 | .index-column ul { 367 | margin: 8px 0 0 0; 368 | padding: 0; 369 | list-style: none; 370 | } 371 | 372 | .index-column ul li { 373 | margin: 0 0 3px 0; 374 | padding: 0; 375 | } 376 | 377 | .hierarchy-column { 378 | min-width: 400px; 379 | } 380 | 381 | .hierarchy-column ul { 382 | margin: 3px 0 0 15px; 383 | } 384 | 385 | .hierarchy-column ul li { 386 | list-style-type: square; 387 | } 388 | 389 | /* @end */ 390 | 391 | /* @group Common formatting elements */ 392 | 393 | .title { 394 | font-weight: normal; 395 | font-size: 215%; 396 | margin-top:0; 397 | } 398 | 399 | .subtitle { 400 | font-weight: normal; 401 | font-size: 180%; 402 | color: #3C4C6C; 403 | border-bottom: 1px solid #5088C5; 404 | } 405 | 406 | .subsubtitle { 407 | font-weight: normal; 408 | font-size: 145%; 409 | height: 0.7em; 410 | } 411 | 412 | .note { 413 | border: 1px solid #5088C5; 414 | background-color: white; 415 | margin: 1.667em 0 1.75em 0; 416 | padding: 0 .667em .083em .750em; 417 | } 418 | 419 | .warning { 420 | border: 1px solid #5088C5; 421 | background-color: #F0F3F7; 422 | margin-bottom: 0.5em; 423 | padding: 0.3em 0.8em; 424 | } 425 | 426 | .bug { 427 | border: 1px solid #000; 428 | background-color: #ffffcc; 429 | margin-bottom: 0.5em; 430 | padding: 0.3em 0.8em; 431 | } 432 | 433 | .deprecated { 434 | color: #F60425; 435 | } 436 | 437 | /* @end */ 438 | 439 | /* @group Common layout */ 440 | 441 | .section { 442 | margin-top: 3em; 443 | } 444 | 445 | /* @end */ 446 | 447 | /* @group Object specification section */ 448 | 449 | .section-specification { 450 | margin-left: 2.5em; 451 | margin-right: 2.5em; 452 | font-size: 12px; 453 | } 454 | 455 | .section-specification table { 456 | margin-bottom: 0em; 457 | border-top: 1px solid #d6e0e5; 458 | } 459 | 460 | .section-specification td { 461 | vertical-align: top; 462 | border-bottom: 1px solid #d6e0e5; 463 | border-left-width: 0px; 464 | border-right-width: 0px; 465 | border-top-width: 0px; 466 | padding: .6em; 467 | } 468 | 469 | .section-specification .specification-title { 470 | font-weight: bold; 471 | } 472 | 473 | /* @end */ 474 | 475 | /* @group Tasks section */ 476 | 477 | .task-list { 478 | list-style-type: none; 479 | padding-left: 0px; 480 | } 481 | 482 | .task-list li { 483 | margin-bottom: 3px; 484 | } 485 | 486 | .task-item-suffix { 487 | color: #996; 488 | font-size: 12px; 489 | font-style: italic; 490 | margin-left: 0.5em; 491 | } 492 | 493 | span.tooltip span.tooltip { 494 | font-size: 1.0em; 495 | display: none; 496 | padding: 0.3em; 497 | border: 1px solid #aaa; 498 | background-color: #fdfec8; 499 | color: #000; 500 | text-align: left; 501 | } 502 | 503 | span.tooltip:hover span.tooltip { 504 | display: block; 505 | position: absolute; 506 | margin-left: 2em; 507 | } 508 | 509 | /* @end */ 510 | 511 | /* @group Method section */ 512 | 513 | .section-method { 514 | margin-top: 2.3em; 515 | } 516 | 517 | .method-title { 518 | margin-bottom: 1.5em; 519 | } 520 | 521 | .method-subtitle { 522 | margin-top: 0.7em; 523 | margin-bottom: 0.2em; 524 | } 525 | 526 | .method-subsection p { 527 | margin-top: 0.4em; 528 | margin-bottom: 0.8em; 529 | } 530 | 531 | .method-declaration { 532 | margin-top:1.182em; 533 | margin-bottom:.909em; 534 | } 535 | 536 | .method-declaration code { 537 | font:14px Courier, Consolas, monospace; 538 | color:#000; 539 | } 540 | 541 | .declaration { 542 | color: #000; 543 | } 544 | 545 | .argument-def { 546 | margin-top: 0.3em; 547 | margin-bottom: 0.3em; 548 | } 549 | 550 | .argument-def dd { 551 | margin-left: 1.25em; 552 | } 553 | 554 | .see-also-section ul { 555 | list-style-type: none; 556 | padding-left: 0px; 557 | margin-top: 0; 558 | } 559 | 560 | .see-also-section li { 561 | margin-bottom: 3px; 562 | } 563 | 564 | .declared-in-ref { 565 | color: #666; 566 | } 567 | 568 | #tocContainer.hideInXcode { 569 | display: none; 570 | border: 0px solid black; 571 | } 572 | 573 | #top_header.hideInXcode { 574 | display: none; 575 | } 576 | 577 | #contents.hideInXcode { 578 | border: 0px solid black; 579 | top: 0px; 580 | left: 0px; 581 | } 582 | 583 | /* @end */ 584 | 585 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | MTLog: an NSLog replacement for coders! 2 | ===== 3 | 4 | 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. 5 | 6 | ------------------------------------ 7 | Including MTLog in your project 8 | ==================================== 9 | 10 | 11 | #### Get it either as: 1) source files 12 | 13 | 1. Download the MTLog repository as a [zip file](https://github.com/icanzilb/MTLog/archive/master.zip) or clone it 14 | 2. Copy the MTLog sub-folder into your Xcode project 15 | 3. `#import "MTLog.h"` in the project **.pch** file to have MTLog working throughout all your classes 16 | 17 | 18 | #### or 2) via Cocoa pods 19 | 20 | 1. In your project's **Podfile** add the MTLog pod: 21 | 22 | ```ruby 23 | pod 'MTLog' 24 | ``` 25 | 26 | 2. From the Terminal, inside the project folder, run: 27 | 28 | ```ruby 29 | pod install 30 | ``` 31 | 32 | 3. `#import "MTLog.h"` in the project **.pch** file to have MTLog working throughout all your classes 33 | 34 | If you want to read more about CocoaPods, have a look at [this short tutorial](http://www.raywenderlich.com/12139/introduction-to-cocoapods). 35 | 36 | ------------------------------------ 37 | Using MTLog 38 | ==================================== 39 | 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. 40 | 41 | It's best if you just read through the examples below. 42 | 43 | **NB!** Always make sure you've imported MTLog.h 44 | 45 | ## Prefix command 46 | ------- 47 | 48 | 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): 49 | 50 | ```ObjC 51 | NSLog(@"My log message!"); 52 | ``` 53 | 54 |
 55 | MyClass.m:20 > My log message!
 56 | 
57 | 58 | ##### _prefix:set(…) 59 | 60 | If you want a different prefix than the default you can use the prefix command to change it: 61 | 62 | ```ObjC 63 | NSLog(@"_prefix:set($method $line) My log message!"); 64 | NSLog(@"My second message."); 65 | ``` 66 | 67 |
 68 | myMethod 20 > My log message!
 69 | myMethod 21 > My second message!
 70 | 
71 | 72 | **NB!**: _prefix:set(…) sets the prefix for the current log message AND for all the messages you log afterwards. 73 | 74 | ##### prefix variables 75 | 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: 76 | 77 | 78 | 79 | 82 | 85 | 86 | 89 | 92 | 93 | 96 | 99 | 100 | 103 | 106 | 107 | 110 | 113 | 114 |
80 | $file 81 | 83 | The name of the current file 84 |
87 | $class 88 | 90 | The current class name 91 |
94 | $method 95 | 97 | The current method name 98 |
101 | $line 102 | 104 | The current line number 105 |
108 | $counter 109 | 111 | A counter starting from 1. It increases every time you log from the same line in the same file. 112 |
115 | 116 | ##### _prefix:set() 117 | 118 | If you don't want a special prefix to your log messages, just call set with no arguments: 119 | 120 | ```ObjC 121 | NSLog(@"_prefix:set()"); 122 | ``` 123 | 124 | ##### _prefix:use(…) 125 | 126 | If you want to change the prefix for the current log message ONLY you use _prefix:use(…) 127 | 128 | ```ObjC 129 | NSLog(@"My log message!"); 130 | NSLog(@"_prefix:use($method $line) My log message!"); 131 | NSLog(@"My second message."); 132 | ``` 133 |
134 | MyClass.m:20 > My log message!
135 | myMethod 21 > My log message!
136 | MyClass.m:22 > My second message!
137 | 
138 | 139 | ##### _prefix:set(default) 140 | 141 | 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: 142 | 143 | ```ObjC 144 | NSLog(@"_prefix:set(default)"); 145 | ``` 146 | 147 | 148 | ## Filter command 149 | ---- 150 | 151 | 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. 152 | 153 | You can use filter in several different ways. 154 | 155 | ##### _filter:MyClass.m 156 | 157 | If you pass a file name to _filter after this log statement you will see only the output from this file. 158 | 159 | ```ObjC 160 | YourClass.m 161 | ... 162 | +(void)message 163 | { 164 | NSLog(@"Your message!"); 165 | } 166 | 167 | MyClass.m 168 | ... 169 | NSLog(@"_filter:MyClass.m"); 170 | NSLog(@"My message"); 171 | [YourClass message]; 172 | ``` 173 | 174 |
175 | MyClass.m:20 > My message
176 | // "Your message" will be filtered and not show up in the output console
177 | 
178 | 179 | ##### _filter:MyClass.m(10,200) 180 | 181 | 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. 182 | 183 | ```ObjC 184 | NSLog(@"_filter:MyClass.m(10,100)"); 185 | ``` 186 | 187 | This command will allow only log statements from the lines between line 10 and line 100 to generate output to the console. 188 | 189 | ##### _filter:myMethod:withString: 190 | 191 | You can also pass a method signature to the _filter command - then it will allow output only from the method matching this signature. 192 | 193 | ##### combined _filter commands 194 | 195 | _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. 196 | 197 | ```ObjC 198 | NSLog(@"_filter:MyClass.m"); 199 | NSLog(@"_filter:init"); 200 | ``` 201 | 202 | After this NSLog statements only logging from "init" in MyClass.m will generate output. 203 | 204 | ##### _filter:$this 205 | 206 | 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). 207 | 208 | ```ObjC 209 | NSLog(@"begin counting"); 210 | for (int i=1;i<=3;i++) { 211 | NSLog(@"_filter:$this i=%i", i); 212 | NSLog(@"more loop output"); 213 | } 214 | NSLog(@"_filter:$this loop ended"); 215 | NSLog(@"last log message"); 216 | ``` 217 | 218 | 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: 219 | 220 |
221 | MyClass.m:20 > begin counting
222 | MyClass.m:22 > i=1
223 | MyClass.m:22 > i=2
224 | MyClass.m:22 > i=3
225 | MyClass.m:25 > loop ended
226 | 
227 | 228 | ## Remove command 229 | ---- 230 | 231 | Since commands like filter affect all log messages afterwards you need a way to also deactivate them. 232 | 233 | ##### _remove:<command to remove> 234 | 235 | 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: 236 | 237 | ```ObjC 238 | NSLog(@"_filter:$this Message 1"); 239 | NSLog(@"Message 2"); 240 | NSLog(@"_remove:_filter:$this"); //remove: + the command including arguments 241 | NSLog(@"Message 3"); 242 | } 243 | ``` 244 | 245 | Since the remove command will remove the filter you will also see the last log in the output console. 246 | 247 |
248 | MyClass.m:20 > Message 1
249 | MyClass.m:23 > Message 3
250 | 
251 | 252 | In general all commands affect all logs after they get executed. Therefore you need to use _remove to deactivate them. 253 | 254 | Some commands like _prefix:use(…) are one-shot commands therefore you don't need to use _remove for them. 255 | 256 | ## Route command 257 | ---- 258 | 259 | 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. 260 | 261 | ##### _route:file(log.txt) 262 | 263 | 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. 264 | 265 | ##### _route:file(/<full path>/log.txt) 266 | 267 | If you pass in a full blown path to a file, the command will create (or overwrite) it at the location you specify. 268 | 269 | ##### _route:file(log.txt,append) 270 | 271 | 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). 272 | 273 | ```ObjC 274 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 275 | { 276 | NSLog(@"message one"); 277 | NSLog(@"message two"); 278 | return YES; 279 | } 280 | ``` 281 | 282 |
283 | --- 2013-09-14 08:36:21 +0000 ---- New log entry ----
284 | [application:didFinishLaunchingWithOptions: 19] message one
285 | [application:didFinishLaunchingWithOptions: 20] message two
286 | 
287 | --- 2013-09-14 08:42:01 +0000 ---- New log entry ----
288 | [application:didFinishLaunchingWithOptions: 19] message one
289 | [application:didFinishLaunchingWithOptions: 20] message two
290 | 
291 | 
292 | 293 | ## Search command 294 | ---- 295 | 296 | 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. 297 | 298 | ##### _search:clear(<search term>) 299 | 300 | 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. 301 | 302 | ```ObjC 303 | NSLog(@"_search:clear(vacation)"); 304 | … 305 | NSLog(@"Yupee!"); 306 | NSLog(@"I'm going on vacation!"); 307 | ``` 308 | 309 |
310 | MyClass.m:20 > Yupee!
311 | 
312 | … //19 more empty lines
313 | MyClass.m:22 > I'm going on vacation!
314 | 
315 | 316 | ##### _search:throw(<search term>) 317 | 318 | 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. 319 | 320 | ## Register command 321 | ---- 322 | 323 | 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! 324 | 325 | ##### _register:command(CommandClassName) 326 | 327 | You need to subclass MTLogPlugin and override some or all of the following methods (in the order of invocation): 328 | 329 | ```ObjC 330 | // to tell MTLog how many arguments you are expecting 331 | // for example return [0,2] - for zero, one or two args 332 | +(NSRange)expectedNumberOfArgumentsForCommand:(NSString*)command; 333 | 334 | // the custom plugin init- name contains the command, value is the part after the colon 335 | // args is an array of the arguments (not trimmed of spaces) 336 | -(instancetype)initWithName:(NSString*)name value:(NSString*)value args:(NSArray*)args; 337 | 338 | // the method is invoked just before the plugin is added to enabled plugins list 339 | -(void)willEnableForLog:(MTLog*)log; 340 | 341 | // text contains the log message, you can alter it in any way and return it 342 | // look up "env" below 343 | -(NSString*)preProcessLogMessage:(NSString*)text env:(NSArray*)env; 344 | 345 | // use this method to react to a command after the message is logged 346 | // look up "env" below 347 | -(void)postProcessLogMessage:(NSString*)text env:(NSArray*)env; 348 | ``` 349 | 350 | The "env" array contains as follows: 351 | 352 | 1. File name 353 | 2. Class name 354 | 3. Method name 355 | 4. Line number 356 | 5. List of all enabled plugins 357 | 6. List of all registered plugins 358 | 359 | You can alter the enabled and registered plugins lists if you need to. 360 | 361 | #### Implementing the new "smilie" command 362 | 363 | Let's see the code for a new command called "smilie" that adds a smilie to each log message. 364 | 365 | **PluginSmilie.h** 366 | 367 | ```ObjC 368 | #import "MTLogPlugin.h" 369 | @interface PluginSmilie : MTLogPlugin 370 | @end 371 | ``` 372 | 373 | **PluginSmilie.m** 374 | 375 | ```ObjC 376 | #import "PluginSmilie.h" 377 | @implementation PluginSmilie 378 | +(NSRange)expectedNumberOfArgumentsForCommand:(NSString*)command 379 | { 380 | if ([command isEqualToString:@"extended"]) { 381 | return NSMakeRange(1, 1); 382 | } 383 | return NSMakeRange(0, 0); 384 | } 385 | 386 | -(NSString*)preProcessLogMessage:(NSString *)text env:(NSArray *)env 387 | { 388 | if ([self.value isEqualToString:@"classic"]) { 389 | return [NSString stringWithFormat:@"%@ :-)", text]; 390 | } 391 | 392 | if ([self.value isEqualToString:@"extended"]) { 393 | return [NSString stringWithFormat:@"%@ :%@)", text, self.args.firstObject]; 394 | } 395 | 396 | return text; 397 | } 398 | 399 | @end 400 | ``` 401 | 402 | That's a complete, working command for MTLog. 403 | 404 | 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. 405 | 406 | Then in `preProcessLogMessage:env:` you add a smilie to the text argument, which alters the message being logged. 407 | 408 | Let's see how you can use the new command in your code: 409 | 410 | ```ObjC 411 | #import "PluginSmilie.h" 412 | NSLog(@"_register:smilie(PluginSmilie)"); 413 | … 414 | NSLog(@"Message One"); 415 | NSLog(@"_smilie:classic Message Two"); 416 | NSLog(@"Message Three"); 417 | 418 | NSLog(@"_remove:_smilie:classic"); 419 | NSLog(@"Message Four"); 420 | 421 | NSLog(@"_smilie:extended(-{)"); 422 | NSLog(@"A hipster message"); 423 | ``` 424 | 425 |
426 | MyClass.m:20 > Message One
427 | MyClass.m:21 > Message Two :-)
428 | MyClass.m:22 > Message Three :-)
429 | MyClass.m:25 > Message Four
430 | MyClass.m:28 > A hipster message :-{)
431 | 
432 | 433 | ------------------------------------ 434 | Misc 435 | ==================================== 436 | Author: [Marin Todorov](http://www.touch-code-magazine.com/about/) 437 | 438 | ------- 439 | #### License 440 | 441 | This code is distributed under the terms and conditions of the MIT license. 442 | 443 | ------- 444 | #### Contribution guidelines 445 | 446 | **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. 447 | -------------------------------------------------------------------------------- /docs/html/Protocols/MTLogPluginProtocol.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MTLogPluginProtocol Protocol Reference 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 18 | 19 | 22 | 58 |
59 | 96 |
97 |
98 | 99 | 105 | 110 |
111 | 112 |
113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 |
Conforms toNSObject
Declared inMTLogPlugin.h
121 | 122 | 123 | 124 | 125 |
126 | 127 |

Overview

128 |

The MTLogPluginProtocol specifies the optional methods a plugin can implement in order 129 | to process the output to the console. All methods are in fact optional.

130 | 131 |

In a custom plugin any or all of these methods can be implemented and will be automatically 132 | invoked by MTLog at the point of execution they refer to.

133 |
134 | 135 | 136 | 137 | 138 | 139 |
140 | 141 |

Tasks

142 | 143 | 144 | 145 |

Reacting to the plugin being activated

146 | 147 |
    148 |
  • 149 | 150 | – willEnableForLog: 151 |

    Called just before the plugin is added to the list of active plugins.

    152 |
    153 | 154 | 155 |
  • 156 |
157 | 158 | 159 | 160 |

Reacting to debug output

161 | 162 |
    163 |
  • 164 | 165 | – preProcessLogMessage:env: 166 |

    MTLog passes throug the plugin the log message before printing it out to the console. Here a custom plugin can alter the output 167 | or react to it somehow, BEFORE the message shows up in the console.

    168 |
    169 | 170 | 171 |
  • 172 | 173 | – postProcessLogMessage:env: 174 |

    MTLog calls this method on each plugn AFTER the message shows up in the console. 175 | Therefore this method cannot alter anymore the message itself, but it could react to it in other ways.

    176 |
    177 | 178 | 179 |
  • 180 |
181 | 182 |
183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 |
193 | 194 |

Instance Methods

195 | 196 |
197 | 198 |

postProcessLogMessage:env:

199 | 200 | 201 | 202 |
203 |

MTLog calls this method on each plugn AFTER the message shows up in the console. 204 | Therefore this method cannot alter anymore the message itself, but it could react to it in other ways.

205 |
206 | 207 | 208 | 209 |
- (void)postProcessLogMessage:(NSString *)text env:(NSArray *)env
210 | 211 | 212 | 213 |
214 |

Parameters

215 | 216 |
217 |
text
218 |

the log message

219 |
220 | 221 |
222 |
env
223 |

the environment of the log call (lookup README.md)

224 |
225 | 226 |
227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 |
241 |

Declared In

242 | MTLogPlugin.h
243 |
244 | 245 | 246 |
247 | 248 |
249 | 250 |

preProcessLogMessage:env:

251 | 252 | 253 | 254 |
255 |

MTLog passes throug the plugin the log message before printing it out to the console. Here a custom plugin can alter the output 256 | or react to it somehow, BEFORE the message shows up in the console.

257 |
258 | 259 | 260 | 261 |
- (NSString *)preProcessLogMessage:(NSString *)text env:(NSArray *)env
262 | 263 | 264 | 265 |
266 |

Parameters

267 | 268 |
269 |
text
270 |

the log message

271 |
272 | 273 |
274 |
env
275 |

the environment of the log call (lookup README.md)

276 |
277 | 278 |
279 | 280 | 281 | 282 |
283 |

Return Value

284 |

the modified or unmodified log message

285 |
286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 |
298 |

Declared In

299 | MTLogPlugin.h
300 |
301 | 302 | 303 |
304 | 305 |
306 | 307 |

willEnableForLog:

308 | 309 | 310 | 311 |
312 |

Called just before the plugin is added to the list of active plugins.

313 |
314 | 315 | 316 | 317 |
- (void)willEnableForLog:(MTLog *)log
318 | 319 | 320 | 321 |
322 |

Parameters

323 | 324 |
325 |
log
326 |

is an instance of MTLog

327 |
328 | 329 |
330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 |
344 |

Declared In

345 | MTLogPlugin.h
346 |
347 | 348 | 349 |
350 | 351 |
352 | 353 | 354 |
355 | 361 | 370 |
371 |
372 | 463 | 464 | -------------------------------------------------------------------------------- /MTLogDemo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | -------------------------------------------------------------------------------- /docs/html/Classes/MTLog.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MTLog Class Reference 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 18 | 19 | 22 | 65 |
66 | 111 |
112 |
113 | 114 | 120 | 125 |
126 | 127 |
128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 |
Inherits fromNSObject
Declared inMTLog.h
MTLog.m
136 | 137 | 138 | 139 | 140 |
141 | 142 |

Overview

143 |

In most cases you would not need to interact with the MTLog class directly. Just keep using NSLog() as normal and 144 | have a look at the list of available commands you can use from within the text you supply to NSLog.

145 | 146 |

When you call NSLog(@“… message …”) it calls behind the scene [MTLog log:method:lineNr:text:]. 147 | This is also the way you execute commands, for example:

148 | 149 |
150 | NSLog(@"_filter:MyClass.m(10,125) Log message");
151 | 
152 | 153 | 154 |

Consult README.md for a list of the available commands and examples. 155 | This documentation is provided only for the ones who would like to write classes for their own log commands.

156 |
157 | 158 | 159 | 160 | 161 | 162 |
163 | 164 |

Tasks

165 | 166 | 167 | 168 |

Logging

169 | 170 |
    171 |
  • 172 | 173 | + log:method:lineNr:text: 174 |

    Don’t call this method directly, rather just use NSLog() directly and it will then 175 | call this method passing the correct paramters.

    176 |
    177 | 178 | 179 |
  • 180 |
181 | 182 | 183 | 184 |

Manipulating plugins

185 | 186 |
    187 |
  • 188 | 189 | + addPlugin: 190 |

    Adds an initialized plugin to the plugin chain

    191 |
    192 | 193 | 194 |
  • 195 | 196 | + removePlugin: 197 |

    Removes a plugin instance of the plugin chain

    198 |
    199 | 200 | 201 |
  • 202 | 203 | + pluginsByName: 204 |

    Gets a list of all the plugins in the plugin chain that are of certain class (i.e. you can grab all filter plugins in the chain)

    205 |
    206 | 207 | 208 |
  • 209 | 210 |   enabledPlugins 211 |

    A list of all currently active plugins

    212 |
    213 | property 214 | 215 |
  • 216 |
217 | 218 |
219 | 220 | 221 | 222 | 223 | 224 |
225 | 226 |

Properties

227 | 228 |
229 | 230 |

enabledPlugins

231 | 232 | 233 | 234 |
235 |

A list of all currently active plugins

236 |
237 | 238 | 239 | 240 |
@property (strong, readonly, nonatomic) NSMutableArray *enabledPlugins
241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 |
257 |

Declared In

258 | MTLog.h
259 |
260 | 261 | 262 |
263 | 264 |
265 | 266 | 267 | 268 |
269 | 270 |

Class Methods

271 | 272 |
273 | 274 |

addPlugin:

275 | 276 | 277 | 278 |
279 |

Adds an initialized plugin to the plugin chain

280 |
281 | 282 | 283 | 284 |
+ (void)addPlugin:(id)plugin
285 | 286 | 287 | 288 |
289 |

Parameters

290 | 291 |
292 |
plugin
293 |

an instance of a subclass of MTLogPlugin

294 |
295 | 296 |
297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 |
311 |

Declared In

312 | MTLog.h
313 |
314 | 315 | 316 |
317 | 318 |
319 | 320 |

log:method:lineNr:text:

321 | 322 | 323 | 324 |
325 |

Don’t call this method directly, rather just use NSLog() directly and it will then 326 | call this method passing the correct paramters.

327 |
328 | 329 | 330 | 331 |
+ (void)log:(NSString *)fileName method:(NSString *)method lineNr:(NSNumber *)lineNr text:(NSString *)format, ...
332 | 333 | 334 | 335 |
336 |

Parameters

337 | 338 |
339 |
fileName
340 |

the file the message is logged from

341 |
342 | 343 |
344 |
method
345 |

the method name where the message is logged from

346 |
347 | 348 |
349 |
lineNr
350 |

the number of the line in the source code file

351 |
352 | 353 |
354 |
format
355 |

the message to log, followed by a arbitrary number of arguments

356 |
357 | 358 |
359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 |
373 |

Declared In

374 | MTLog.h
375 |
376 | 377 | 378 |
379 | 380 |
381 | 382 |

pluginsByName:

383 | 384 | 385 | 386 |
387 |

Gets a list of all the plugins in the plugin chain that are of certain class (i.e. you can grab all filter plugins in the chain)

388 |
389 | 390 | 391 | 392 |
+ (NSArray *)pluginsByName:(NSString *)name
393 | 394 | 395 | 396 |
397 |

Parameters

398 | 399 |
400 |
name
401 |

the class name of the plugins to fetch

402 |
403 | 404 |
405 | 406 | 407 | 408 |
409 |

Return Value

410 |

NSArray list of currently active plugins matching the provided class name

411 |
412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 |
424 |

Declared In

425 | MTLog.h
426 |
427 | 428 | 429 |
430 | 431 |
432 | 433 |

removePlugin:

434 | 435 | 436 | 437 |
438 |

Removes a plugin instance of the plugin chain

439 |
440 | 441 | 442 | 443 |
+ (void)removePlugin:(id)plugin
444 | 445 | 446 | 447 |
448 |

Parameters

449 | 450 |
451 |
plugin
452 |

an instance of a subclass of MTLogPlugin

453 |
454 | 455 |
456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 |
470 |

Declared In

471 | MTLog.h
472 |
473 | 474 | 475 |
476 | 477 |
478 | 479 | 480 | 481 | 482 |
483 | 489 | 498 |
499 |
500 | 591 | 592 | -------------------------------------------------------------------------------- /docs/html/Classes/MTLogPlugin.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MTLogPlugin Class Reference 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 18 | 19 | 22 | 72 |
73 | 130 |
131 |
132 | 133 | 139 | 144 |
145 | 146 |
147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 |
Inherits fromNSObject
Conforms toMTLogPluginProtocol
Declared inMTLogPlugin.h
MTLogPlugin.m
158 | 159 | 160 | 161 | 162 |
163 | 164 |

Overview

165 |

MTLogPlugin is an abstract implementation of an MTLog plugin class.

166 | 167 |

To build a custom plugin you need to subclass MTLogPlugin and implemented at least one method [MTLogPlugin expectedNumberOfArgumentsForCommand:]

168 | 169 |

You can override the custom initializer method [MTLogPlugin initWithName:value:args:] if you want to customize the class initialization and/or you 170 | can additionally implement all the methods from the MTLogPluginProtocol

171 |
172 | 173 | 174 | 175 | 176 | 177 |
178 | 179 |

Tasks

180 | 181 | 182 | 183 |

Plugin information

184 | 185 |
    186 |
  • 187 | 188 |   name 189 |

    The name of the plugin command.

    190 |
    191 | property 192 | 193 |
  • 194 | 195 |   value 196 |

    The specific call to the plugin command.

    197 |
    198 | property 199 | 200 |
  • 201 | 202 |   args 203 |

    The name of the plugin command.

    204 |
    205 | property 206 | 207 |
  • 208 | 209 |   pluginID 210 |

    A string ID to uniquely identify a combination of plugin, command and an argument list.

    211 |
    212 | property 213 | 214 |
  • 215 |
216 | 217 | 218 | 219 |

Custom behaviour

220 | 221 |
    222 |
  • 223 | 224 |   affectsFirstLogmessage 225 |

    Whether the plugin should affect a message immediately following the command.

    226 |
    227 | property 228 | 229 |
  • 230 |
231 | 232 | 233 | 234 |

Basic plugin methods

235 | 236 | 246 | 247 | 248 | 249 |

Initialization

250 | 251 |
    252 |
  • 253 | 254 | – initWithName:value:args: 255 |

    Creates a new instance of the plugin. Usually you do not call this method and create plugin instances yourself.

    256 |
    257 | 258 | 259 |
  • 260 |
261 | 262 |
263 | 264 | 265 | 266 | 267 | 268 |
269 | 270 |

Properties

271 | 272 |
273 | 274 |

affectsFirstLogmessage

275 | 276 | 277 | 278 |
279 |

Whether the plugin should affect a message immediately following the command.

280 |
281 | 282 | 283 | 284 |
@property (assign, nonatomic) BOOL affectsFirstLogmessage
285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 |
295 |

Discussion

296 |

For example:

297 | 298 |
NSLog(@"_filter:MyClass.m My Message"); //there's a message immediately following a command
299 | ...
300 | NSLog(@"_filter:MyClass.m");
301 | NSLog(@"My other message"); //this message is separate from the command message
302 | 
303 | 304 |
305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 |
313 |

Declared In

314 | MTLogPlugin.h
315 |
316 | 317 | 318 |
319 | 320 |
321 | 322 |

args

323 | 324 | 325 | 326 |
327 |

The name of the plugin command.

328 |
329 | 330 | 331 | 332 |
@property (strong, nonatomic) NSArray *args
333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 |
343 |

Discussion

344 |

I.e. for “_filter:MyClass.m(10,20)”, args is a 2 item array [10,20]. Set by [MTLogPlugin initWithName:value:args:]

345 |
346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 |
354 |

Declared In

355 | MTLogPlugin.h
356 |
357 | 358 | 359 |
360 | 361 |
362 | 363 |

name

364 | 365 | 366 | 367 |
368 |

The name of the plugin command.

369 |
370 | 371 | 372 | 373 |
@property (strong, nonatomic) NSString *name
374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 |
384 |

Discussion

385 |

I.e. for “_filter:MyClass.m”, value equals “filter”. Set by [MTLogPlugin initWithName:value:args:]

386 |
387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 |
395 |

Declared In

396 | MTLogPlugin.h
397 |
398 | 399 | 400 |
401 | 402 |
403 | 404 |

pluginID

405 | 406 | 407 | 408 |
409 |

A string ID to uniquely identify a combination of plugin, command and an argument list.

410 |
411 | 412 | 413 | 414 |
@property (strong, nonatomic, readonly) NSString *pluginID
415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 |
431 |

Declared In

432 | MTLogPlugin.h
433 |
434 | 435 | 436 |
437 | 438 |
439 | 440 |

value

441 | 442 | 443 | 444 |
445 |

The specific call to the plugin command.

446 |
447 | 448 | 449 | 450 |
@property (strong, nonatomic) NSString *value
451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 |
461 |

Discussion

462 |

I.e. for “_filter:MyClass.m”, value equals “MyClass.m”. Set by [MTLogPlugin initWithName:value:args:]

463 |
464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 |
472 |

Declared In

473 | MTLogPlugin.h
474 |
475 | 476 | 477 |
478 | 479 |
480 | 481 | 482 | 483 |
484 | 485 |

Class Methods

486 | 487 |
488 | 489 |

expectedNumberOfArgumentsForCommand:

490 | 491 | 492 | 493 |
494 |

Returns the minimum and maximum number of expected arguments for a given command to the plugin.

495 |
496 | 497 | 498 | 499 |
+ (NSRange)expectedNumberOfArgumentsForCommand:(NSString *)command
500 | 501 | 502 | 503 |
504 |

Parameters

505 | 506 |
507 |
command
508 |

the specific call part to a command. I.e. for “_filter:MyClass.m” command is “MyClass.m”

509 |
510 | 511 |
512 | 513 | 514 | 515 |
516 |

Return Value

517 |

an NSRange with the minimum and maximum number of arguments expected. If the command can take only X number of arguments return NSMakeRange(X,X)

518 |
519 | 520 | 521 | 522 | 523 | 524 |
525 |

Discussion

526 |

For a full example how to implement this feature lookup README.md, section “Implementing the new "smilie” command".

527 |
528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 |
536 |

Declared In

537 | MTLogPlugin.h
538 |
539 | 540 | 541 |
542 | 543 |
544 | 545 | 546 | 547 |
548 | 549 |

Instance Methods

550 | 551 |
552 | 553 |

initWithName:value:args:

554 | 555 | 556 | 557 |
558 |

Creates a new instance of the plugin. Usually you do not call this method and create plugin instances yourself.

559 |
560 | 561 | 562 | 563 |
- (instancetype)initWithName:(NSString *)name value:(NSString *)value args:(NSArray *)args
564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 |
574 |

Discussion

575 |

When you log a command:

576 | 577 |
578 | NSLog(@"_prefix:set($class $method) Log message");
579 | 
580 | 581 | 582 |

MTLog takes care to parse the plugin name (“prefix”), the value (“set”) and argument list (1 list item – “$class $method”) and create plugin instance and add it ot the plugin chain. Therefore in most cases you would never need to create a plugin instance yourself.

583 |
584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 |
592 |

Declared In

593 | MTLogPlugin.h
594 |
595 | 596 | 597 |
598 | 599 |
600 | 601 | 602 |
603 | 609 | 618 |
619 |
620 | 711 | 712 | --------------------------------------------------------------------------------