DDTTYLogger.m 54 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481
  1. // Software License Agreement (BSD License)
  2. //
  3. // Copyright (c) 2010-2015, Deusty, LLC
  4. // All rights reserved.
  5. //
  6. // Redistribution and use of this software in source and binary forms,
  7. // with or without modification, are permitted provided that the following conditions are met:
  8. //
  9. // * Redistributions of source code must retain the above copyright notice,
  10. // this list of conditions and the following disclaimer.
  11. //
  12. // * Neither the name of Deusty nor the names of its contributors may be used
  13. // to endorse or promote products derived from this software without specific
  14. // prior written permission of Deusty, LLC.
  15. #import "DDTTYLogger.h"
  16. #import <unistd.h>
  17. #import <sys/uio.h>
  18. #if !__has_feature(objc_arc)
  19. #error This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
  20. #endif
  21. // We probably shouldn't be using DDLog() statements within the DDLog implementation.
  22. // But we still want to leave our log statements for any future debugging,
  23. // and to allow other developers to trace the implementation (which is a great learning tool).
  24. //
  25. // So we use primitive logging macros around NSLog.
  26. // We maintain the NS prefix on the macros to be explicit about the fact that we're using NSLog.
  27. #define LOG_LEVEL 2
  28. #define NSLogError(frmt, ...) do{ if(LOG_LEVEL >= 1) NSLog((frmt), ##__VA_ARGS__); } while(0)
  29. #define NSLogWarn(frmt, ...) do{ if(LOG_LEVEL >= 2) NSLog((frmt), ##__VA_ARGS__); } while(0)
  30. #define NSLogInfo(frmt, ...) do{ if(LOG_LEVEL >= 3) NSLog((frmt), ##__VA_ARGS__); } while(0)
  31. #define NSLogDebug(frmt, ...) do{ if(LOG_LEVEL >= 4) NSLog((frmt), ##__VA_ARGS__); } while(0)
  32. #define NSLogVerbose(frmt, ...) do{ if(LOG_LEVEL >= 5) NSLog((frmt), ##__VA_ARGS__); } while(0)
  33. // Xcode does NOT natively support colors in the Xcode debugging console.
  34. // You'll need to install the XcodeColors plugin to see colors in the Xcode console.
  35. // https://github.com/robbiehanson/XcodeColors
  36. //
  37. // The following is documentation from the XcodeColors project:
  38. //
  39. //
  40. // How to apply color formatting to your log statements:
  41. //
  42. // To set the foreground color:
  43. // Insert the ESCAPE_SEQ into your string, followed by "fg124,12,255;" where r=124, g=12, b=255.
  44. //
  45. // To set the background color:
  46. // Insert the ESCAPE_SEQ into your string, followed by "bg12,24,36;" where r=12, g=24, b=36.
  47. //
  48. // To reset the foreground color (to default value):
  49. // Insert the ESCAPE_SEQ into your string, followed by "fg;"
  50. //
  51. // To reset the background color (to default value):
  52. // Insert the ESCAPE_SEQ into your string, followed by "bg;"
  53. //
  54. // To reset the foreground and background color (to default values) in one operation:
  55. // Insert the ESCAPE_SEQ into your string, followed by ";"
  56. #define XCODE_COLORS_ESCAPE_SEQ "\033["
  57. #define XCODE_COLORS_RESET_FG XCODE_COLORS_ESCAPE_SEQ "fg;" // Clear any foreground color
  58. #define XCODE_COLORS_RESET_BG XCODE_COLORS_ESCAPE_SEQ "bg;" // Clear any background color
  59. #define XCODE_COLORS_RESET XCODE_COLORS_ESCAPE_SEQ ";" // Clear any foreground or background color
  60. // If running in a shell, not all RGB colors will be supported.
  61. // In this case we automatically map to the closest available color.
  62. // In order to provide this mapping, we have a hard-coded set of the standard RGB values available in the shell.
  63. // However, not every shell is the same, and Apple likes to think different even when it comes to shell colors.
  64. //
  65. // Map to standard Terminal.app colors (1), or
  66. // map to standard xterm colors (0).
  67. #define MAP_TO_TERMINAL_APP_COLORS 1
  68. @interface DDTTYLoggerColorProfile : NSObject {
  69. @public
  70. DDLogFlag mask;
  71. NSInteger context;
  72. uint8_t fg_r;
  73. uint8_t fg_g;
  74. uint8_t fg_b;
  75. uint8_t bg_r;
  76. uint8_t bg_g;
  77. uint8_t bg_b;
  78. NSUInteger fgCodeIndex;
  79. NSString *fgCodeRaw;
  80. NSUInteger bgCodeIndex;
  81. NSString *bgCodeRaw;
  82. char fgCode[24];
  83. size_t fgCodeLen;
  84. char bgCode[24];
  85. size_t bgCodeLen;
  86. char resetCode[8];
  87. size_t resetCodeLen;
  88. }
  89. - (instancetype)initWithForegroundColor:(DDColor *)fgColor backgroundColor:(DDColor *)bgColor flag:(DDLogFlag)mask context:(NSInteger)ctxt;
  90. @end
  91. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  92. #pragma mark -
  93. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  94. @interface DDTTYLogger () {
  95. NSUInteger _calendarUnitFlags;
  96. NSString *_appName;
  97. char *_app;
  98. size_t _appLen;
  99. NSString *_processID;
  100. char *_pid;
  101. size_t _pidLen;
  102. BOOL _colorsEnabled;
  103. NSMutableArray *_colorProfilesArray;
  104. NSMutableDictionary *_colorProfilesDict;
  105. }
  106. @end
  107. @implementation DDTTYLogger
  108. static BOOL isaColorTTY;
  109. static BOOL isaColor256TTY;
  110. static BOOL isaXcodeColorTTY;
  111. static NSArray *codes_fg = nil;
  112. static NSArray *codes_bg = nil;
  113. static NSArray *colors = nil;
  114. static DDTTYLogger *sharedInstance;
  115. /**
  116. * Initializes the colors array, as well as the codes_fg and codes_bg arrays, for 16 color mode.
  117. *
  118. * This method is used when the application is running from within a shell that only supports 16 color mode.
  119. * This method is not invoked if the application is running within Xcode, or via normal UI app launch.
  120. **/
  121. + (void)initialize_colors_16 {
  122. if (codes_fg || codes_bg || colors) {
  123. return;
  124. }
  125. NSMutableArray *m_codes_fg = [NSMutableArray arrayWithCapacity:16];
  126. NSMutableArray *m_codes_bg = [NSMutableArray arrayWithCapacity:16];
  127. NSMutableArray *m_colors = [NSMutableArray arrayWithCapacity:16];
  128. // In a standard shell only 16 colors are supported.
  129. //
  130. // More information about ansi escape codes can be found online.
  131. // http://en.wikipedia.org/wiki/ANSI_escape_code
  132. [m_codes_fg addObject:@"30m"]; // normal - black
  133. [m_codes_fg addObject:@"31m"]; // normal - red
  134. [m_codes_fg addObject:@"32m"]; // normal - green
  135. [m_codes_fg addObject:@"33m"]; // normal - yellow
  136. [m_codes_fg addObject:@"34m"]; // normal - blue
  137. [m_codes_fg addObject:@"35m"]; // normal - magenta
  138. [m_codes_fg addObject:@"36m"]; // normal - cyan
  139. [m_codes_fg addObject:@"37m"]; // normal - gray
  140. [m_codes_fg addObject:@"1;30m"]; // bright - darkgray
  141. [m_codes_fg addObject:@"1;31m"]; // bright - red
  142. [m_codes_fg addObject:@"1;32m"]; // bright - green
  143. [m_codes_fg addObject:@"1;33m"]; // bright - yellow
  144. [m_codes_fg addObject:@"1;34m"]; // bright - blue
  145. [m_codes_fg addObject:@"1;35m"]; // bright - magenta
  146. [m_codes_fg addObject:@"1;36m"]; // bright - cyan
  147. [m_codes_fg addObject:@"1;37m"]; // bright - white
  148. [m_codes_bg addObject:@"40m"]; // normal - black
  149. [m_codes_bg addObject:@"41m"]; // normal - red
  150. [m_codes_bg addObject:@"42m"]; // normal - green
  151. [m_codes_bg addObject:@"43m"]; // normal - yellow
  152. [m_codes_bg addObject:@"44m"]; // normal - blue
  153. [m_codes_bg addObject:@"45m"]; // normal - magenta
  154. [m_codes_bg addObject:@"46m"]; // normal - cyan
  155. [m_codes_bg addObject:@"47m"]; // normal - gray
  156. [m_codes_bg addObject:@"1;40m"]; // bright - darkgray
  157. [m_codes_bg addObject:@"1;41m"]; // bright - red
  158. [m_codes_bg addObject:@"1;42m"]; // bright - green
  159. [m_codes_bg addObject:@"1;43m"]; // bright - yellow
  160. [m_codes_bg addObject:@"1;44m"]; // bright - blue
  161. [m_codes_bg addObject:@"1;45m"]; // bright - magenta
  162. [m_codes_bg addObject:@"1;46m"]; // bright - cyan
  163. [m_codes_bg addObject:@"1;47m"]; // bright - white
  164. #if MAP_TO_TERMINAL_APP_COLORS
  165. // Standard Terminal.app colors:
  166. //
  167. // These are the default colors used by Apple's Terminal.app.
  168. [m_colors addObject:DDMakeColor( 0, 0, 0)]; // normal - black
  169. [m_colors addObject:DDMakeColor(194, 54, 33)]; // normal - red
  170. [m_colors addObject:DDMakeColor( 37, 188, 36)]; // normal - green
  171. [m_colors addObject:DDMakeColor(173, 173, 39)]; // normal - yellow
  172. [m_colors addObject:DDMakeColor( 73, 46, 225)]; // normal - blue
  173. [m_colors addObject:DDMakeColor(211, 56, 211)]; // normal - magenta
  174. [m_colors addObject:DDMakeColor( 51, 187, 200)]; // normal - cyan
  175. [m_colors addObject:DDMakeColor(203, 204, 205)]; // normal - gray
  176. [m_colors addObject:DDMakeColor(129, 131, 131)]; // bright - darkgray
  177. [m_colors addObject:DDMakeColor(252, 57, 31)]; // bright - red
  178. [m_colors addObject:DDMakeColor( 49, 231, 34)]; // bright - green
  179. [m_colors addObject:DDMakeColor(234, 236, 35)]; // bright - yellow
  180. [m_colors addObject:DDMakeColor( 88, 51, 255)]; // bright - blue
  181. [m_colors addObject:DDMakeColor(249, 53, 248)]; // bright - magenta
  182. [m_colors addObject:DDMakeColor( 20, 240, 240)]; // bright - cyan
  183. [m_colors addObject:DDMakeColor(233, 235, 235)]; // bright - white
  184. #else /* if MAP_TO_TERMINAL_APP_COLORS */
  185. // Standard xterm colors:
  186. //
  187. // These are the default colors used by most xterm shells.
  188. [m_colors addObject:DDMakeColor( 0, 0, 0)]; // normal - black
  189. [m_colors addObject:DDMakeColor(205, 0, 0)]; // normal - red
  190. [m_colors addObject:DDMakeColor( 0, 205, 0)]; // normal - green
  191. [m_colors addObject:DDMakeColor(205, 205, 0)]; // normal - yellow
  192. [m_colors addObject:DDMakeColor( 0, 0, 238)]; // normal - blue
  193. [m_colors addObject:DDMakeColor(205, 0, 205)]; // normal - magenta
  194. [m_colors addObject:DDMakeColor( 0, 205, 205)]; // normal - cyan
  195. [m_colors addObject:DDMakeColor(229, 229, 229)]; // normal - gray
  196. [m_colors addObject:DDMakeColor(127, 127, 127)]; // bright - darkgray
  197. [m_colors addObject:DDMakeColor(255, 0, 0)]; // bright - red
  198. [m_colors addObject:DDMakeColor( 0, 255, 0)]; // bright - green
  199. [m_colors addObject:DDMakeColor(255, 255, 0)]; // bright - yellow
  200. [m_colors addObject:DDMakeColor( 92, 92, 255)]; // bright - blue
  201. [m_colors addObject:DDMakeColor(255, 0, 255)]; // bright - magenta
  202. [m_colors addObject:DDMakeColor( 0, 255, 255)]; // bright - cyan
  203. [m_colors addObject:DDMakeColor(255, 255, 255)]; // bright - white
  204. #endif /* if MAP_TO_TERMINAL_APP_COLORS */
  205. codes_fg = [m_codes_fg copy];
  206. codes_bg = [m_codes_bg copy];
  207. colors = [m_colors copy];
  208. NSAssert([codes_fg count] == [codes_bg count], @"Invalid colors/codes array(s)");
  209. NSAssert([codes_fg count] == [colors count], @"Invalid colors/codes array(s)");
  210. }
  211. /**
  212. * Initializes the colors array, as well as the codes_fg and codes_bg arrays, for 256 color mode.
  213. *
  214. * This method is used when the application is running from within a shell that supports 256 color mode.
  215. * This method is not invoked if the application is running within Xcode, or via normal UI app launch.
  216. **/
  217. + (void)initialize_colors_256 {
  218. if (codes_fg || codes_bg || colors) {
  219. return;
  220. }
  221. NSMutableArray *m_codes_fg = [NSMutableArray arrayWithCapacity:(256 - 16)];
  222. NSMutableArray *m_codes_bg = [NSMutableArray arrayWithCapacity:(256 - 16)];
  223. NSMutableArray *m_colors = [NSMutableArray arrayWithCapacity:(256 - 16)];
  224. #if MAP_TO_TERMINAL_APP_COLORS
  225. // Standard Terminal.app colors:
  226. //
  227. // These are the colors the Terminal.app uses in xterm-256color mode.
  228. // In this mode, the terminal supports 256 different colors, specified by 256 color codes.
  229. //
  230. // The first 16 color codes map to the original 16 color codes supported by the earlier xterm-color mode.
  231. // These are actually configurable, and thus we ignore them for the purposes of mapping,
  232. // as we can't rely on them being constant. They are largely duplicated anyway.
  233. //
  234. // The next 216 color codes are designed to run the spectrum, with several shades of every color.
  235. // While the color codes are standardized, the actual RGB values for each color code is not.
  236. // Apple's Terminal.app uses different RGB values from that of a standard xterm.
  237. // Apple's choices in colors are designed to be a little nicer on the eyes.
  238. //
  239. // The last 24 color codes represent a grayscale.
  240. //
  241. // Unfortunately, unlike the standard xterm color chart,
  242. // Apple's RGB values cannot be calculated using a simple formula (at least not that I know of).
  243. // Also, I don't know of any ways to programmatically query the shell for the RGB values.
  244. // So this big giant color chart had to be made by hand.
  245. //
  246. // More information about ansi escape codes can be found online.
  247. // http://en.wikipedia.org/wiki/ANSI_escape_code
  248. // Colors
  249. [m_colors addObject:DDMakeColor( 47, 49, 49)];
  250. [m_colors addObject:DDMakeColor( 60, 42, 144)];
  251. [m_colors addObject:DDMakeColor( 66, 44, 183)];
  252. [m_colors addObject:DDMakeColor( 73, 46, 222)];
  253. [m_colors addObject:DDMakeColor( 81, 50, 253)];
  254. [m_colors addObject:DDMakeColor( 88, 51, 255)];
  255. [m_colors addObject:DDMakeColor( 42, 128, 37)];
  256. [m_colors addObject:DDMakeColor( 42, 127, 128)];
  257. [m_colors addObject:DDMakeColor( 44, 126, 169)];
  258. [m_colors addObject:DDMakeColor( 56, 125, 209)];
  259. [m_colors addObject:DDMakeColor( 59, 124, 245)];
  260. [m_colors addObject:DDMakeColor( 66, 123, 255)];
  261. [m_colors addObject:DDMakeColor( 51, 163, 41)];
  262. [m_colors addObject:DDMakeColor( 39, 162, 121)];
  263. [m_colors addObject:DDMakeColor( 42, 161, 162)];
  264. [m_colors addObject:DDMakeColor( 53, 160, 202)];
  265. [m_colors addObject:DDMakeColor( 45, 159, 240)];
  266. [m_colors addObject:DDMakeColor( 58, 158, 255)];
  267. [m_colors addObject:DDMakeColor( 31, 196, 37)];
  268. [m_colors addObject:DDMakeColor( 48, 196, 115)];
  269. [m_colors addObject:DDMakeColor( 39, 195, 155)];
  270. [m_colors addObject:DDMakeColor( 49, 195, 195)];
  271. [m_colors addObject:DDMakeColor( 32, 194, 235)];
  272. [m_colors addObject:DDMakeColor( 53, 193, 255)];
  273. [m_colors addObject:DDMakeColor( 50, 229, 35)];
  274. [m_colors addObject:DDMakeColor( 40, 229, 109)];
  275. [m_colors addObject:DDMakeColor( 27, 229, 149)];
  276. [m_colors addObject:DDMakeColor( 49, 228, 189)];
  277. [m_colors addObject:DDMakeColor( 33, 228, 228)];
  278. [m_colors addObject:DDMakeColor( 53, 227, 255)];
  279. [m_colors addObject:DDMakeColor( 27, 254, 30)];
  280. [m_colors addObject:DDMakeColor( 30, 254, 103)];
  281. [m_colors addObject:DDMakeColor( 45, 254, 143)];
  282. [m_colors addObject:DDMakeColor( 38, 253, 182)];
  283. [m_colors addObject:DDMakeColor( 38, 253, 222)];
  284. [m_colors addObject:DDMakeColor( 42, 253, 252)];
  285. [m_colors addObject:DDMakeColor(140, 48, 40)];
  286. [m_colors addObject:DDMakeColor(136, 51, 136)];
  287. [m_colors addObject:DDMakeColor(135, 52, 177)];
  288. [m_colors addObject:DDMakeColor(134, 52, 217)];
  289. [m_colors addObject:DDMakeColor(135, 56, 248)];
  290. [m_colors addObject:DDMakeColor(134, 53, 255)];
  291. [m_colors addObject:DDMakeColor(125, 125, 38)];
  292. [m_colors addObject:DDMakeColor(124, 125, 125)];
  293. [m_colors addObject:DDMakeColor(122, 124, 166)];
  294. [m_colors addObject:DDMakeColor(123, 124, 207)];
  295. [m_colors addObject:DDMakeColor(123, 122, 247)];
  296. [m_colors addObject:DDMakeColor(124, 121, 255)];
  297. [m_colors addObject:DDMakeColor(119, 160, 35)];
  298. [m_colors addObject:DDMakeColor(117, 160, 120)];
  299. [m_colors addObject:DDMakeColor(117, 160, 160)];
  300. [m_colors addObject:DDMakeColor(115, 159, 201)];
  301. [m_colors addObject:DDMakeColor(116, 158, 240)];
  302. [m_colors addObject:DDMakeColor(117, 157, 255)];
  303. [m_colors addObject:DDMakeColor(113, 195, 39)];
  304. [m_colors addObject:DDMakeColor(110, 194, 114)];
  305. [m_colors addObject:DDMakeColor(111, 194, 154)];
  306. [m_colors addObject:DDMakeColor(108, 194, 194)];
  307. [m_colors addObject:DDMakeColor(109, 193, 234)];
  308. [m_colors addObject:DDMakeColor(108, 192, 255)];
  309. [m_colors addObject:DDMakeColor(105, 228, 30)];
  310. [m_colors addObject:DDMakeColor(103, 228, 109)];
  311. [m_colors addObject:DDMakeColor(105, 228, 148)];
  312. [m_colors addObject:DDMakeColor(100, 227, 188)];
  313. [m_colors addObject:DDMakeColor( 99, 227, 227)];
  314. [m_colors addObject:DDMakeColor( 99, 226, 253)];
  315. [m_colors addObject:DDMakeColor( 92, 253, 34)];
  316. [m_colors addObject:DDMakeColor( 96, 253, 103)];
  317. [m_colors addObject:DDMakeColor( 97, 253, 142)];
  318. [m_colors addObject:DDMakeColor( 88, 253, 182)];
  319. [m_colors addObject:DDMakeColor( 93, 253, 221)];
  320. [m_colors addObject:DDMakeColor( 88, 254, 251)];
  321. [m_colors addObject:DDMakeColor(177, 53, 34)];
  322. [m_colors addObject:DDMakeColor(174, 54, 131)];
  323. [m_colors addObject:DDMakeColor(172, 55, 172)];
  324. [m_colors addObject:DDMakeColor(171, 57, 213)];
  325. [m_colors addObject:DDMakeColor(170, 55, 249)];
  326. [m_colors addObject:DDMakeColor(170, 57, 255)];
  327. [m_colors addObject:DDMakeColor(165, 123, 37)];
  328. [m_colors addObject:DDMakeColor(163, 123, 123)];
  329. [m_colors addObject:DDMakeColor(162, 123, 164)];
  330. [m_colors addObject:DDMakeColor(161, 122, 205)];
  331. [m_colors addObject:DDMakeColor(161, 121, 241)];
  332. [m_colors addObject:DDMakeColor(161, 121, 255)];
  333. [m_colors addObject:DDMakeColor(158, 159, 33)];
  334. [m_colors addObject:DDMakeColor(157, 158, 118)];
  335. [m_colors addObject:DDMakeColor(157, 158, 159)];
  336. [m_colors addObject:DDMakeColor(155, 157, 199)];
  337. [m_colors addObject:DDMakeColor(155, 157, 239)];
  338. [m_colors addObject:DDMakeColor(154, 156, 255)];
  339. [m_colors addObject:DDMakeColor(152, 193, 40)];
  340. [m_colors addObject:DDMakeColor(151, 193, 113)];
  341. [m_colors addObject:DDMakeColor(150, 193, 153)];
  342. [m_colors addObject:DDMakeColor(150, 192, 193)];
  343. [m_colors addObject:DDMakeColor(148, 192, 232)];
  344. [m_colors addObject:DDMakeColor(149, 191, 253)];
  345. [m_colors addObject:DDMakeColor(146, 227, 28)];
  346. [m_colors addObject:DDMakeColor(144, 227, 108)];
  347. [m_colors addObject:DDMakeColor(144, 227, 147)];
  348. [m_colors addObject:DDMakeColor(144, 227, 187)];
  349. [m_colors addObject:DDMakeColor(142, 226, 227)];
  350. [m_colors addObject:DDMakeColor(142, 225, 252)];
  351. [m_colors addObject:DDMakeColor(138, 253, 36)];
  352. [m_colors addObject:DDMakeColor(137, 253, 102)];
  353. [m_colors addObject:DDMakeColor(136, 253, 141)];
  354. [m_colors addObject:DDMakeColor(138, 254, 181)];
  355. [m_colors addObject:DDMakeColor(135, 255, 220)];
  356. [m_colors addObject:DDMakeColor(133, 255, 250)];
  357. [m_colors addObject:DDMakeColor(214, 57, 30)];
  358. [m_colors addObject:DDMakeColor(211, 59, 126)];
  359. [m_colors addObject:DDMakeColor(209, 57, 168)];
  360. [m_colors addObject:DDMakeColor(208, 55, 208)];
  361. [m_colors addObject:DDMakeColor(207, 58, 247)];
  362. [m_colors addObject:DDMakeColor(206, 61, 255)];
  363. [m_colors addObject:DDMakeColor(204, 121, 32)];
  364. [m_colors addObject:DDMakeColor(202, 121, 121)];
  365. [m_colors addObject:DDMakeColor(201, 121, 161)];
  366. [m_colors addObject:DDMakeColor(200, 120, 202)];
  367. [m_colors addObject:DDMakeColor(200, 120, 241)];
  368. [m_colors addObject:DDMakeColor(198, 119, 255)];
  369. [m_colors addObject:DDMakeColor(198, 157, 37)];
  370. [m_colors addObject:DDMakeColor(196, 157, 116)];
  371. [m_colors addObject:DDMakeColor(195, 156, 157)];
  372. [m_colors addObject:DDMakeColor(195, 156, 197)];
  373. [m_colors addObject:DDMakeColor(194, 155, 236)];
  374. [m_colors addObject:DDMakeColor(193, 155, 255)];
  375. [m_colors addObject:DDMakeColor(191, 192, 36)];
  376. [m_colors addObject:DDMakeColor(190, 191, 112)];
  377. [m_colors addObject:DDMakeColor(189, 191, 152)];
  378. [m_colors addObject:DDMakeColor(189, 191, 191)];
  379. [m_colors addObject:DDMakeColor(188, 190, 230)];
  380. [m_colors addObject:DDMakeColor(187, 190, 253)];
  381. [m_colors addObject:DDMakeColor(185, 226, 28)];
  382. [m_colors addObject:DDMakeColor(184, 226, 106)];
  383. [m_colors addObject:DDMakeColor(183, 225, 146)];
  384. [m_colors addObject:DDMakeColor(183, 225, 186)];
  385. [m_colors addObject:DDMakeColor(182, 225, 225)];
  386. [m_colors addObject:DDMakeColor(181, 224, 252)];
  387. [m_colors addObject:DDMakeColor(178, 255, 35)];
  388. [m_colors addObject:DDMakeColor(178, 255, 101)];
  389. [m_colors addObject:DDMakeColor(177, 254, 141)];
  390. [m_colors addObject:DDMakeColor(176, 254, 180)];
  391. [m_colors addObject:DDMakeColor(176, 254, 220)];
  392. [m_colors addObject:DDMakeColor(175, 253, 249)];
  393. [m_colors addObject:DDMakeColor(247, 56, 30)];
  394. [m_colors addObject:DDMakeColor(245, 57, 122)];
  395. [m_colors addObject:DDMakeColor(243, 59, 163)];
  396. [m_colors addObject:DDMakeColor(244, 60, 204)];
  397. [m_colors addObject:DDMakeColor(242, 59, 241)];
  398. [m_colors addObject:DDMakeColor(240, 55, 255)];
  399. [m_colors addObject:DDMakeColor(241, 119, 36)];
  400. [m_colors addObject:DDMakeColor(240, 120, 118)];
  401. [m_colors addObject:DDMakeColor(238, 119, 158)];
  402. [m_colors addObject:DDMakeColor(237, 119, 199)];
  403. [m_colors addObject:DDMakeColor(237, 118, 238)];
  404. [m_colors addObject:DDMakeColor(236, 118, 255)];
  405. [m_colors addObject:DDMakeColor(235, 154, 36)];
  406. [m_colors addObject:DDMakeColor(235, 154, 114)];
  407. [m_colors addObject:DDMakeColor(234, 154, 154)];
  408. [m_colors addObject:DDMakeColor(232, 154, 194)];
  409. [m_colors addObject:DDMakeColor(232, 153, 234)];
  410. [m_colors addObject:DDMakeColor(232, 153, 255)];
  411. [m_colors addObject:DDMakeColor(230, 190, 30)];
  412. [m_colors addObject:DDMakeColor(229, 189, 110)];
  413. [m_colors addObject:DDMakeColor(228, 189, 150)];
  414. [m_colors addObject:DDMakeColor(227, 189, 190)];
  415. [m_colors addObject:DDMakeColor(227, 189, 229)];
  416. [m_colors addObject:DDMakeColor(226, 188, 255)];
  417. [m_colors addObject:DDMakeColor(224, 224, 35)];
  418. [m_colors addObject:DDMakeColor(223, 224, 105)];
  419. [m_colors addObject:DDMakeColor(222, 224, 144)];
  420. [m_colors addObject:DDMakeColor(222, 223, 184)];
  421. [m_colors addObject:DDMakeColor(222, 223, 224)];
  422. [m_colors addObject:DDMakeColor(220, 223, 253)];
  423. [m_colors addObject:DDMakeColor(217, 253, 28)];
  424. [m_colors addObject:DDMakeColor(217, 253, 99)];
  425. [m_colors addObject:DDMakeColor(216, 252, 139)];
  426. [m_colors addObject:DDMakeColor(216, 252, 179)];
  427. [m_colors addObject:DDMakeColor(215, 252, 218)];
  428. [m_colors addObject:DDMakeColor(215, 251, 250)];
  429. [m_colors addObject:DDMakeColor(255, 61, 30)];
  430. [m_colors addObject:DDMakeColor(255, 60, 118)];
  431. [m_colors addObject:DDMakeColor(255, 58, 159)];
  432. [m_colors addObject:DDMakeColor(255, 56, 199)];
  433. [m_colors addObject:DDMakeColor(255, 55, 238)];
  434. [m_colors addObject:DDMakeColor(255, 59, 255)];
  435. [m_colors addObject:DDMakeColor(255, 117, 29)];
  436. [m_colors addObject:DDMakeColor(255, 117, 115)];
  437. [m_colors addObject:DDMakeColor(255, 117, 155)];
  438. [m_colors addObject:DDMakeColor(255, 117, 195)];
  439. [m_colors addObject:DDMakeColor(255, 116, 235)];
  440. [m_colors addObject:DDMakeColor(254, 116, 255)];
  441. [m_colors addObject:DDMakeColor(255, 152, 27)];
  442. [m_colors addObject:DDMakeColor(255, 152, 111)];
  443. [m_colors addObject:DDMakeColor(254, 152, 152)];
  444. [m_colors addObject:DDMakeColor(255, 152, 192)];
  445. [m_colors addObject:DDMakeColor(254, 151, 231)];
  446. [m_colors addObject:DDMakeColor(253, 151, 253)];
  447. [m_colors addObject:DDMakeColor(255, 187, 33)];
  448. [m_colors addObject:DDMakeColor(253, 187, 107)];
  449. [m_colors addObject:DDMakeColor(252, 187, 148)];
  450. [m_colors addObject:DDMakeColor(253, 187, 187)];
  451. [m_colors addObject:DDMakeColor(254, 187, 227)];
  452. [m_colors addObject:DDMakeColor(252, 186, 252)];
  453. [m_colors addObject:DDMakeColor(252, 222, 34)];
  454. [m_colors addObject:DDMakeColor(251, 222, 103)];
  455. [m_colors addObject:DDMakeColor(251, 222, 143)];
  456. [m_colors addObject:DDMakeColor(250, 222, 182)];
  457. [m_colors addObject:DDMakeColor(251, 221, 222)];
  458. [m_colors addObject:DDMakeColor(252, 221, 252)];
  459. [m_colors addObject:DDMakeColor(251, 252, 15)];
  460. [m_colors addObject:DDMakeColor(251, 252, 97)];
  461. [m_colors addObject:DDMakeColor(249, 252, 137)];
  462. [m_colors addObject:DDMakeColor(247, 252, 177)];
  463. [m_colors addObject:DDMakeColor(247, 253, 217)];
  464. [m_colors addObject:DDMakeColor(254, 255, 255)];
  465. // Grayscale
  466. [m_colors addObject:DDMakeColor( 52, 53, 53)];
  467. [m_colors addObject:DDMakeColor( 57, 58, 59)];
  468. [m_colors addObject:DDMakeColor( 66, 67, 67)];
  469. [m_colors addObject:DDMakeColor( 75, 76, 76)];
  470. [m_colors addObject:DDMakeColor( 83, 85, 85)];
  471. [m_colors addObject:DDMakeColor( 92, 93, 94)];
  472. [m_colors addObject:DDMakeColor(101, 102, 102)];
  473. [m_colors addObject:DDMakeColor(109, 111, 111)];
  474. [m_colors addObject:DDMakeColor(118, 119, 119)];
  475. [m_colors addObject:DDMakeColor(126, 127, 128)];
  476. [m_colors addObject:DDMakeColor(134, 136, 136)];
  477. [m_colors addObject:DDMakeColor(143, 144, 145)];
  478. [m_colors addObject:DDMakeColor(151, 152, 153)];
  479. [m_colors addObject:DDMakeColor(159, 161, 161)];
  480. [m_colors addObject:DDMakeColor(167, 169, 169)];
  481. [m_colors addObject:DDMakeColor(176, 177, 177)];
  482. [m_colors addObject:DDMakeColor(184, 185, 186)];
  483. [m_colors addObject:DDMakeColor(192, 193, 194)];
  484. [m_colors addObject:DDMakeColor(200, 201, 202)];
  485. [m_colors addObject:DDMakeColor(208, 209, 210)];
  486. [m_colors addObject:DDMakeColor(216, 218, 218)];
  487. [m_colors addObject:DDMakeColor(224, 226, 226)];
  488. [m_colors addObject:DDMakeColor(232, 234, 234)];
  489. [m_colors addObject:DDMakeColor(240, 242, 242)];
  490. // Color codes
  491. int index = 16;
  492. while (index < 256) {
  493. [m_codes_fg addObject:[NSString stringWithFormat:@"38;5;%dm", index]];
  494. [m_codes_bg addObject:[NSString stringWithFormat:@"48;5;%dm", index]];
  495. index++;
  496. }
  497. #else /* if MAP_TO_TERMINAL_APP_COLORS */
  498. // Standard xterm colors:
  499. //
  500. // These are the colors xterm shells use in xterm-256color mode.
  501. // In this mode, the shell supports 256 different colors, specified by 256 color codes.
  502. //
  503. // The first 16 color codes map to the original 16 color codes supported by the earlier xterm-color mode.
  504. // These are generally configurable, and thus we ignore them for the purposes of mapping,
  505. // as we can't rely on them being constant. They are largely duplicated anyway.
  506. //
  507. // The next 216 color codes are designed to run the spectrum, with several shades of every color.
  508. // The last 24 color codes represent a grayscale.
  509. //
  510. // While the color codes are standardized, the actual RGB values for each color code is not.
  511. // However most standard xterms follow a well known color chart,
  512. // which can easily be calculated using the simple formula below.
  513. //
  514. // More information about ansi escape codes can be found online.
  515. // http://en.wikipedia.org/wiki/ANSI_escape_code
  516. int index = 16;
  517. int r; // red
  518. int g; // green
  519. int b; // blue
  520. int ri; // r increment
  521. int gi; // g increment
  522. int bi; // b increment
  523. // Calculate xterm colors (using standard algorithm)
  524. int r = 0;
  525. int g = 0;
  526. int b = 0;
  527. for (ri = 0; ri < 6; ri++) {
  528. r = (ri == 0) ? 0 : 95 + (40 * (ri - 1));
  529. for (gi = 0; gi < 6; gi++) {
  530. g = (gi == 0) ? 0 : 95 + (40 * (gi - 1));
  531. for (bi = 0; bi < 6; bi++) {
  532. b = (bi == 0) ? 0 : 95 + (40 * (bi - 1));
  533. [m_codes_fg addObject:[NSString stringWithFormat:@"38;5;%dm", index]];
  534. [m_codes_bg addObject:[NSString stringWithFormat:@"48;5;%dm", index]];
  535. [m_colors addObject:DDMakeColor(r, g, b)];
  536. index++;
  537. }
  538. }
  539. }
  540. // Calculate xterm grayscale (using standard algorithm)
  541. r = 8;
  542. g = 8;
  543. b = 8;
  544. while (index < 256) {
  545. [m_codes_fg addObject:[NSString stringWithFormat:@"38;5;%dm", index]];
  546. [m_codes_bg addObject:[NSString stringWithFormat:@"48;5;%dm", index]];
  547. [m_colors addObject:DDMakeColor(r, g, b)];
  548. r += 10;
  549. g += 10;
  550. b += 10;
  551. index++;
  552. }
  553. #endif /* if MAP_TO_TERMINAL_APP_COLORS */
  554. codes_fg = [m_codes_fg copy];
  555. codes_bg = [m_codes_bg copy];
  556. colors = [m_colors copy];
  557. NSAssert([codes_fg count] == [codes_bg count], @"Invalid colors/codes array(s)");
  558. NSAssert([codes_fg count] == [colors count], @"Invalid colors/codes array(s)");
  559. }
  560. + (void)getRed:(CGFloat *)rPtr green:(CGFloat *)gPtr blue:(CGFloat *)bPtr fromColor:(DDColor *)color {
  561. #if TARGET_OS_IPHONE
  562. // iOS
  563. BOOL done = NO;
  564. if ([color respondsToSelector:@selector(getRed:green:blue:alpha:)]) {
  565. done = [color getRed:rPtr green:gPtr blue:bPtr alpha:NULL];
  566. }
  567. if (!done) {
  568. // The method getRed:green:blue:alpha: was only available starting iOS 5.
  569. // So in iOS 4 and earlier, we have to jump through hoops.
  570. CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
  571. unsigned char pixel[4];
  572. CGContextRef context = CGBitmapContextCreate(&pixel, 1, 1, 8, 4, rgbColorSpace, (CGBitmapInfo)(kCGBitmapAlphaInfoMask & kCGImageAlphaNoneSkipLast));
  573. CGContextSetFillColorWithColor(context, [color CGColor]);
  574. CGContextFillRect(context, CGRectMake(0, 0, 1, 1));
  575. if (rPtr) {
  576. *rPtr = pixel[0] / 255.0f;
  577. }
  578. if (gPtr) {
  579. *gPtr = pixel[1] / 255.0f;
  580. }
  581. if (bPtr) {
  582. *bPtr = pixel[2] / 255.0f;
  583. }
  584. CGContextRelease(context);
  585. CGColorSpaceRelease(rgbColorSpace);
  586. }
  587. #elif __has_include(<AppKit/NSColor.h>)
  588. // OS X with AppKit
  589. NSColor *safeColor = [color colorUsingColorSpaceName:NSCalibratedRGBColorSpace];
  590. [safeColor getRed:rPtr green:gPtr blue:bPtr alpha:NULL];
  591. #else /* if TARGET_OS_IPHONE */
  592. // OS X without AppKit
  593. [color getRed:rPtr green:gPtr blue:bPtr alpha:NULL];
  594. #endif /* if TARGET_OS_IPHONE */
  595. }
  596. /**
  597. * Maps the given color to the closest available color supported by the shell.
  598. * The shell may support 256 colors, or only 16.
  599. *
  600. * This method loops through the known supported color set, and calculates the closest color.
  601. * The array index of that color, within the colors array, is then returned.
  602. * This array index may also be used as the index within the codes_fg and codes_bg arrays.
  603. **/
  604. + (NSUInteger)codeIndexForColor:(DDColor *)inColor {
  605. CGFloat inR, inG, inB;
  606. [self getRed:&inR green:&inG blue:&inB fromColor:inColor];
  607. NSUInteger bestIndex = 0;
  608. CGFloat lowestDistance = 100.0f;
  609. NSUInteger i = 0;
  610. for (DDColor *color in colors) {
  611. // Calculate Euclidean distance (lower value means closer to given color)
  612. CGFloat r, g, b;
  613. [self getRed:&r green:&g blue:&b fromColor:color];
  614. #if CGFLOAT_IS_DOUBLE
  615. CGFloat distance = sqrt(pow(r - inR, 2.0) + pow(g - inG, 2.0) + pow(b - inB, 2.0));
  616. #else
  617. CGFloat distance = sqrtf(powf(r - inR, 2.0f) + powf(g - inG, 2.0f) + powf(b - inB, 2.0f));
  618. #endif
  619. NSLogVerbose(@"DDTTYLogger: %3lu : %.3f,%.3f,%.3f & %.3f,%.3f,%.3f = %.6f",
  620. (unsigned long)i, inR, inG, inB, r, g, b, distance);
  621. if (distance < lowestDistance) {
  622. bestIndex = i;
  623. lowestDistance = distance;
  624. NSLogVerbose(@"DDTTYLogger: New best index = %lu", (unsigned long)bestIndex);
  625. }
  626. i++;
  627. }
  628. return bestIndex;
  629. }
  630. + (instancetype)sharedInstance {
  631. static dispatch_once_t DDTTYLoggerOnceToken;
  632. dispatch_once(&DDTTYLoggerOnceToken, ^{
  633. // Xcode does NOT natively support colors in the Xcode debugging console.
  634. // You'll need to install the XcodeColors plugin to see colors in the Xcode console.
  635. //
  636. // PS - Please read the header file before diving into the source code.
  637. char *xcode_colors = getenv("XcodeColors");
  638. char *term = getenv("TERM");
  639. if (xcode_colors && (strcmp(xcode_colors, "YES") == 0)) {
  640. isaXcodeColorTTY = YES;
  641. } else if (term) {
  642. if (strcasestr(term, "color") != NULL) {
  643. isaColorTTY = YES;
  644. isaColor256TTY = (strcasestr(term, "256") != NULL);
  645. if (isaColor256TTY) {
  646. [self initialize_colors_256];
  647. } else {
  648. [self initialize_colors_16];
  649. }
  650. }
  651. }
  652. NSLogInfo(@"DDTTYLogger: isaColorTTY = %@", (isaColorTTY ? @"YES" : @"NO"));
  653. NSLogInfo(@"DDTTYLogger: isaColor256TTY: %@", (isaColor256TTY ? @"YES" : @"NO"));
  654. NSLogInfo(@"DDTTYLogger: isaXcodeColorTTY: %@", (isaXcodeColorTTY ? @"YES" : @"NO"));
  655. sharedInstance = [[[self class] alloc] init];
  656. });
  657. return sharedInstance;
  658. }
  659. - (instancetype)init {
  660. if (sharedInstance != nil) {
  661. return nil;
  662. }
  663. if ((self = [super init])) {
  664. _calendarUnitFlags = (NSCalendarUnitYear |
  665. NSCalendarUnitMonth |
  666. NSCalendarUnitDay |
  667. NSCalendarUnitHour |
  668. NSCalendarUnitMinute |
  669. NSCalendarUnitSecond);
  670. // Initialze 'app' variable (char *)
  671. _appName = [[NSProcessInfo processInfo] processName];
  672. _appLen = [_appName lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
  673. if (_appLen == 0) {
  674. _appName = @"<UnnamedApp>";
  675. _appLen = [_appName lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
  676. }
  677. _app = (char *)malloc(_appLen + 1);
  678. if (_app == NULL) {
  679. return nil;
  680. }
  681. BOOL processedAppName = [_appName getCString:_app maxLength:(_appLen + 1) encoding:NSUTF8StringEncoding];
  682. if (NO == processedAppName) {
  683. free(_app);
  684. return nil;
  685. }
  686. // Initialize 'pid' variable (char *)
  687. _processID = [NSString stringWithFormat:@"%i", (int)getpid()];
  688. _pidLen = [_processID lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
  689. _pid = (char *)malloc(_pidLen + 1);
  690. if (_pid == NULL) {
  691. free(_app);
  692. return nil;
  693. }
  694. BOOL processedID = [_processID getCString:_pid maxLength:(_pidLen + 1) encoding:NSUTF8StringEncoding];
  695. if (NO == processedID) {
  696. free(_app);
  697. free(_pid);
  698. return nil;
  699. }
  700. // Initialize color stuff
  701. _colorsEnabled = NO;
  702. _colorProfilesArray = [[NSMutableArray alloc] initWithCapacity:8];
  703. _colorProfilesDict = [[NSMutableDictionary alloc] initWithCapacity:8];
  704. _automaticallyAppendNewlineForCustomFormatters = YES;
  705. }
  706. return self;
  707. }
  708. - (void)loadDefaultColorProfiles {
  709. [self setForegroundColor:DDMakeColor(214, 57, 30) backgroundColor:nil forFlag:DDLogFlagError];
  710. [self setForegroundColor:DDMakeColor(204, 121, 32) backgroundColor:nil forFlag:DDLogFlagWarning];
  711. }
  712. - (BOOL)colorsEnabled {
  713. // The design of this method is taken from the DDAbstractLogger implementation.
  714. // For extensive documentation please refer to the DDAbstractLogger implementation.
  715. // Note: The internal implementation MUST access the colorsEnabled variable directly,
  716. // This method is designed explicitly for external access.
  717. //
  718. // Using "self." syntax to go through this method will cause immediate deadlock.
  719. // This is the intended result. Fix it by accessing the ivar directly.
  720. // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.
  721. NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
  722. NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
  723. dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
  724. __block BOOL result;
  725. dispatch_sync(globalLoggingQueue, ^{
  726. dispatch_sync(self.loggerQueue, ^{
  727. result = _colorsEnabled;
  728. });
  729. });
  730. return result;
  731. }
  732. - (void)setColorsEnabled:(BOOL)newColorsEnabled {
  733. dispatch_block_t block = ^{
  734. @autoreleasepool {
  735. _colorsEnabled = newColorsEnabled;
  736. if ([_colorProfilesArray count] == 0) {
  737. [self loadDefaultColorProfiles];
  738. }
  739. }
  740. };
  741. // The design of this method is taken from the DDAbstractLogger implementation.
  742. // For extensive documentation please refer to the DDAbstractLogger implementation.
  743. // Note: The internal implementation MUST access the colorsEnabled variable directly,
  744. // This method is designed explicitly for external access.
  745. //
  746. // Using "self." syntax to go through this method will cause immediate deadlock.
  747. // This is the intended result. Fix it by accessing the ivar directly.
  748. // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.
  749. NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
  750. NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
  751. dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
  752. dispatch_async(globalLoggingQueue, ^{
  753. dispatch_async(self.loggerQueue, block);
  754. });
  755. }
  756. - (void)setForegroundColor:(DDColor *)txtColor backgroundColor:(DDColor *)bgColor forFlag:(DDLogFlag)mask {
  757. [self setForegroundColor:txtColor backgroundColor:bgColor forFlag:mask context:LOG_CONTEXT_ALL];
  758. }
  759. - (void)setForegroundColor:(DDColor *)txtColor backgroundColor:(DDColor *)bgColor forFlag:(DDLogFlag)mask context:(NSInteger)ctxt {
  760. dispatch_block_t block = ^{
  761. @autoreleasepool {
  762. DDTTYLoggerColorProfile *newColorProfile =
  763. [[DDTTYLoggerColorProfile alloc] initWithForegroundColor:txtColor
  764. backgroundColor:bgColor
  765. flag:mask
  766. context:ctxt];
  767. NSLogInfo(@"DDTTYLogger: newColorProfile: %@", newColorProfile);
  768. NSUInteger i = 0;
  769. for (DDTTYLoggerColorProfile *colorProfile in _colorProfilesArray) {
  770. if ((colorProfile->mask == mask) && (colorProfile->context == ctxt)) {
  771. break;
  772. }
  773. i++;
  774. }
  775. if (i < [_colorProfilesArray count]) {
  776. _colorProfilesArray[i] = newColorProfile;
  777. } else {
  778. [_colorProfilesArray addObject:newColorProfile];
  779. }
  780. }
  781. };
  782. // The design of the setter logic below is taken from the DDAbstractLogger implementation.
  783. // For documentation please refer to the DDAbstractLogger implementation.
  784. if ([self isOnInternalLoggerQueue]) {
  785. block();
  786. } else {
  787. dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
  788. NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
  789. dispatch_async(globalLoggingQueue, ^{
  790. dispatch_async(self.loggerQueue, block);
  791. });
  792. }
  793. }
  794. - (void)setForegroundColor:(DDColor *)txtColor backgroundColor:(DDColor *)bgColor forTag:(id <NSCopying>)tag {
  795. NSAssert([(id < NSObject >) tag conformsToProtocol: @protocol(NSCopying)], @"Invalid tag");
  796. dispatch_block_t block = ^{
  797. @autoreleasepool {
  798. DDTTYLoggerColorProfile *newColorProfile =
  799. [[DDTTYLoggerColorProfile alloc] initWithForegroundColor:txtColor
  800. backgroundColor:bgColor
  801. flag:(DDLogFlag)0
  802. context:0];
  803. NSLogInfo(@"DDTTYLogger: newColorProfile: %@", newColorProfile);
  804. _colorProfilesDict[tag] = newColorProfile;
  805. }
  806. };
  807. // The design of the setter logic below is taken from the DDAbstractLogger implementation.
  808. // For documentation please refer to the DDAbstractLogger implementation.
  809. if ([self isOnInternalLoggerQueue]) {
  810. block();
  811. } else {
  812. dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
  813. NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
  814. dispatch_async(globalLoggingQueue, ^{
  815. dispatch_async(self.loggerQueue, block);
  816. });
  817. }
  818. }
  819. - (void)clearColorsForFlag:(DDLogFlag)mask {
  820. [self clearColorsForFlag:mask context:0];
  821. }
  822. - (void)clearColorsForFlag:(DDLogFlag)mask context:(NSInteger)context {
  823. dispatch_block_t block = ^{
  824. @autoreleasepool {
  825. NSUInteger i = 0;
  826. for (DDTTYLoggerColorProfile *colorProfile in _colorProfilesArray) {
  827. if ((colorProfile->mask == mask) && (colorProfile->context == context)) {
  828. break;
  829. }
  830. i++;
  831. }
  832. if (i < [_colorProfilesArray count]) {
  833. [_colorProfilesArray removeObjectAtIndex:i];
  834. }
  835. }
  836. };
  837. // The design of the setter logic below is taken from the DDAbstractLogger implementation.
  838. // For documentation please refer to the DDAbstractLogger implementation.
  839. if ([self isOnInternalLoggerQueue]) {
  840. block();
  841. } else {
  842. dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
  843. NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
  844. dispatch_async(globalLoggingQueue, ^{
  845. dispatch_async(self.loggerQueue, block);
  846. });
  847. }
  848. }
  849. - (void)clearColorsForTag:(id <NSCopying>)tag {
  850. NSAssert([(id < NSObject >) tag conformsToProtocol: @protocol(NSCopying)], @"Invalid tag");
  851. dispatch_block_t block = ^{
  852. @autoreleasepool {
  853. [_colorProfilesDict removeObjectForKey:tag];
  854. }
  855. };
  856. // The design of the setter logic below is taken from the DDAbstractLogger implementation.
  857. // For documentation please refer to the DDAbstractLogger implementation.
  858. if ([self isOnInternalLoggerQueue]) {
  859. block();
  860. } else {
  861. dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
  862. NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
  863. dispatch_async(globalLoggingQueue, ^{
  864. dispatch_async(self.loggerQueue, block);
  865. });
  866. }
  867. }
  868. - (void)clearColorsForAllFlags {
  869. dispatch_block_t block = ^{
  870. @autoreleasepool {
  871. [_colorProfilesArray removeAllObjects];
  872. }
  873. };
  874. // The design of the setter logic below is taken from the DDAbstractLogger implementation.
  875. // For documentation please refer to the DDAbstractLogger implementation.
  876. if ([self isOnInternalLoggerQueue]) {
  877. block();
  878. } else {
  879. dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
  880. NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
  881. dispatch_async(globalLoggingQueue, ^{
  882. dispatch_async(self.loggerQueue, block);
  883. });
  884. }
  885. }
  886. - (void)clearColorsForAllTags {
  887. dispatch_block_t block = ^{
  888. @autoreleasepool {
  889. [_colorProfilesDict removeAllObjects];
  890. }
  891. };
  892. // The design of the setter logic below is taken from the DDAbstractLogger implementation.
  893. // For documentation please refer to the DDAbstractLogger implementation.
  894. if ([self isOnInternalLoggerQueue]) {
  895. block();
  896. } else {
  897. dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
  898. NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
  899. dispatch_async(globalLoggingQueue, ^{
  900. dispatch_async(self.loggerQueue, block);
  901. });
  902. }
  903. }
  904. - (void)clearAllColors {
  905. dispatch_block_t block = ^{
  906. @autoreleasepool {
  907. [_colorProfilesArray removeAllObjects];
  908. [_colorProfilesDict removeAllObjects];
  909. }
  910. };
  911. // The design of the setter logic below is taken from the DDAbstractLogger implementation.
  912. // For documentation please refer to the DDAbstractLogger implementation.
  913. if ([self isOnInternalLoggerQueue]) {
  914. block();
  915. } else {
  916. dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
  917. NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
  918. dispatch_async(globalLoggingQueue, ^{
  919. dispatch_async(self.loggerQueue, block);
  920. });
  921. }
  922. }
  923. - (void)logMessage:(DDLogMessage *)logMessage {
  924. NSString *logMsg = logMessage->_message;
  925. BOOL isFormatted = NO;
  926. if (_logFormatter) {
  927. logMsg = [_logFormatter formatLogMessage:logMessage];
  928. isFormatted = logMsg != logMessage->_message;
  929. }
  930. if (logMsg) {
  931. // Search for a color profile associated with the log message
  932. DDTTYLoggerColorProfile *colorProfile = nil;
  933. if (_colorsEnabled) {
  934. if (logMessage->_tag) {
  935. colorProfile = _colorProfilesDict[logMessage->_tag];
  936. }
  937. if (colorProfile == nil) {
  938. for (DDTTYLoggerColorProfile *cp in _colorProfilesArray) {
  939. if (logMessage->_flag & cp->mask) {
  940. // Color profile set for this context?
  941. if (logMessage->_context == cp->context) {
  942. colorProfile = cp;
  943. // Stop searching
  944. break;
  945. }
  946. // Check if LOG_CONTEXT_ALL was specified as a default color for this flag
  947. if (cp->context == LOG_CONTEXT_ALL) {
  948. colorProfile = cp;
  949. // We don't break to keep searching for more specific color profiles for the context
  950. }
  951. }
  952. }
  953. }
  954. }
  955. // Convert log message to C string.
  956. //
  957. // We use the stack instead of the heap for speed if possible.
  958. // But we're extra cautious to avoid a stack overflow.
  959. NSUInteger msgLen = [logMsg lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
  960. const BOOL useStack = msgLen < (1024 * 4);
  961. char msgStack[useStack ? (msgLen + 1) : 1]; // Analyzer doesn't like zero-size array, hence the 1
  962. char *msg = useStack ? msgStack : (char *)malloc(msgLen + 1);
  963. if (msg == NULL) {
  964. return;
  965. }
  966. BOOL logMsgEnc = [logMsg getCString:msg maxLength:(msgLen + 1) encoding:NSUTF8StringEncoding];
  967. if (!logMsgEnc) {
  968. if (!useStack && msg != NULL) {
  969. free(msg);
  970. }
  971. return;
  972. }
  973. // Write the log message to STDERR
  974. if (isFormatted) {
  975. // The log message has already been formatted.
  976. int iovec_len = (_automaticallyAppendNewlineForCustomFormatters) ? 5 : 4;
  977. struct iovec v[iovec_len];
  978. if (colorProfile) {
  979. v[0].iov_base = colorProfile->fgCode;
  980. v[0].iov_len = colorProfile->fgCodeLen;
  981. v[1].iov_base = colorProfile->bgCode;
  982. v[1].iov_len = colorProfile->bgCodeLen;
  983. v[iovec_len - 1].iov_base = colorProfile->resetCode;
  984. v[iovec_len - 1].iov_len = colorProfile->resetCodeLen;
  985. } else {
  986. v[0].iov_base = "";
  987. v[0].iov_len = 0;
  988. v[1].iov_base = "";
  989. v[1].iov_len = 0;
  990. v[iovec_len - 1].iov_base = "";
  991. v[iovec_len - 1].iov_len = 0;
  992. }
  993. v[2].iov_base = (char *)msg;
  994. v[2].iov_len = msgLen;
  995. if (iovec_len == 5) {
  996. v[3].iov_base = "\n";
  997. v[3].iov_len = (msg[msgLen] == '\n') ? 0 : 1;
  998. }
  999. writev(STDERR_FILENO, v, iovec_len);
  1000. } else {
  1001. // The log message is unformatted, so apply standard NSLog style formatting.
  1002. int len;
  1003. char ts[24] = "";
  1004. size_t tsLen = 0;
  1005. // Calculate timestamp.
  1006. // The technique below is faster than using NSDateFormatter.
  1007. if (logMessage->_timestamp) {
  1008. NSDateComponents *components = [[NSCalendar autoupdatingCurrentCalendar] components:_calendarUnitFlags fromDate:logMessage->_timestamp];
  1009. NSTimeInterval epoch = [logMessage->_timestamp timeIntervalSinceReferenceDate];
  1010. int milliseconds = (int)((epoch - floor(epoch)) * 1000);
  1011. len = snprintf(ts, 24, "%04ld-%02ld-%02ld %02ld:%02ld:%02ld:%03d", // yyyy-MM-dd HH:mm:ss:SSS
  1012. (long)components.year,
  1013. (long)components.month,
  1014. (long)components.day,
  1015. (long)components.hour,
  1016. (long)components.minute,
  1017. (long)components.second, milliseconds);
  1018. tsLen = (NSUInteger)MAX(MIN(24 - 1, len), 0);
  1019. }
  1020. // Calculate thread ID
  1021. //
  1022. // How many characters do we need for the thread id?
  1023. // logMessage->machThreadID is of type mach_port_t, which is an unsigned int.
  1024. //
  1025. // 1 hex char = 4 bits
  1026. // 8 hex chars for 32 bit, plus ending '\0' = 9
  1027. char tid[9];
  1028. len = snprintf(tid, 9, "%s", [logMessage->_threadID cStringUsingEncoding:NSUTF8StringEncoding]);
  1029. size_t tidLen = (NSUInteger)MAX(MIN(9 - 1, len), 0);
  1030. // Here is our format: "%s %s[%i:%s] %s", timestamp, appName, processID, threadID, logMsg
  1031. struct iovec v[13];
  1032. if (colorProfile) {
  1033. v[0].iov_base = colorProfile->fgCode;
  1034. v[0].iov_len = colorProfile->fgCodeLen;
  1035. v[1].iov_base = colorProfile->bgCode;
  1036. v[1].iov_len = colorProfile->bgCodeLen;
  1037. v[12].iov_base = colorProfile->resetCode;
  1038. v[12].iov_len = colorProfile->resetCodeLen;
  1039. } else {
  1040. v[0].iov_base = "";
  1041. v[0].iov_len = 0;
  1042. v[1].iov_base = "";
  1043. v[1].iov_len = 0;
  1044. v[12].iov_base = "";
  1045. v[12].iov_len = 0;
  1046. }
  1047. v[2].iov_base = ts;
  1048. v[2].iov_len = tsLen;
  1049. v[3].iov_base = " ";
  1050. v[3].iov_len = 1;
  1051. v[4].iov_base = _app;
  1052. v[4].iov_len = _appLen;
  1053. v[5].iov_base = "[";
  1054. v[5].iov_len = 1;
  1055. v[6].iov_base = _pid;
  1056. v[6].iov_len = _pidLen;
  1057. v[7].iov_base = ":";
  1058. v[7].iov_len = 1;
  1059. v[8].iov_base = tid;
  1060. v[8].iov_len = MIN((size_t)8, tidLen); // snprintf doesn't return what you might think
  1061. v[9].iov_base = "] ";
  1062. v[9].iov_len = 2;
  1063. v[10].iov_base = (char *)msg;
  1064. v[10].iov_len = msgLen;
  1065. v[11].iov_base = "\n";
  1066. v[11].iov_len = (msg[msgLen] == '\n') ? 0 : 1;
  1067. writev(STDERR_FILENO, v, 13);
  1068. }
  1069. if (!useStack) {
  1070. free(msg);
  1071. }
  1072. }
  1073. }
  1074. - (NSString *)loggerName {
  1075. return @"cocoa.lumberjack.ttyLogger";
  1076. }
  1077. @end
  1078. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1079. @implementation DDTTYLoggerColorProfile
  1080. - (instancetype)initWithForegroundColor:(DDColor *)fgColor backgroundColor:(DDColor *)bgColor flag:(DDLogFlag)aMask context:(NSInteger)ctxt {
  1081. if ((self = [super init])) {
  1082. mask = aMask;
  1083. context = ctxt;
  1084. CGFloat r, g, b;
  1085. if (fgColor) {
  1086. [DDTTYLogger getRed:&r green:&g blue:&b fromColor:fgColor];
  1087. fg_r = (uint8_t)(r * 255.0f);
  1088. fg_g = (uint8_t)(g * 255.0f);
  1089. fg_b = (uint8_t)(b * 255.0f);
  1090. }
  1091. if (bgColor) {
  1092. [DDTTYLogger getRed:&r green:&g blue:&b fromColor:bgColor];
  1093. bg_r = (uint8_t)(r * 255.0f);
  1094. bg_g = (uint8_t)(g * 255.0f);
  1095. bg_b = (uint8_t)(b * 255.0f);
  1096. }
  1097. if (fgColor && isaColorTTY) {
  1098. // Map foreground color to closest available shell color
  1099. fgCodeIndex = [DDTTYLogger codeIndexForColor:fgColor];
  1100. fgCodeRaw = codes_fg[fgCodeIndex];
  1101. NSString *escapeSeq = @"\033[";
  1102. NSUInteger len1 = [escapeSeq lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
  1103. NSUInteger len2 = [fgCodeRaw lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
  1104. BOOL escapeSeqEnc = [escapeSeq getCString:(fgCode) maxLength:(len1 + 1) encoding:NSUTF8StringEncoding];
  1105. BOOL fgCodeRawEsc = [fgCodeRaw getCString:(fgCode + len1) maxLength:(len2 + 1) encoding:NSUTF8StringEncoding];
  1106. if (!escapeSeqEnc || !fgCodeRawEsc) {
  1107. return nil;
  1108. }
  1109. fgCodeLen = len1 + len2;
  1110. } else if (fgColor && isaXcodeColorTTY) {
  1111. // Convert foreground color to color code sequence
  1112. const char *escapeSeq = XCODE_COLORS_ESCAPE_SEQ;
  1113. int result = snprintf(fgCode, 24, "%sfg%u,%u,%u;", escapeSeq, fg_r, fg_g, fg_b);
  1114. fgCodeLen = (NSUInteger)MAX(MIN(result, (24 - 1)), 0);
  1115. } else {
  1116. // No foreground color or no color support
  1117. fgCode[0] = '\0';
  1118. fgCodeLen = 0;
  1119. }
  1120. if (bgColor && isaColorTTY) {
  1121. // Map background color to closest available shell color
  1122. bgCodeIndex = [DDTTYLogger codeIndexForColor:bgColor];
  1123. bgCodeRaw = codes_bg[bgCodeIndex];
  1124. NSString *escapeSeq = @"\033[";
  1125. NSUInteger len1 = [escapeSeq lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
  1126. NSUInteger len2 = [bgCodeRaw lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
  1127. BOOL escapeSeqEnc = [escapeSeq getCString:(bgCode) maxLength:(len1 + 1) encoding:NSUTF8StringEncoding];
  1128. BOOL bgCodeRawEsc = [bgCodeRaw getCString:(bgCode + len1) maxLength:(len2 + 1) encoding:NSUTF8StringEncoding];
  1129. if (!escapeSeqEnc || !bgCodeRawEsc) {
  1130. return nil;
  1131. }
  1132. bgCodeLen = len1 + len2;
  1133. } else if (bgColor && isaXcodeColorTTY) {
  1134. // Convert background color to color code sequence
  1135. const char *escapeSeq = XCODE_COLORS_ESCAPE_SEQ;
  1136. int result = snprintf(bgCode, 24, "%sbg%u,%u,%u;", escapeSeq, bg_r, bg_g, bg_b);
  1137. bgCodeLen = (NSUInteger)MAX(MIN(result, (24 - 1)), 0);
  1138. } else {
  1139. // No background color or no color support
  1140. bgCode[0] = '\0';
  1141. bgCodeLen = 0;
  1142. }
  1143. if (isaColorTTY) {
  1144. resetCodeLen = (NSUInteger)MAX(snprintf(resetCode, 8, "\033[0m"), 0);
  1145. } else if (isaXcodeColorTTY) {
  1146. resetCodeLen = (NSUInteger)MAX(snprintf(resetCode, 8, XCODE_COLORS_RESET), 0);
  1147. } else {
  1148. resetCode[0] = '\0';
  1149. resetCodeLen = 0;
  1150. }
  1151. }
  1152. return self;
  1153. }
  1154. - (NSString *)description {
  1155. return [NSString stringWithFormat:
  1156. @"<DDTTYLoggerColorProfile: %p mask:%i ctxt:%ld fg:%u,%u,%u bg:%u,%u,%u fgCode:%@ bgCode:%@>",
  1157. self, (int)mask, (long)context, fg_r, fg_g, fg_b, bg_r, bg_g, bg_b, fgCodeRaw, bgCodeRaw];
  1158. }
  1159. @end