xref: /aosp_15_r20/external/google-breakpad/src/common/mac/GTMLogger.m (revision 9712c20fc9bbfbac4935993a2ca0b3958c5adad2)
1*9712c20fSFrederick Mayle//
2*9712c20fSFrederick Mayle//  GTMLogger.m
3*9712c20fSFrederick Mayle//
4*9712c20fSFrederick Mayle//  Copyright 2007-2008 Google LLC
5*9712c20fSFrederick Mayle//
6*9712c20fSFrederick Mayle//  Licensed under the Apache License, Version 2.0 (the "License"); you may not
7*9712c20fSFrederick Mayle//  use this file except in compliance with the License.  You may obtain a copy
8*9712c20fSFrederick Mayle//  of the License at
9*9712c20fSFrederick Mayle//
10*9712c20fSFrederick Mayle//  http://www.apache.org/licenses/LICENSE-2.0
11*9712c20fSFrederick Mayle//
12*9712c20fSFrederick Mayle//  Unless required by applicable law or agreed to in writing, software
13*9712c20fSFrederick Mayle//  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14*9712c20fSFrederick Mayle//  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
15*9712c20fSFrederick Mayle//  License for the specific language governing permissions and limitations under
16*9712c20fSFrederick Mayle//  the License.
17*9712c20fSFrederick Mayle//
18*9712c20fSFrederick Mayle
19*9712c20fSFrederick Mayle#import "GTMLogger.h"
20*9712c20fSFrederick Mayle#import <fcntl.h>
21*9712c20fSFrederick Mayle#import <unistd.h>
22*9712c20fSFrederick Mayle#import <stdlib.h>
23*9712c20fSFrederick Mayle#import <pthread.h>
24*9712c20fSFrederick Mayle
25*9712c20fSFrederick Mayle
26*9712c20fSFrederick Mayle#if !defined(__clang__) && (__GNUC__*10+__GNUC_MINOR__ >= 42)
27*9712c20fSFrederick Mayle// Some versions of GCC (4.2 and below AFAIK) aren't great about supporting
28*9712c20fSFrederick Mayle// -Wmissing-format-attribute
29*9712c20fSFrederick Mayle// when the function is anything more complex than foo(NSString *fmt, ...).
30*9712c20fSFrederick Mayle// You see the error inside the function when you turn ... into va_args and
31*9712c20fSFrederick Mayle// attempt to call another function (like vsprintf for example).
32*9712c20fSFrederick Mayle// So we just shut off the warning for this file. We reenable it at the end.
33*9712c20fSFrederick Mayle#pragma GCC diagnostic ignored "-Wmissing-format-attribute"
34*9712c20fSFrederick Mayle#endif  // !__clang__
35*9712c20fSFrederick Mayle
36*9712c20fSFrederick Mayle// Reference to the shared GTMLogger instance. This is not a singleton, it's
37*9712c20fSFrederick Mayle// just an easy reference to one shared instance.
38*9712c20fSFrederick Maylestatic GTMLogger *gSharedLogger = nil;
39*9712c20fSFrederick Mayle
40*9712c20fSFrederick Mayle
41*9712c20fSFrederick Mayle@implementation GTMLogger
42*9712c20fSFrederick Mayle
43*9712c20fSFrederick Mayle// Returns a pointer to the shared logger instance. If none exists, a standard
44*9712c20fSFrederick Mayle// logger is created and returned.
45*9712c20fSFrederick Mayle+ (id)sharedLogger {
46*9712c20fSFrederick Mayle  @synchronized(self) {
47*9712c20fSFrederick Mayle    if (gSharedLogger == nil) {
48*9712c20fSFrederick Mayle      gSharedLogger = [[self standardLogger] retain];
49*9712c20fSFrederick Mayle    }
50*9712c20fSFrederick Mayle  }
51*9712c20fSFrederick Mayle  return [[gSharedLogger retain] autorelease];
52*9712c20fSFrederick Mayle}
53*9712c20fSFrederick Mayle
54*9712c20fSFrederick Mayle+ (void)setSharedLogger:(GTMLogger *)logger {
55*9712c20fSFrederick Mayle  @synchronized(self) {
56*9712c20fSFrederick Mayle    [gSharedLogger autorelease];
57*9712c20fSFrederick Mayle    gSharedLogger = [logger retain];
58*9712c20fSFrederick Mayle  }
59*9712c20fSFrederick Mayle}
60*9712c20fSFrederick Mayle
61*9712c20fSFrederick Mayle+ (id)standardLogger {
62*9712c20fSFrederick Mayle  // Don't trust NSFileHandle not to throw
63*9712c20fSFrederick Mayle  @try {
64*9712c20fSFrederick Mayle    id<GTMLogWriter> writer = [NSFileHandle fileHandleWithStandardOutput];
65*9712c20fSFrederick Mayle    id<GTMLogFormatter> fr = [[[GTMLogStandardFormatter alloc] init]
66*9712c20fSFrederick Mayle                                 autorelease];
67*9712c20fSFrederick Mayle    id<GTMLogFilter> filter = [[[GTMLogLevelFilter alloc] init] autorelease];
68*9712c20fSFrederick Mayle    return [[[self alloc] initWithWriter:writer
69*9712c20fSFrederick Mayle                               formatter:fr
70*9712c20fSFrederick Mayle                                  filter:filter] autorelease];
71*9712c20fSFrederick Mayle  }
72*9712c20fSFrederick Mayle  @catch (id e) {
73*9712c20fSFrederick Mayle    // Ignored
74*9712c20fSFrederick Mayle  }
75*9712c20fSFrederick Mayle  return nil;
76*9712c20fSFrederick Mayle}
77*9712c20fSFrederick Mayle
78*9712c20fSFrederick Mayle+ (id)standardLoggerWithStderr {
79*9712c20fSFrederick Mayle  // Don't trust NSFileHandle not to throw
80*9712c20fSFrederick Mayle  @try {
81*9712c20fSFrederick Mayle    id me = [self standardLogger];
82*9712c20fSFrederick Mayle    [me setWriter:[NSFileHandle fileHandleWithStandardError]];
83*9712c20fSFrederick Mayle    return me;
84*9712c20fSFrederick Mayle  }
85*9712c20fSFrederick Mayle  @catch (id e) {
86*9712c20fSFrederick Mayle    // Ignored
87*9712c20fSFrederick Mayle  }
88*9712c20fSFrederick Mayle  return nil;
89*9712c20fSFrederick Mayle}
90*9712c20fSFrederick Mayle
91*9712c20fSFrederick Mayle+ (id)standardLoggerWithStdoutAndStderr {
92*9712c20fSFrederick Mayle  // We're going to take advantage of the GTMLogger to GTMLogWriter adaptor
93*9712c20fSFrederick Mayle  // and create a composite logger that an outer "standard" logger can use
94*9712c20fSFrederick Mayle  // as a writer. Our inner loggers should apply no formatting since the main
95*9712c20fSFrederick Mayle  // logger does that and we want the caller to be able to change formatters
96*9712c20fSFrederick Mayle  // or add writers without knowing the inner structure of our composite.
97*9712c20fSFrederick Mayle
98*9712c20fSFrederick Mayle  // Don't trust NSFileHandle not to throw
99*9712c20fSFrederick Mayle  @try {
100*9712c20fSFrederick Mayle    GTMLogBasicFormatter *formatter = [[[GTMLogBasicFormatter alloc] init]
101*9712c20fSFrederick Mayle                                          autorelease];
102*9712c20fSFrederick Mayle    GTMLogger *stdoutLogger =
103*9712c20fSFrederick Mayle        [self loggerWithWriter:[NSFileHandle fileHandleWithStandardOutput]
104*9712c20fSFrederick Mayle                     formatter:formatter
105*9712c20fSFrederick Mayle                        filter:[[[GTMLogMaximumLevelFilter alloc]
106*9712c20fSFrederick Mayle                                  initWithMaximumLevel:kGTMLoggerLevelInfo]
107*9712c20fSFrederick Mayle                                      autorelease]];
108*9712c20fSFrederick Mayle    GTMLogger *stderrLogger =
109*9712c20fSFrederick Mayle        [self loggerWithWriter:[NSFileHandle fileHandleWithStandardError]
110*9712c20fSFrederick Mayle                     formatter:formatter
111*9712c20fSFrederick Mayle                        filter:[[[GTMLogMininumLevelFilter alloc]
112*9712c20fSFrederick Mayle                                  initWithMinimumLevel:kGTMLoggerLevelError]
113*9712c20fSFrederick Mayle                                      autorelease]];
114*9712c20fSFrederick Mayle    GTMLogger *compositeWriter =
115*9712c20fSFrederick Mayle        [self loggerWithWriter:[NSArray arrayWithObjects:
116*9712c20fSFrederick Mayle                                   stdoutLogger, stderrLogger, nil]
117*9712c20fSFrederick Mayle                     formatter:formatter
118*9712c20fSFrederick Mayle                        filter:[[[GTMLogNoFilter alloc] init] autorelease]];
119*9712c20fSFrederick Mayle    GTMLogger *outerLogger = [self standardLogger];
120*9712c20fSFrederick Mayle    [outerLogger setWriter:compositeWriter];
121*9712c20fSFrederick Mayle    return outerLogger;
122*9712c20fSFrederick Mayle  }
123*9712c20fSFrederick Mayle  @catch (id e) {
124*9712c20fSFrederick Mayle    // Ignored
125*9712c20fSFrederick Mayle  }
126*9712c20fSFrederick Mayle  return nil;
127*9712c20fSFrederick Mayle}
128*9712c20fSFrederick Mayle
129*9712c20fSFrederick Mayle+ (id)standardLoggerWithPath:(NSString *)path {
130*9712c20fSFrederick Mayle  @try {
131*9712c20fSFrederick Mayle    NSFileHandle *fh = [NSFileHandle fileHandleForLoggingAtPath:path mode:0644];
132*9712c20fSFrederick Mayle    if (fh == nil) return nil;
133*9712c20fSFrederick Mayle    id me = [self standardLogger];
134*9712c20fSFrederick Mayle    [me setWriter:fh];
135*9712c20fSFrederick Mayle    return me;
136*9712c20fSFrederick Mayle  }
137*9712c20fSFrederick Mayle  @catch (id e) {
138*9712c20fSFrederick Mayle    // Ignored
139*9712c20fSFrederick Mayle  }
140*9712c20fSFrederick Mayle  return nil;
141*9712c20fSFrederick Mayle}
142*9712c20fSFrederick Mayle
143*9712c20fSFrederick Mayle+ (id)loggerWithWriter:(id<GTMLogWriter>)writer
144*9712c20fSFrederick Mayle             formatter:(id<GTMLogFormatter>)formatter
145*9712c20fSFrederick Mayle                filter:(id<GTMLogFilter>)filter {
146*9712c20fSFrederick Mayle  return [[[self alloc] initWithWriter:writer
147*9712c20fSFrederick Mayle                             formatter:formatter
148*9712c20fSFrederick Mayle                                filter:filter] autorelease];
149*9712c20fSFrederick Mayle}
150*9712c20fSFrederick Mayle
151*9712c20fSFrederick Mayle+ (id)logger {
152*9712c20fSFrederick Mayle  return [[[self alloc] init] autorelease];
153*9712c20fSFrederick Mayle}
154*9712c20fSFrederick Mayle
155*9712c20fSFrederick Mayle- (id)init {
156*9712c20fSFrederick Mayle  return [self initWithWriter:nil formatter:nil filter:nil];
157*9712c20fSFrederick Mayle}
158*9712c20fSFrederick Mayle
159*9712c20fSFrederick Mayle- (id)initWithWriter:(id<GTMLogWriter>)writer
160*9712c20fSFrederick Mayle           formatter:(id<GTMLogFormatter>)formatter
161*9712c20fSFrederick Mayle              filter:(id<GTMLogFilter>)filter {
162*9712c20fSFrederick Mayle  if ((self = [super init])) {
163*9712c20fSFrederick Mayle    [self setWriter:writer];
164*9712c20fSFrederick Mayle    [self setFormatter:formatter];
165*9712c20fSFrederick Mayle    [self setFilter:filter];
166*9712c20fSFrederick Mayle  }
167*9712c20fSFrederick Mayle  return self;
168*9712c20fSFrederick Mayle}
169*9712c20fSFrederick Mayle
170*9712c20fSFrederick Mayle- (void)dealloc {
171*9712c20fSFrederick Mayle  // Unlikely, but |writer_| may be an NSFileHandle, which can throw
172*9712c20fSFrederick Mayle  @try {
173*9712c20fSFrederick Mayle    [formatter_ release];
174*9712c20fSFrederick Mayle    [filter_ release];
175*9712c20fSFrederick Mayle    [writer_ release];
176*9712c20fSFrederick Mayle  }
177*9712c20fSFrederick Mayle  @catch (id e) {
178*9712c20fSFrederick Mayle    // Ignored
179*9712c20fSFrederick Mayle  }
180*9712c20fSFrederick Mayle  [super dealloc];
181*9712c20fSFrederick Mayle}
182*9712c20fSFrederick Mayle
183*9712c20fSFrederick Mayle- (id<GTMLogWriter>)writer {
184*9712c20fSFrederick Mayle  return [[writer_ retain] autorelease];
185*9712c20fSFrederick Mayle}
186*9712c20fSFrederick Mayle
187*9712c20fSFrederick Mayle- (void)setWriter:(id<GTMLogWriter>)writer {
188*9712c20fSFrederick Mayle  @synchronized(self) {
189*9712c20fSFrederick Mayle    [writer_ autorelease];
190*9712c20fSFrederick Mayle    writer_ = nil;
191*9712c20fSFrederick Mayle    if (writer == nil) {
192*9712c20fSFrederick Mayle      // Try to use stdout, but don't trust NSFileHandle
193*9712c20fSFrederick Mayle      @try {
194*9712c20fSFrederick Mayle        writer_ = [[NSFileHandle fileHandleWithStandardOutput] retain];
195*9712c20fSFrederick Mayle      }
196*9712c20fSFrederick Mayle      @catch (id e) {
197*9712c20fSFrederick Mayle        // Leave |writer_| nil
198*9712c20fSFrederick Mayle      }
199*9712c20fSFrederick Mayle    } else {
200*9712c20fSFrederick Mayle      writer_ = [writer retain];
201*9712c20fSFrederick Mayle    }
202*9712c20fSFrederick Mayle  }
203*9712c20fSFrederick Mayle}
204*9712c20fSFrederick Mayle
205*9712c20fSFrederick Mayle- (id<GTMLogFormatter>)formatter {
206*9712c20fSFrederick Mayle  return [[formatter_ retain] autorelease];
207*9712c20fSFrederick Mayle}
208*9712c20fSFrederick Mayle
209*9712c20fSFrederick Mayle- (void)setFormatter:(id<GTMLogFormatter>)formatter {
210*9712c20fSFrederick Mayle  @synchronized(self) {
211*9712c20fSFrederick Mayle    [formatter_ autorelease];
212*9712c20fSFrederick Mayle    formatter_ = nil;
213*9712c20fSFrederick Mayle    if (formatter == nil) {
214*9712c20fSFrederick Mayle      @try {
215*9712c20fSFrederick Mayle        formatter_ = [[GTMLogBasicFormatter alloc] init];
216*9712c20fSFrederick Mayle      }
217*9712c20fSFrederick Mayle      @catch (id e) {
218*9712c20fSFrederick Mayle        // Leave |formatter_| nil
219*9712c20fSFrederick Mayle      }
220*9712c20fSFrederick Mayle    } else {
221*9712c20fSFrederick Mayle      formatter_ = [formatter retain];
222*9712c20fSFrederick Mayle    }
223*9712c20fSFrederick Mayle  }
224*9712c20fSFrederick Mayle}
225*9712c20fSFrederick Mayle
226*9712c20fSFrederick Mayle- (id<GTMLogFilter>)filter {
227*9712c20fSFrederick Mayle  return [[filter_ retain] autorelease];
228*9712c20fSFrederick Mayle}
229*9712c20fSFrederick Mayle
230*9712c20fSFrederick Mayle- (void)setFilter:(id<GTMLogFilter>)filter {
231*9712c20fSFrederick Mayle  @synchronized(self) {
232*9712c20fSFrederick Mayle    [filter_ autorelease];
233*9712c20fSFrederick Mayle    filter_ = nil;
234*9712c20fSFrederick Mayle    if (filter == nil) {
235*9712c20fSFrederick Mayle      @try {
236*9712c20fSFrederick Mayle        filter_ = [[GTMLogNoFilter alloc] init];
237*9712c20fSFrederick Mayle      }
238*9712c20fSFrederick Mayle      @catch (id e) {
239*9712c20fSFrederick Mayle        // Leave |filter_| nil
240*9712c20fSFrederick Mayle      }
241*9712c20fSFrederick Mayle    } else {
242*9712c20fSFrederick Mayle      filter_ = [filter retain];
243*9712c20fSFrederick Mayle    }
244*9712c20fSFrederick Mayle  }
245*9712c20fSFrederick Mayle}
246*9712c20fSFrederick Mayle
247*9712c20fSFrederick Mayle- (void)logDebug:(NSString *)fmt, ... {
248*9712c20fSFrederick Mayle  va_list args;
249*9712c20fSFrederick Mayle  va_start(args, fmt);
250*9712c20fSFrederick Mayle  [self logInternalFunc:NULL format:fmt valist:args level:kGTMLoggerLevelDebug];
251*9712c20fSFrederick Mayle  va_end(args);
252*9712c20fSFrederick Mayle}
253*9712c20fSFrederick Mayle
254*9712c20fSFrederick Mayle- (void)logInfo:(NSString *)fmt, ... {
255*9712c20fSFrederick Mayle  va_list args;
256*9712c20fSFrederick Mayle  va_start(args, fmt);
257*9712c20fSFrederick Mayle  [self logInternalFunc:NULL format:fmt valist:args level:kGTMLoggerLevelInfo];
258*9712c20fSFrederick Mayle  va_end(args);
259*9712c20fSFrederick Mayle}
260*9712c20fSFrederick Mayle
261*9712c20fSFrederick Mayle- (void)logError:(NSString *)fmt, ... {
262*9712c20fSFrederick Mayle  va_list args;
263*9712c20fSFrederick Mayle  va_start(args, fmt);
264*9712c20fSFrederick Mayle  [self logInternalFunc:NULL format:fmt valist:args level:kGTMLoggerLevelError];
265*9712c20fSFrederick Mayle  va_end(args);
266*9712c20fSFrederick Mayle}
267*9712c20fSFrederick Mayle
268*9712c20fSFrederick Mayle- (void)logAssert:(NSString *)fmt, ... {
269*9712c20fSFrederick Mayle  va_list args;
270*9712c20fSFrederick Mayle  va_start(args, fmt);
271*9712c20fSFrederick Mayle  [self logInternalFunc:NULL format:fmt valist:args level:kGTMLoggerLevelAssert];
272*9712c20fSFrederick Mayle  va_end(args);
273*9712c20fSFrederick Mayle}
274*9712c20fSFrederick Mayle
275*9712c20fSFrederick Mayle@end  // GTMLogger
276*9712c20fSFrederick Mayle
277*9712c20fSFrederick Mayle@implementation GTMLogger (GTMLoggerMacroHelpers)
278*9712c20fSFrederick Mayle
279*9712c20fSFrederick Mayle- (void)logFuncDebug:(const char *)func msg:(NSString *)fmt, ... {
280*9712c20fSFrederick Mayle  va_list args;
281*9712c20fSFrederick Mayle  va_start(args, fmt);
282*9712c20fSFrederick Mayle  [self logInternalFunc:func format:fmt valist:args level:kGTMLoggerLevelDebug];
283*9712c20fSFrederick Mayle  va_end(args);
284*9712c20fSFrederick Mayle}
285*9712c20fSFrederick Mayle
286*9712c20fSFrederick Mayle- (void)logFuncInfo:(const char *)func msg:(NSString *)fmt, ... {
287*9712c20fSFrederick Mayle  va_list args;
288*9712c20fSFrederick Mayle  va_start(args, fmt);
289*9712c20fSFrederick Mayle  [self logInternalFunc:func format:fmt valist:args level:kGTMLoggerLevelInfo];
290*9712c20fSFrederick Mayle  va_end(args);
291*9712c20fSFrederick Mayle}
292*9712c20fSFrederick Mayle
293*9712c20fSFrederick Mayle- (void)logFuncError:(const char *)func msg:(NSString *)fmt, ... {
294*9712c20fSFrederick Mayle  va_list args;
295*9712c20fSFrederick Mayle  va_start(args, fmt);
296*9712c20fSFrederick Mayle  [self logInternalFunc:func format:fmt valist:args level:kGTMLoggerLevelError];
297*9712c20fSFrederick Mayle  va_end(args);
298*9712c20fSFrederick Mayle}
299*9712c20fSFrederick Mayle
300*9712c20fSFrederick Mayle- (void)logFuncAssert:(const char *)func msg:(NSString *)fmt, ... {
301*9712c20fSFrederick Mayle  va_list args;
302*9712c20fSFrederick Mayle  va_start(args, fmt);
303*9712c20fSFrederick Mayle  [self logInternalFunc:func format:fmt valist:args level:kGTMLoggerLevelAssert];
304*9712c20fSFrederick Mayle  va_end(args);
305*9712c20fSFrederick Mayle}
306*9712c20fSFrederick Mayle
307*9712c20fSFrederick Mayle@end  // GTMLoggerMacroHelpers
308*9712c20fSFrederick Mayle
309*9712c20fSFrederick Mayle@implementation GTMLogger (PrivateMethods)
310*9712c20fSFrederick Mayle
311*9712c20fSFrederick Mayle- (void)logInternalFunc:(const char *)func
312*9712c20fSFrederick Mayle                 format:(NSString *)fmt
313*9712c20fSFrederick Mayle                 valist:(va_list)args
314*9712c20fSFrederick Mayle                  level:(GTMLoggerLevel)level {
315*9712c20fSFrederick Mayle  // Primary point where logging happens, logging should never throw, catch
316*9712c20fSFrederick Mayle  // everything.
317*9712c20fSFrederick Mayle  @try {
318*9712c20fSFrederick Mayle    NSString *fname = func ? [NSString stringWithUTF8String:func] : nil;
319*9712c20fSFrederick Mayle    NSString *msg = [formatter_ stringForFunc:fname
320*9712c20fSFrederick Mayle                                   withFormat:fmt
321*9712c20fSFrederick Mayle                                       valist:args
322*9712c20fSFrederick Mayle                                        level:level];
323*9712c20fSFrederick Mayle    if (msg && [filter_ filterAllowsMessage:msg level:level])
324*9712c20fSFrederick Mayle      [writer_ logMessage:msg level:level];
325*9712c20fSFrederick Mayle  }
326*9712c20fSFrederick Mayle  @catch (id e) {
327*9712c20fSFrederick Mayle    // Ignored
328*9712c20fSFrederick Mayle  }
329*9712c20fSFrederick Mayle}
330*9712c20fSFrederick Mayle
331*9712c20fSFrederick Mayle@end  // PrivateMethods
332*9712c20fSFrederick Mayle
333*9712c20fSFrederick Mayle
334*9712c20fSFrederick Mayle@implementation NSFileHandle (GTMFileHandleLogWriter)
335*9712c20fSFrederick Mayle
336*9712c20fSFrederick Mayle+ (id)fileHandleForLoggingAtPath:(NSString *)path mode:(mode_t)mode {
337*9712c20fSFrederick Mayle  int fd = -1;
338*9712c20fSFrederick Mayle  if (path) {
339*9712c20fSFrederick Mayle    int flags = O_WRONLY | O_APPEND | O_CREAT;
340*9712c20fSFrederick Mayle    fd = open([path fileSystemRepresentation], flags, mode);
341*9712c20fSFrederick Mayle  }
342*9712c20fSFrederick Mayle  if (fd == -1) return nil;
343*9712c20fSFrederick Mayle  return [[[self alloc] initWithFileDescriptor:fd
344*9712c20fSFrederick Mayle                                closeOnDealloc:YES] autorelease];
345*9712c20fSFrederick Mayle}
346*9712c20fSFrederick Mayle
347*9712c20fSFrederick Mayle- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level {
348*9712c20fSFrederick Mayle  @synchronized(self) {
349*9712c20fSFrederick Mayle    // Closed pipes should not generate exceptions in our caller. Catch here
350*9712c20fSFrederick Mayle    // as well [GTMLogger logInternalFunc:...] so that an exception in this
351*9712c20fSFrederick Mayle    // writer does not prevent other writers from having a chance.
352*9712c20fSFrederick Mayle    @try {
353*9712c20fSFrederick Mayle      NSString *line = [NSString stringWithFormat:@"%@\n", msg];
354*9712c20fSFrederick Mayle      [self writeData:[line dataUsingEncoding:NSUTF8StringEncoding]];
355*9712c20fSFrederick Mayle    }
356*9712c20fSFrederick Mayle    @catch (id e) {
357*9712c20fSFrederick Mayle      // Ignored
358*9712c20fSFrederick Mayle    }
359*9712c20fSFrederick Mayle  }
360*9712c20fSFrederick Mayle}
361*9712c20fSFrederick Mayle
362*9712c20fSFrederick Mayle@end  // GTMFileHandleLogWriter
363*9712c20fSFrederick Mayle
364*9712c20fSFrederick Mayle
365*9712c20fSFrederick Mayle@implementation NSArray (GTMArrayCompositeLogWriter)
366*9712c20fSFrederick Mayle
367*9712c20fSFrederick Mayle- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level {
368*9712c20fSFrederick Mayle  @synchronized(self) {
369*9712c20fSFrederick Mayle    id<GTMLogWriter> child = nil;
370*9712c20fSFrederick Mayle    GTM_FOREACH_OBJECT(child, self) {
371*9712c20fSFrederick Mayle      if ([child conformsToProtocol:@protocol(GTMLogWriter)])
372*9712c20fSFrederick Mayle        [child logMessage:msg level:level];
373*9712c20fSFrederick Mayle    }
374*9712c20fSFrederick Mayle  }
375*9712c20fSFrederick Mayle}
376*9712c20fSFrederick Mayle
377*9712c20fSFrederick Mayle@end  // GTMArrayCompositeLogWriter
378*9712c20fSFrederick Mayle
379*9712c20fSFrederick Mayle
380*9712c20fSFrederick Mayle@implementation GTMLogger (GTMLoggerLogWriter)
381*9712c20fSFrederick Mayle
382*9712c20fSFrederick Mayle- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level {
383*9712c20fSFrederick Mayle  switch (level) {
384*9712c20fSFrederick Mayle    case kGTMLoggerLevelDebug:
385*9712c20fSFrederick Mayle      [self logDebug:@"%@", msg];
386*9712c20fSFrederick Mayle      break;
387*9712c20fSFrederick Mayle    case kGTMLoggerLevelInfo:
388*9712c20fSFrederick Mayle      [self logInfo:@"%@", msg];
389*9712c20fSFrederick Mayle      break;
390*9712c20fSFrederick Mayle    case kGTMLoggerLevelError:
391*9712c20fSFrederick Mayle      [self logError:@"%@", msg];
392*9712c20fSFrederick Mayle      break;
393*9712c20fSFrederick Mayle    case kGTMLoggerLevelAssert:
394*9712c20fSFrederick Mayle      [self logAssert:@"%@", msg];
395*9712c20fSFrederick Mayle      break;
396*9712c20fSFrederick Mayle    default:
397*9712c20fSFrederick Mayle      // Ignore the message.
398*9712c20fSFrederick Mayle      break;
399*9712c20fSFrederick Mayle  }
400*9712c20fSFrederick Mayle}
401*9712c20fSFrederick Mayle
402*9712c20fSFrederick Mayle@end  // GTMLoggerLogWriter
403*9712c20fSFrederick Mayle
404*9712c20fSFrederick Mayle
405*9712c20fSFrederick Mayle@implementation GTMLogBasicFormatter
406*9712c20fSFrederick Mayle
407*9712c20fSFrederick Mayle- (NSString *)prettyNameForFunc:(NSString *)func {
408*9712c20fSFrederick Mayle  NSString *name = [func stringByTrimmingCharactersInSet:
409*9712c20fSFrederick Mayle                     [NSCharacterSet whitespaceAndNewlineCharacterSet]];
410*9712c20fSFrederick Mayle  NSString *function = @"(unknown)";
411*9712c20fSFrederick Mayle  if ([name length]) {
412*9712c20fSFrederick Mayle    if (// Objective C __func__ and __PRETTY_FUNCTION__
413*9712c20fSFrederick Mayle        [name hasPrefix:@"-["] || [name hasPrefix:@"+["] ||
414*9712c20fSFrederick Mayle        // C++ __PRETTY_FUNCTION__ and other preadorned formats
415*9712c20fSFrederick Mayle        [name hasSuffix:@")"]) {
416*9712c20fSFrederick Mayle      function = name;
417*9712c20fSFrederick Mayle    } else {
418*9712c20fSFrederick Mayle      // Assume C99 __func__
419*9712c20fSFrederick Mayle      function = [NSString stringWithFormat:@"%@()", name];
420*9712c20fSFrederick Mayle    }
421*9712c20fSFrederick Mayle  }
422*9712c20fSFrederick Mayle  return function;
423*9712c20fSFrederick Mayle}
424*9712c20fSFrederick Mayle
425*9712c20fSFrederick Mayle- (NSString *)stringForFunc:(NSString *)func
426*9712c20fSFrederick Mayle                 withFormat:(NSString *)fmt
427*9712c20fSFrederick Mayle                     valist:(va_list)args
428*9712c20fSFrederick Mayle                      level:(GTMLoggerLevel)level {
429*9712c20fSFrederick Mayle  // Performance note: We may want to do a quick check here to see if |fmt|
430*9712c20fSFrederick Mayle  // contains a '%', and if not, simply return 'fmt'.
431*9712c20fSFrederick Mayle  if (!(fmt && args)) return nil;
432*9712c20fSFrederick Mayle  return [[[NSString alloc] initWithFormat:fmt arguments:args] autorelease];
433*9712c20fSFrederick Mayle}
434*9712c20fSFrederick Mayle
435*9712c20fSFrederick Mayle@end  // GTMLogBasicFormatter
436*9712c20fSFrederick Mayle
437*9712c20fSFrederick Mayle
438*9712c20fSFrederick Mayle@implementation GTMLogStandardFormatter
439*9712c20fSFrederick Mayle
440*9712c20fSFrederick Mayle- (id)init {
441*9712c20fSFrederick Mayle  if ((self = [super init])) {
442*9712c20fSFrederick Mayle    dateFormatter_ = [[NSDateFormatter alloc] init];
443*9712c20fSFrederick Mayle    [dateFormatter_ setFormatterBehavior:NSDateFormatterBehavior10_4];
444*9712c20fSFrederick Mayle    [dateFormatter_ setDateFormat:@"yyyy-MM-dd HH:mm:ss.SSS"];
445*9712c20fSFrederick Mayle    pname_ = [[[NSProcessInfo processInfo] processName] copy];
446*9712c20fSFrederick Mayle    pid_ = [[NSProcessInfo processInfo] processIdentifier];
447*9712c20fSFrederick Mayle    if (!(dateFormatter_ && pname_)) {
448*9712c20fSFrederick Mayle      [self release];
449*9712c20fSFrederick Mayle      return nil;
450*9712c20fSFrederick Mayle    }
451*9712c20fSFrederick Mayle  }
452*9712c20fSFrederick Mayle  return self;
453*9712c20fSFrederick Mayle}
454*9712c20fSFrederick Mayle
455*9712c20fSFrederick Mayle- (void)dealloc {
456*9712c20fSFrederick Mayle  [dateFormatter_ release];
457*9712c20fSFrederick Mayle  [pname_ release];
458*9712c20fSFrederick Mayle  [super dealloc];
459*9712c20fSFrederick Mayle}
460*9712c20fSFrederick Mayle
461*9712c20fSFrederick Mayle- (NSString *)stringForFunc:(NSString *)func
462*9712c20fSFrederick Mayle                 withFormat:(NSString *)fmt
463*9712c20fSFrederick Mayle                     valist:(va_list)args
464*9712c20fSFrederick Mayle                      level:(GTMLoggerLevel)level {
465*9712c20fSFrederick Mayle  NSString *tstamp = nil;
466*9712c20fSFrederick Mayle  @synchronized (dateFormatter_) {
467*9712c20fSFrederick Mayle    tstamp = [dateFormatter_ stringFromDate:[NSDate date]];
468*9712c20fSFrederick Mayle  }
469*9712c20fSFrederick Mayle  return [NSString stringWithFormat:@"%@ %@[%d/%p] [lvl=%d] %@ %@",
470*9712c20fSFrederick Mayle           tstamp, pname_, pid_, pthread_self(),
471*9712c20fSFrederick Mayle           level, [self prettyNameForFunc:func],
472*9712c20fSFrederick Mayle           // |super| has guard for nil |fmt| and |args|
473*9712c20fSFrederick Mayle           [super stringForFunc:func withFormat:fmt valist:args level:level]];
474*9712c20fSFrederick Mayle}
475*9712c20fSFrederick Mayle
476*9712c20fSFrederick Mayle@end  // GTMLogStandardFormatter
477*9712c20fSFrederick Mayle
478*9712c20fSFrederick Mayle
479*9712c20fSFrederick Mayle@implementation GTMLogLevelFilter
480*9712c20fSFrederick Mayle
481*9712c20fSFrederick Mayle// Check the environment and the user preferences for the GTMVerboseLogging key
482*9712c20fSFrederick Mayle// to see if verbose logging has been enabled. The environment variable will
483*9712c20fSFrederick Mayle// override the defaults setting, so check the environment first.
484*9712c20fSFrederick Mayle// COV_NF_START
485*9712c20fSFrederick Maylestatic BOOL IsVerboseLoggingEnabled(void) {
486*9712c20fSFrederick Mayle  static NSString *const kVerboseLoggingKey = @"GTMVerboseLogging";
487*9712c20fSFrederick Mayle  NSString *value = [[[NSProcessInfo processInfo] environment]
488*9712c20fSFrederick Mayle                        objectForKey:kVerboseLoggingKey];
489*9712c20fSFrederick Mayle  if (value) {
490*9712c20fSFrederick Mayle    // Emulate [NSString boolValue] for pre-10.5
491*9712c20fSFrederick Mayle    value = [value stringByTrimmingCharactersInSet:
492*9712c20fSFrederick Mayle                [NSCharacterSet whitespaceAndNewlineCharacterSet]];
493*9712c20fSFrederick Mayle    if ([[value uppercaseString] hasPrefix:@"Y"] ||
494*9712c20fSFrederick Mayle        [[value uppercaseString] hasPrefix:@"T"] ||
495*9712c20fSFrederick Mayle        [value intValue]) {
496*9712c20fSFrederick Mayle      return YES;
497*9712c20fSFrederick Mayle    } else {
498*9712c20fSFrederick Mayle      return NO;
499*9712c20fSFrederick Mayle    }
500*9712c20fSFrederick Mayle  }
501*9712c20fSFrederick Mayle  return [[NSUserDefaults standardUserDefaults] boolForKey:kVerboseLoggingKey];
502*9712c20fSFrederick Mayle}
503*9712c20fSFrederick Mayle// COV_NF_END
504*9712c20fSFrederick Mayle
505*9712c20fSFrederick Mayle// In DEBUG builds, log everything. If we're not in a debug build we'll assume
506*9712c20fSFrederick Mayle// that we're in a Release build.
507*9712c20fSFrederick Mayle- (BOOL)filterAllowsMessage:(NSString *)msg level:(GTMLoggerLevel)level {
508*9712c20fSFrederick Mayle#if defined(DEBUG) && DEBUG
509*9712c20fSFrederick Mayle  return YES;
510*9712c20fSFrederick Mayle#endif
511*9712c20fSFrederick Mayle
512*9712c20fSFrederick Mayle  BOOL allow = YES;
513*9712c20fSFrederick Mayle
514*9712c20fSFrederick Mayle  switch (level) {
515*9712c20fSFrederick Mayle    case kGTMLoggerLevelDebug:
516*9712c20fSFrederick Mayle      allow = NO;
517*9712c20fSFrederick Mayle      break;
518*9712c20fSFrederick Mayle    case kGTMLoggerLevelInfo:
519*9712c20fSFrederick Mayle      allow = IsVerboseLoggingEnabled();
520*9712c20fSFrederick Mayle      break;
521*9712c20fSFrederick Mayle    case kGTMLoggerLevelError:
522*9712c20fSFrederick Mayle      allow = YES;
523*9712c20fSFrederick Mayle      break;
524*9712c20fSFrederick Mayle    case kGTMLoggerLevelAssert:
525*9712c20fSFrederick Mayle      allow = YES;
526*9712c20fSFrederick Mayle      break;
527*9712c20fSFrederick Mayle    default:
528*9712c20fSFrederick Mayle      allow = YES;
529*9712c20fSFrederick Mayle      break;
530*9712c20fSFrederick Mayle  }
531*9712c20fSFrederick Mayle
532*9712c20fSFrederick Mayle  return allow;
533*9712c20fSFrederick Mayle}
534*9712c20fSFrederick Mayle
535*9712c20fSFrederick Mayle@end  // GTMLogLevelFilter
536*9712c20fSFrederick Mayle
537*9712c20fSFrederick Mayle
538*9712c20fSFrederick Mayle@implementation GTMLogNoFilter
539*9712c20fSFrederick Mayle
540*9712c20fSFrederick Mayle- (BOOL)filterAllowsMessage:(NSString *)msg level:(GTMLoggerLevel)level {
541*9712c20fSFrederick Mayle  return YES;  // Allow everything through
542*9712c20fSFrederick Mayle}
543*9712c20fSFrederick Mayle
544*9712c20fSFrederick Mayle@end  // GTMLogNoFilter
545*9712c20fSFrederick Mayle
546*9712c20fSFrederick Mayle
547*9712c20fSFrederick Mayle@implementation GTMLogAllowedLevelFilter
548*9712c20fSFrederick Mayle
549*9712c20fSFrederick Mayle// Private designated initializer
550*9712c20fSFrederick Mayle- (id)initWithAllowedLevels:(NSIndexSet *)levels {
551*9712c20fSFrederick Mayle  self = [super init];
552*9712c20fSFrederick Mayle  if (self != nil) {
553*9712c20fSFrederick Mayle    allowedLevels_ = [levels retain];
554*9712c20fSFrederick Mayle    // Cap min/max level
555*9712c20fSFrederick Mayle    if (!allowedLevels_ ||
556*9712c20fSFrederick Mayle        // NSIndexSet is unsigned so only check the high bound, but need to
557*9712c20fSFrederick Mayle        // check both first and last index because NSIndexSet appears to allow
558*9712c20fSFrederick Mayle        // wraparound.
559*9712c20fSFrederick Mayle        ([allowedLevels_ firstIndex] > kGTMLoggerLevelAssert) ||
560*9712c20fSFrederick Mayle        ([allowedLevels_ lastIndex] > kGTMLoggerLevelAssert)) {
561*9712c20fSFrederick Mayle      [self release];
562*9712c20fSFrederick Mayle      return nil;
563*9712c20fSFrederick Mayle    }
564*9712c20fSFrederick Mayle  }
565*9712c20fSFrederick Mayle  return self;
566*9712c20fSFrederick Mayle}
567*9712c20fSFrederick Mayle
568*9712c20fSFrederick Mayle- (id)init {
569*9712c20fSFrederick Mayle  // Allow all levels in default init
570*9712c20fSFrederick Mayle  return [self initWithAllowedLevels:[NSIndexSet indexSetWithIndexesInRange:
571*9712c20fSFrederick Mayle             NSMakeRange(kGTMLoggerLevelUnknown,
572*9712c20fSFrederick Mayle                 (kGTMLoggerLevelAssert - kGTMLoggerLevelUnknown + 1))]];
573*9712c20fSFrederick Mayle}
574*9712c20fSFrederick Mayle
575*9712c20fSFrederick Mayle- (void)dealloc {
576*9712c20fSFrederick Mayle  [allowedLevels_ release];
577*9712c20fSFrederick Mayle  [super dealloc];
578*9712c20fSFrederick Mayle}
579*9712c20fSFrederick Mayle
580*9712c20fSFrederick Mayle- (BOOL)filterAllowsMessage:(NSString *)msg level:(GTMLoggerLevel)level {
581*9712c20fSFrederick Mayle  return [allowedLevels_ containsIndex:level];
582*9712c20fSFrederick Mayle}
583*9712c20fSFrederick Mayle
584*9712c20fSFrederick Mayle@end  // GTMLogAllowedLevelFilter
585*9712c20fSFrederick Mayle
586*9712c20fSFrederick Mayle
587*9712c20fSFrederick Mayle@implementation GTMLogMininumLevelFilter
588*9712c20fSFrederick Mayle
589*9712c20fSFrederick Mayle- (id)initWithMinimumLevel:(GTMLoggerLevel)level {
590*9712c20fSFrederick Mayle  return [super initWithAllowedLevels:[NSIndexSet indexSetWithIndexesInRange:
591*9712c20fSFrederick Mayle             NSMakeRange(level,
592*9712c20fSFrederick Mayle                         (kGTMLoggerLevelAssert - level + 1))]];
593*9712c20fSFrederick Mayle}
594*9712c20fSFrederick Mayle
595*9712c20fSFrederick Mayle@end  // GTMLogMininumLevelFilter
596*9712c20fSFrederick Mayle
597*9712c20fSFrederick Mayle
598*9712c20fSFrederick Mayle@implementation GTMLogMaximumLevelFilter
599*9712c20fSFrederick Mayle
600*9712c20fSFrederick Mayle- (id)initWithMaximumLevel:(GTMLoggerLevel)level {
601*9712c20fSFrederick Mayle  return [super initWithAllowedLevels:[NSIndexSet indexSetWithIndexesInRange:
602*9712c20fSFrederick Mayle             NSMakeRange(kGTMLoggerLevelUnknown, level + 1)]];
603*9712c20fSFrederick Mayle}
604*9712c20fSFrederick Mayle
605*9712c20fSFrederick Mayle@end  // GTMLogMaximumLevelFilter
606*9712c20fSFrederick Mayle
607*9712c20fSFrederick Mayle#if !defined(__clang__) && (__GNUC__*10+__GNUC_MINOR__ >= 42)
608*9712c20fSFrederick Mayle// See comment at top of file.
609*9712c20fSFrederick Mayle#pragma GCC diagnostic error "-Wmissing-format-attribute"
610*9712c20fSFrederick Mayle#endif  // !__clang__
611*9712c20fSFrederick Mayle
612