Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Add dev-controlled request/response logging to iOS SDK

Summary:
Adds global, dev-controlled request/response logging to the iOS SDK.  This address #987699.

This is exposed via a class method on FBSession that receives an NSSet of string constants determining what logging to turn on.  (Would love to use a class-level ##@property## instead, but they don't seem to exist.)

Have logging on FBURLConnection and FBRequestConnection.  Example output is similar to, but not identical to (since there's been an update since these Pastes) https://phabricator.fb.com/P1717428 and https://phabricator.fb.com/P1717420, respectively.

Timing, error, and response size information are also reported.

Also put our "session state transition" NSLogging behind one of these logging constants that you can turn on.

All of this is turned off by default.

I don't think we need to go much beyond that, at least not until we here demand for it.

access_token can optionally be omitted in the logs as well... defaulting to off, even when you enable logging.

Test Plan:
Inserted various forms of

  [FBSession setLoggingLevel:...]

into demo apps and watch the logs flow by.

Reviewers: mmarucheck, clang, jacl

Reviewed By: mmarucheck

CC: ekoneil, vijaye, yariv

Differential Revision: https://phabricator.fb.com/D460707

Revert Plan: Revert the code.
  • Loading branch information...
commit fb2323914c8a074eaee732551b3afd60a685be25 1 parent 278f3af
@gregschechter gregschechter authored
View
77 src/FBLogger.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2010 Facebook
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#import <Foundation/Foundation.h>
+
+/*!
+ @class FBLogger
+
+ @abstract
+ Simple logging utility for conditionally logging strings and then emitting them
+ via NSLog().
+
+ @unsorted
+ */
+@interface FBLogger : NSObject
+
+// Access current accumulated contents of the logger.
+@property (copy, nonatomic) NSString *contents;
+
+// Each FBLogger gets a unique serial number to allow the client to log these numbers and, for instance, correlation of Request/Response
+@property (nonatomic, readonly) NSUInteger loggerSerialNumber;
+
+// The logging behavior of this logger. See the FB_LOG_BEHAVIOR* constants in FBSession.h
+@property (copy, nonatomic, readonly) NSString *loggingBehavior;
+
+// Is the current logger instance active, based on its loggingBehavior?
+@property (nonatomic, readonly) BOOL isActive;
+
+//
+// Instance methods
+//
+
+// Create with specified logging behavior
+- (id)initWithLoggingBehavior:(NSString *)loggingBehavior;
+
+// Append string, or key/value pair
+- (void)appendString:(NSString *)string;
+- (void)appendFormat:(NSString *)formatString, ...;
+- (void)appendKey:(NSString *)key value:(NSString *)value;
+
+// Emit log, clearing out the logger contents.
+- (void)emitToNSLog;
+
+//
+// Class methods
+//
+
+//
+// Return a globally unique serial number to be used for correlating multiple output from the same logger.
+//
++ (NSUInteger)newSerialNumber;
+
+// Simple helper to write a single log entry, based upon whether the behavior matches a specified on.
++ (void)singleShotLogEntry:(NSString *)loggingBehavior
+ logEntry:(NSString *)logEntry;
+
++ (void)singleShotLogEntry:(NSString *)loggingBehavior
+ formatString:(NSString *)formatString, ...;
+
+// When logging strings, replace all instances of 'replace' with instances of 'replaceWith'.
++ (void)registerStringToReplace:(NSString *)replace
+ replaceWith:(NSString *)replaceWith;
+
+@end
View
159 src/FBLogger.m
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2010 Facebook
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#import "FBLogger.h"
+#import "FBSession.h"
+#import "FBUtility.h"
+
+static NSUInteger g_serialNumberCounter = 1111;
+static NSMutableDictionary *g_stringsToReplace = nil;
+
+@interface FBLogger ()
+
+@property (nonatomic, retain, readonly) NSMutableString *internalContents;
+@property (nonatomic, retain, readonly) NSMutableDictionary *stringsToReplace;
+
+@end
+
+@implementation FBLogger
+
+@synthesize internalContents = _internalContents;
+@synthesize isActive = _isActive;
+@synthesize loggingBehavior = _loggingBehavior;
+@synthesize loggerSerialNumber = _loggerSerialNumber;
+@synthesize stringsToReplace = _stringsToReplace;
+
+// Lifetime
+
+- (id)initWithLoggingBehavior:(NSString *)loggingBehavior {
+ if (self = [super init]) {
+ _isActive = [[FBSession loggingBehavior] containsObject:loggingBehavior];
+ _loggingBehavior = loggingBehavior;
+ if (_isActive) {
+ _internalContents = [[NSMutableString alloc] init];
+ _loggerSerialNumber = [FBLogger newSerialNumber];
+ }
+ }
+
+ return self;
+}
+
+- (void)dealloc {
+ [_internalContents release];
+ [super dealloc];
+}
+
+// Public properties
+
+- (NSString *)contents {
+ return _internalContents;
+}
+
+- (void)setContents:(NSString *)contents {
+ if (_isActive) {
+ [_internalContents release];
+ _internalContents = [NSMutableString stringWithString:contents];
+ }
+}
+
+// Public instance methods
+
+- (void)appendString:(NSString *)string {
+ if (_isActive) {
+ [_internalContents appendString:string];
+ }
+}
+
+- (void)appendFormat:(NSString *)formatString, ... {
+ if (_isActive) {
+ va_list vaArguments;
+ va_start(vaArguments, formatString);
+ NSString *logString = [[NSString alloc] initWithFormat:formatString arguments:vaArguments];
+ va_end(vaArguments);
+
+ [self appendString:logString];
+ }
+}
+
+
+- (void)appendKey:(NSString *)key value:(NSString *)value {
+ if (_isActive && [value length]) {
+ [_internalContents appendFormat:@" %@:\t%@\n", key, value];
+ }
+}
+
+- (void)emitToNSLog {
+ if (_isActive) {
+
+ for (NSString *key in [g_stringsToReplace keyEnumerator]) {
+ [_internalContents replaceOccurrencesOfString:key
+ withString:[g_stringsToReplace objectForKey:key]
+ options:NSLiteralSearch
+ range:NSMakeRange(0, _internalContents.length)];
+ }
+
+ NSLog(@"FBSDKLog: %@", _internalContents);
+ [_internalContents setString:@""];
+ }
+}
+
+// Public static methods
+
++ (NSUInteger)newSerialNumber {
+ return g_serialNumberCounter++;
+}
+
++ (void)singleShotLogEntry:(NSString *)loggingBehavior
+ logEntry:(NSString *)logEntry {
+ if ([[FBSession loggingBehavior] containsObject:loggingBehavior]) {
+ FBLogger *logger = [[FBLogger alloc] initWithLoggingBehavior:loggingBehavior];
+ [logger appendString:logEntry];
+ [logger emitToNSLog];
+ [logger release];
+ }
+}
+
++ (void)singleShotLogEntry:(NSString *)loggingBehavior
+ formatString:(NSString *)formatString, ...; {
+
+ if ([[FBSession loggingBehavior] containsObject:loggingBehavior]) {
+ va_list vaArguments;
+ va_start(vaArguments, formatString);
+ NSString *logString = [[NSString alloc] initWithFormat:formatString arguments:vaArguments];
+ va_end(vaArguments);
+
+ [self singleShotLogEntry:loggingBehavior logEntry:logString];
+ }
+}
+
++ (void)registerStringToReplace:(NSString *)replace
+ replaceWith:(NSString *)replaceWith {
+
+ // Strings sent in here never get cleaned up, but that's OK, don't ever expect too many.
+
+ if ([[FBSession loggingBehavior] count] > 0) { // otherwise there's no logging.
+
+ if (!g_stringsToReplace) {
+ g_stringsToReplace = [[NSMutableDictionary alloc] init];
+ }
+
+ [g_stringsToReplace setValue:replaceWith forKey:replace];
+ }
+}
+
+
+
+@end
View
6 src/FBRequest.m
@@ -33,7 +33,7 @@ @implementation FBRequest
@synthesize session = _session;
@synthesize graphPath = _graphPath;
@synthesize restMethod = _restMethod;
-@synthesize HTTPMethod = _HTTPMethod;
+@synthesize HTTPMethod = _HTTPMethod;
- (id)init
{
@@ -311,7 +311,7 @@ - (BOOL)loading
+ (NSString *)serializeURL:(NSString *)baseUrl
params:(NSDictionary *)params {
- return [self serializeURL:baseUrl params:params httpMethod:@"GET"];
+ return [self serializeURL:baseUrl params:params httpMethod:kGetHTTPMethod];
}
+ (NSString*)serializeURL:(NSString *)baseUrl
@@ -325,7 +325,7 @@ + (NSString*)serializeURL:(NSString *)baseUrl
for (NSString* key in [params keyEnumerator]) {
if (([[params objectForKey:key] isKindOfClass:[UIImage class]])
||([[params objectForKey:key] isKindOfClass:[NSData class]])) {
- if ([httpMethod isEqualToString:@"GET"]) {
+ if ([httpMethod isEqualToString:kGetHTTPMethod]) {
NSLog(@"can not use GET to upload a file");
}
continue;
View
10 src/FBRequestBody.h
@@ -16,6 +16,7 @@
#import <Foundation/Foundation.h>
#import <UIKit/UIImage.h>
+#import "FBLogger.h"
@interface FBRequestBody : NSObject
@@ -24,13 +25,16 @@
- (id)init;
- (void)appendWithKey:(NSString *)key
- formValue:(NSString *)value;
+ formValue:(NSString *)value
+ logger:(FBLogger *)logger;
- (void)appendWithKey:(NSString *)key
- imageValue:(UIImage *)image;
+ imageValue:(UIImage *)image
+ logger:(FBLogger *)logger;
- (void)appendWithKey:(NSString *)key
- dataValue:(NSData *)data;
+ dataValue:(NSData *)data
+ logger:(FBLogger *)logger;
+ (NSString *)mimeContentType;
View
8 src/FBRequestBody.m
@@ -66,16 +66,19 @@ - (void)appendRecordBoundary
- (void)appendWithKey:(NSString *)key
formValue:(NSString *)value
+ logger:(FBLogger *)logger
{
NSString *disposition =
[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", key];
[self appendUTF8:disposition];
[self appendUTF8:value];
[self appendRecordBoundary];
+ [logger appendFormat:@"\n %@:\t%@", key, (NSString *)value];
}
- (void)appendWithKey:(NSString *)key
- imageValue:(UIImage *)image
+ imageValue:(UIImage *)image
+ logger:(FBLogger *)logger
{
NSString *disposition =
[NSString stringWithFormat:@"Content-Disposition: form-data; filename=\"%@\"\r\n", key];
@@ -84,10 +87,12 @@ - (void)appendWithKey:(NSString *)key
NSData *data = UIImagePNGRepresentation(image);
[self.mutableData appendData:data];
[self appendRecordBoundary];
+ [logger appendFormat:@"\n %@:\t<Image - %d kB>", key, [data length] / 1024];
}
- (void)appendWithKey:(NSString *)key
dataValue:(NSData *)data
+ logger:(FBLogger *)logger
{
NSString *disposition =
[NSString stringWithFormat:@"Content-Disposition: form-data; filename=\"%@\"\r\n", key];
@@ -95,6 +100,7 @@ - (void)appendWithKey:(NSString *)key
[self appendUTF8:@"Content-Type: content/unknown\r\n\r\n"];
[self.mutableData appendData:data];
[self appendRecordBoundary];
+ [logger appendFormat:@"\n %@:\t<Data - %d kB>", key, [data length] / 1024];
}
- (NSData *)data
View
122 src/FBRequestConnection.m
@@ -24,6 +24,7 @@
#import "FBRequest.h"
#import "Facebook.h"
#import "FBGraphObject.h"
+#import "FBLogger.h"
#import "FBUtility.h"
// URL construction constants
@@ -109,6 +110,8 @@ @interface FBRequestConnection ()
@property (nonatomic, retain) NSMutableURLRequest *internalUrlRequest;
@property (nonatomic, retain, readwrite) NSHTTPURLResponse *urlResponse;
@property (nonatomic, retain) FBRequest *deprecatedRequest;
+@property (nonatomic, retain) FBLogger *logger;
+@property (nonatomic) unsigned long requestStartTime;
- (NSMutableURLRequest *)requestWithBatch:(NSArray *)requests
timeout:(NSTimeInterval)timeout;
@@ -120,7 +123,8 @@ - (NSString *)commonAccessToken:(NSArray *)requests;
- (void)appendJSONRequests:(NSArray *)requests
toBody:(FBRequestBody *)body
includeTokens:(BOOL)includeTokens
- andNameAttachments:(NSMutableDictionary *)attachments;
+ andNameAttachments:(NSMutableDictionary *)attachments
+ logger:(FBLogger *)logger;
- (void)addRequest:(FBRequestMetadata *)metadata
toBatch:(NSMutableArray *)batch
@@ -131,7 +135,8 @@ - (BOOL)isAttachment:(id)item;
- (void)appendAttachments:(NSDictionary *)attachments
toBody:(FBRequestBody *)body
- addFormData:(BOOL)addFormData;
+ addFormData:(BOOL)addFormData
+ logger:(FBLogger *)logger;
+ (void)processGraphObject:(id<FBGraphObject>)object
withAction:(KeyValueActionHandler)action;
@@ -168,6 +173,8 @@ - (NSError *)errorWithCode:(FBErrorCode)code
- (BOOL)isInvalidSessionError:(NSError *)error;
+- (void)registerTokenToOmitFromLog:(NSString *)token;
+
+ (NSString *)userAgent;
@end
@@ -187,6 +194,8 @@ @implementation FBRequestConnection
@synthesize internalUrlRequest = _internalUrlRequest;
@synthesize urlResponse = _urlResponse;
@synthesize deprecatedRequest = _deprecatedRequest;
+@synthesize logger = _logger;
+@synthesize requestStartTime = _requestStartTime;
- (NSMutableURLRequest *)urlRequest
{
@@ -222,6 +231,7 @@ - (id)initWithTimeout:(NSTimeInterval)timeout
_requests = [[NSMutableArray alloc] init];
_timeout = timeout;
_state = kStateCreated;
+ _logger = [[FBLogger alloc] initWithLoggingBehavior:FB_LOG_BEHAVIOR_FB_REQUESTS];
}
return self;
}
@@ -234,6 +244,7 @@ - (void)dealloc
[_internalUrlRequest release];
[_urlResponse release];
[_deprecatedRequest release];
+ [_logger release];
[super dealloc];
}
@@ -278,12 +289,15 @@ - (FBRequestConnection *)start
}
NSMutableURLRequest *request = self.urlRequest;
+ _requestStartTime = [FBUtility currentTimeInMilliseconds];
FBURLConnectionHandler handler =
^(FBURLConnection *connection,
NSError *error,
NSURLResponse *response,
NSData *responseData) {
- [self completeWithResponse:response data:responseData orError:error];
+ [self completeWithResponse:response
+ data:responseData
+ orError:error];
};
id<FBRequestDelegate> deprecatedDelegate = [self.deprecatedRequest delegate];
@@ -318,8 +332,11 @@ - (NSMutableURLRequest *)requestWithBatch:(NSArray *)requests
timeout:(NSTimeInterval)timeout
{
FBRequestBody *body = [[FBRequestBody alloc] init];
+ FBLogger *bodyLogger = [[FBLogger alloc] initWithLoggingBehavior:_logger.loggingBehavior];
+ FBLogger *attachmentLogger = [[FBLogger alloc] initWithLoggingBehavior:_logger.loggingBehavior];
+
NSMutableURLRequest *request;
-
+
if ([requests count] == 1) {
FBRequestMetadata *metadata = [requests objectAtIndex:0];
NSURL *url = [self urlWithSingleRequest:metadata.request];
@@ -328,46 +345,72 @@ - (NSMutableURLRequest *)requestWithBatch:(NSArray *)requests
timeoutInterval:timeout];
NSString *httpMethod = metadata.request.HTTPMethod;
- [request setHTTPMethod:httpMethod];
+ [request setHTTPMethod:httpMethod];
[self appendAttachments:metadata.request.parameters
toBody:body
- addFormData:[httpMethod isEqualToString:@"POST"]];
+ addFormData:[httpMethod isEqualToString:@"POST"]
+ logger:attachmentLogger];
// if we have a post object, also roll that into the body
if (metadata.request.graphObject) {
[FBRequestConnection processGraphObject:metadata.request.graphObject
withAction:^(NSString *key, id value) {
- [body appendWithKey:key formValue:value];
+ [body appendWithKey:key formValue:value logger:bodyLogger];
}];
}
} else {
NSString *commonToken = [self commonAccessToken:requests];
if (commonToken) {
- [body appendWithKey:kAccessTokenKey formValue:commonToken];
+ [body appendWithKey:kAccessTokenKey formValue:commonToken logger:bodyLogger];
+ [self registerTokenToOmitFromLog:commonToken];
}
NSMutableDictionary *attachments = [[NSMutableDictionary alloc] init];
+
[self appendJSONRequests:requests
toBody:body
includeTokens:(!commonToken)
- andNameAttachments:attachments];
-
- [self appendAttachments:attachments toBody:body addFormData:NO];
+ andNameAttachments:attachments
+ logger:bodyLogger];
+
+ [self appendAttachments:attachments
+ toBody:body
+ addFormData:NO
+ logger:attachmentLogger];
+
[attachments release];
-
+
request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:kGraphURL]
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
timeoutInterval:timeout];
[request setHTTPMethod:@"POST"];
-
}
[request setHTTPBody:[body data]];
+ NSUInteger bodyLength = [[body data] length] / 1024;
[body release];
[request setValue:[FBRequestConnection userAgent] forHTTPHeaderField:@"User-Agent"];
[request setValue:[FBRequestBody mimeContentType] forHTTPHeaderField:@"Content-Type"];
-
+
+ if (_logger.isActive) {
+ [_logger appendFormat:@"Request <#%d>:\n", _logger.loggerSerialNumber];
+ [_logger appendKey:@"URL" value:[[request URL] absoluteString]];
+ [_logger appendKey:@"Method" value:[request HTTPMethod]];
+ [_logger appendKey:@"UserAgent" value:[FBRequestConnection userAgent]];
+ [_logger appendKey:@"MIME" value:[FBRequestBody mimeContentType]];
+ [_logger appendKey:@"Body Size" value:[NSString stringWithFormat:@"%d kB", bodyLength / 1024]];
+ [_logger appendKey:@"Body (w/o attachments)" value:bodyLogger.contents];
+ [_logger appendKey:@"Attachments" value:attachmentLogger.contents];
+ [_logger appendString:@"\n"];
+
+ [_logger emitToNSLog];
+ }
+
+ // Safely release now that everything's serialized into the logger.
+ [bodyLogger release];
+ [attachmentLogger release];
+
return request;
}
@@ -389,6 +432,7 @@ - (NSURL *)urlWithSingleRequest:(FBRequest *)request
NSString *token = request.session.accessToken;
if (token) {
[request.parameters setValue:token forKey:kAccessTokenKey];
+ [self registerTokenToOmitFromLog:token];
}
NSString *baseURL;
@@ -440,6 +484,7 @@ - (void)appendJSONRequests:(NSArray *)requests
toBody:(FBRequestBody *)body
includeTokens:(BOOL)includeTokens
andNameAttachments:(NSMutableDictionary *)attachments
+ logger:(FBLogger *)logger
{
NSMutableArray *batch = [[NSMutableArray alloc] init];
for (FBRequestMetadata *metadata in requests) {
@@ -448,13 +493,13 @@ - (void)appendJSONRequests:(NSArray *)requests
includeToken:includeTokens
attachments:attachments];
}
-
+
SBJSON *writer = [[SBJSON alloc] init];
NSString *jsonBatch = [writer stringWithObject:batch];
[writer release];
[batch release];
- [body appendWithKey:kBatchKey formValue:jsonBatch];
+ [body appendWithKey:kBatchKey formValue:jsonBatch logger:logger];
}
//
@@ -482,6 +527,7 @@ - (void)addRequest:(FBRequestMetadata *)metadata
NSString *token = metadata.request.session.accessToken;
if (token) {
[metadata.request.parameters setObject:token forKey:kAccessTokenKey];
+ [self registerTokenToOmitFromLog:token];
}
}
@@ -538,12 +584,14 @@ - (BOOL)isAttachment:(id)item
- (void)appendAttachments:(NSDictionary *)attachments
toBody:(FBRequestBody *)body
addFormData:(BOOL)addFormData
-{
+ logger:(FBLogger *)logger
+{
+ // key is name for both, first case is string which we can print, second pass grabs object
if (addFormData) {
for (NSString *key in [attachments keyEnumerator]) {
NSObject *value = [attachments objectForKey:key];
if ([value isKindOfClass:[NSString class]]) {
- [body appendWithKey:key formValue:(NSString *)value];
+ [body appendWithKey:key formValue:(NSString *)value logger:logger];
}
}
}
@@ -551,9 +599,9 @@ - (void)appendAttachments:(NSDictionary *)attachments
for (NSString *key in [attachments keyEnumerator]) {
NSObject *value = [attachments objectForKey:key];
if ([value isKindOfClass:[UIImage class]]) {
- [body appendWithKey:key imageValue:(UIImage *)value];
+ [body appendWithKey:key imageValue:(UIImage *)value logger:logger];
} else if ([value isKindOfClass:[NSData class]]) {
- [body appendWithKey:key dataValue:(NSData *)value];
+ [body appendWithKey:key dataValue:(NSData *)value logger:logger];
}
}
}
@@ -601,12 +649,15 @@ - (void)completeWithResponse:(NSURLResponse *)response
}
int statusCode = self.urlResponse.statusCode;
- error = [self checkConnectionError:error statusCode:statusCode parsedJSONResponse:nil];
-
+
NSArray *results = nil;
if (!error) {
results = [self parseJSONResponse:data error:&error];
}
+
+ error = [self checkConnectionError:error
+ statusCode:statusCode
+ parsedJSONResponse:results];
if (!error) {
if ([self.requests count] != [results count]) {
@@ -616,7 +667,24 @@ - (void)completeWithResponse:(NSURLResponse *)response
parsedJSONResponse:results];
}
}
-
+
+ if (!error) {
+
+ [_logger appendFormat:@"Response <#%d>\nDuration: %lu msec\nSize: %d kB\nResponse Body:\n%@\n\n",
+ [_logger loggerSerialNumber],
+ [FBUtility currentTimeInMilliseconds] - _requestStartTime,
+ [data length],
+ results];
+
+ } else {
+
+ [_logger appendFormat:@"Response <#%d> <Error>:\n%@\n\n",
+ [_logger loggerSerialNumber],
+ [error localizedDescription]];
+
+ }
+ [_logger emitToNSLog];
+
if (self.deprecatedRequest) {
[self completeDeprecatedWithData:data results:results orError:error];
} else {
@@ -864,6 +932,13 @@ - (BOOL)isInvalidSessionError:(NSError *)error
return (idStatusCode && (((int)idStatusCode) == kRESTAPIAccessTokenErrorCode));
}
+- (void)registerTokenToOmitFromLog:(NSString *)token
+{
+ if (![[FBSession loggingBehavior] containsObject:FB_LOG_BEHAVIOR_INCLUDE_ACCESS_TOKENS]) {
+ [FBLogger registerStringToReplace:token replaceWith:@"ACCESS_TOKEN_REMOVED"];
+ }
+}
+
+ (NSString *)userAgent
{
static NSString *agent = nil;
@@ -877,4 +952,5 @@ + (NSString *)userAgent
return agent;
}
+
@end
View
36 src/FBSession.h
@@ -30,6 +30,21 @@
/*! helper macro to test for states that are terminal */
#define FB_SESSIONSTATEVALIDBIT (1 << 9)
+/*
+ * Constants defining logging behavior. Use with [FBSession setLoggingLevel]
+ */
+
+/*! Log requests from FBRequest* classes */
+#define FB_LOG_BEHAVIOR_FB_REQUESTS @"fb_log_fb_requests"
+
+/*! Log requests from FBURLConnection* classes */
+#define FB_LOG_BEHAVIOR_FBURL_CONNECTIONS @"fb_log_fburl_connections"
+
+/*! Include access token in logging. */
+#define FB_LOG_BEHAVIOR_INCLUDE_ACCESS_TOKENS @"fb_log_include_access_tokens"
+
+/*! Log session state transitions. */
+#define FB_LOG_BEHAVIOR_SESSION_STATE_TRANSITIONS @"fb_log_session_state_transitions"
/*!
@typedef FBSessionState enum
@@ -247,4 +262,25 @@ typedef void (^FBSessionStatusHandler)(FBSession *session,
*/
- (BOOL)handleOpenURL:(NSURL*)url;
+/*!
+ @methodgroup Class methods
+ */
+
+/*!
+ @method
+
+ @abstract retrieve the current FB SDK logging behavior.
+
+ */
++ (NSSet *)loggingBehavior;
+
+/*!
+ @method
+
+ @abstract set the current FB SDK logging behavior. Should consist of strings defined as constants with FB_LOG_BEHAVIOR_* above,
+ and can be constructed with [NSSet initWithObjects:]
+
+ */
++ (void)setLoggingBehavior:(NSSet *)loggingBehavior;
+
@end
View
25 src/FBSession.m
@@ -20,6 +20,7 @@
#import "FBSession+Internal.h"
#import "FBSessionTokenCachingStrategy.h"
#import "FBError.h"
+#import "FBLogger.h"
#import "FBUtility.h"
// the sooner we can remove these the better
@@ -59,6 +60,7 @@
// module scoped globals
static NSString *FBPLISTAppID = nil;
+static NSSet *g_loggingBehavior;
@interface FBSession () <FBLoginDialogDelegate> {
@private
@@ -366,7 +368,7 @@ - (NSString*)urlSchemeSuffix {
return _urlSchemeSuffix ? _urlSchemeSuffix : @"";
}
-// actually a private member, but wanted to be close to its public collegue
+// actually a private member, but wanted to be close to its public colleague
- (void)setUrlSchemeSuffix:(NSString*)newValue {
if (_urlSchemeSuffix != newValue) {
[_urlSchemeSuffix release];
@@ -375,6 +377,19 @@ - (void)setUrlSchemeSuffix:(NSString*)newValue {
}
#pragma mark -
+#pragma mark Class Methods
+
++ (NSSet *)loggingBehavior {
+ return g_loggingBehavior;
+}
+
++ (void)setLoggingBehavior:(NSSet *)newValue {
+ [g_loggingBehavior release];
+ g_loggingBehavior = newValue;
+ [g_loggingBehavior retain];
+}
+
+#pragma mark -
#pragma mark Private Members
@synthesize tokenCachingStrategy = _tokenCachingStrategy,
@@ -430,7 +445,8 @@ - (BOOL)transitionToState:(FBSessionState)state
// invalid transition short circuits
if (!isValidTransition) {
- NSLog(@"FBSession !transitionToState:%i fromState:%i", state, statePrior);
+ [FBLogger singleShotLogEntry:FB_LOG_BEHAVIOR_SESSION_STATE_TRANSITIONS
+ logEntry:[NSString stringWithFormat:@"FBSession !transitionToState:%i fromState:%i", state, statePrior]];
return false;
}
@@ -446,8 +462,9 @@ - (BOOL)transitionToState:(FBSessionState)state
}
// valid transitions notify
- NSLog(@"FBSession transitionToState:%i fromState:%i", state, statePrior);
-
+ [FBLogger singleShotLogEntry:FB_LOG_BEHAVIOR_SESSION_STATE_TRANSITIONS
+ logEntry:[NSString stringWithFormat:@"FBSession transitionToState:%i fromState:%i", state, statePrior]];
+
// identify whether we will update token and date, and what the values will be
BOOL changingTokenAndDate = false;
if (token && date) {
View
75 src/FBURLConnection.m
@@ -17,6 +17,9 @@
#import "FBURLConnection.h"
#import "FBError.h"
#import "FBDataDiskCache.h"
+#import "FBSession.h"
+#import "FBLogger.h"
+#import "FBUtility.h"
static NSArray* _cdnHosts;
@@ -26,9 +29,16 @@ @interface FBURLConnection ()
@property (nonatomic, retain) NSMutableData *data;
@property (nonatomic, copy) FBURLConnectionHandler handler;
@property (nonatomic, retain) NSURLResponse *response;
+@property (nonatomic) unsigned long requestStartTime;
+@property (nonatomic, readonly) NSUInteger loggerSerialNumber;
- (BOOL)isCDNURL:(NSURL *)url;
+- (void)invokeHandler:(FBURLConnectionHandler)handler
+ error:(NSError *)error
+ response:(NSURLResponse *)response
+ responseData:(NSData *)responseData;
+
@end
@implementation FBURLConnection
@@ -36,6 +46,8 @@ @implementation FBURLConnection
@synthesize connection = _connection;
@synthesize data = _data;
@synthesize handler = _handler;
+@synthesize loggerSerialNumber = _loggerSerialNumber;
+@synthesize requestStartTime = _requestStartTime;
@synthesize response = _response;
#pragma mark - Lifecycle
@@ -65,14 +77,28 @@ - (FBURLConnection *)initWithRequest:(NSURLRequest *)request
NSURL* url = request.URL;
NSData* cachedData = [[FBDataDiskCache sharedCache] dataForURL:url];
if (cachedData) {
+ [FBLogger singleShotLogEntry:FB_LOG_BEHAVIOR_FBURL_CONNECTIONS
+ formatString:@"FBUrlConnection: <#%d>. Cached response %d kB\n",
+ [url absoluteString],
+ [cachedData length] / 1024];
+
// TODO: It seems wrong to call this within init. There are cases
// with UI where this is not ideal. We should talk about this.
- handler(self, nil, nil, cachedData);
+ handler(self, nil, nil, cachedData);
+
} else {
+
+ _requestStartTime = [FBUtility currentTimeInMilliseconds];
+ _loggerSerialNumber = [FBLogger newSerialNumber];
_connection = [[NSURLConnection alloc]
initWithRequest:request
delegate:self];
_data = [[NSMutableData alloc] init];
+
+ [FBLogger singleShotLogEntry:FB_LOG_BEHAVIOR_FBURL_CONNECTIONS
+ formatString:@"FBURLConnection <#%d>:\n URL: '%@'\n\n",
+ _loggerSerialNumber,
+ [url absoluteString]];
self.handler = handler;
}
@@ -80,6 +106,45 @@ - (FBURLConnection *)initWithRequest:(NSURLRequest *)request
return self;
}
+- (void)invokeHandler:(FBURLConnectionHandler)handler
+ error:(NSError *)error
+ response:(NSURLResponse *)response
+ responseData:(NSData *)responseData
+{
+ NSString *logEntry;
+
+ if (error) {
+
+ logEntry = [NSString
+ stringWithFormat:@"FBURLConnection <#%d>:\n Error: '%@'",
+ _loggerSerialNumber,
+ [error localizedDescription]];
+
+ } else {
+
+ // Basic FBURLConnection logging just prints out the URL. FBRequest logging provides more details.
+ NSString *mimeType = [response MIMEType];
+ NSMutableString *mutableLogEntry = [NSMutableString stringWithFormat:@"FBURLConnection <#%d>:\n Duration: %lu msec\nResponse Size: %d kB\n MIME type: %@\n",
+ _loggerSerialNumber,
+ [FBUtility currentTimeInMilliseconds] - _requestStartTime,
+ [responseData length] / 1024,
+ mimeType];
+
+ if ([mimeType isEqualToString:@"text/javascript"]) {
+ NSString *responseUTF8 = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
+ [mutableLogEntry appendFormat:@" Response:\n%@\n\n", responseUTF8];
+ [responseUTF8 release];
+ }
+
+ logEntry = mutableLogEntry;
+ }
+
+ [FBLogger singleShotLogEntry:FB_LOG_BEHAVIOR_FBURL_CONNECTIONS
+ logEntry:logEntry];
+
+ handler(self, error, response, responseData);
+}
+
- (void)dealloc
{
[_response release];
@@ -107,7 +172,7 @@ - (void)cancel
FBURLConnectionHandler handler = [self.handler retain];
self.handler = nil;
@try {
- handler(self, error, nil, nil);
+ [self invokeHandler:handler error:error response:nil responseData:nil];
} @finally {
[handler release];
[self release];
@@ -133,7 +198,7 @@ - (void)connection:(NSURLConnection *)connection
{
// TODO: translate well-known errors
@try {
- self.handler(self, error, nil, nil);
+ [self invokeHandler:self.handler error:error response:nil responseData:nil];
} @finally {
self.handler = nil;
}
@@ -148,7 +213,7 @@ - (void)connectionDidFinishLoading:(NSURLConnection *)connection
}
@try {
- self.handler(self, nil, self.response, self.data);
+ [self invokeHandler:self.handler error:nil response:self.response responseData:self.data];
} @finally {
self.handler = nil;
}
@@ -172,7 +237,7 @@ -(NSURLRequest *)connection:(NSURLConnection *)connection
MIMEType:@"application/octet-stream"
expectedContentLength:cachedData.length
textEncodingName:@"utf8"];
- self.handler(self, nil, cacheResponse, cachedData);
+ [self invokeHandler:self.handler error:nil response:cacheResponse responseData:cachedData];
[cacheResponse release];
} @finally {
self.handler = nil;
View
2  src/FBUtility.h
@@ -22,4 +22,6 @@
+ (NSString *)stringByURLDecodingString:(NSString*)escapedString;
+ (NSString*)stringByURLEncodingString:(NSString*)unescapedString;
++ (unsigned long)currentTimeInMilliseconds;
+
@end
View
8 src/FBUtility.m
@@ -15,6 +15,8 @@
*/
#import "FBUtility.h"
+#import "FBSession.h"
+#include <sys/time.h>
@implementation FBUtility
@@ -66,4 +68,10 @@ + (NSString*)stringByURLEncodingString:(NSString*)unescapedString {
return result;
}
++ (unsigned long)currentTimeInMilliseconds {
+ struct timeval time;
+ gettimeofday(&time, NULL);
+ return (time.tv_sec * 1000) + (time.tv_usec / 1000);
+}
+
@end
View
8 src/facebook-ios-sdk.xcodeproj/project.pbxproj
@@ -7,6 +7,8 @@
objects = {
/* Begin PBXBuildFile section */
+ 5F7CB4201553ACC600C183CF /* FBLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = 5F7CB41E1553ACC600C183CF /* FBLogger.h */; };
+ 5F7CB4211553ACC600C183CF /* FBLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F7CB41F1553ACC600C183CF /* FBLogger.m */; };
8409694C1541F41100479AD9 /* FBOpenGraphAction.h in Headers */ = {isa = PBXBuildFile; fileRef = 8409694B1541F41100479AD9 /* FBOpenGraphAction.h */; settings = {ATTRIBUTES = (Public, ); }; };
840FDCBD152B5D9200F9C927 /* FBFrictionlessRequestSettings.h in Headers */ = {isa = PBXBuildFile; fileRef = 840FDCBB152B5D9200F9C927 /* FBFrictionlessRequestSettings.h */; };
840FDCBE152B5D9200F9C927 /* FBFrictionlessRequestSettings.m in Sources */ = {isa = PBXBuildFile; fileRef = 840FDCBC152B5D9200F9C927 /* FBFrictionlessRequestSettings.m */; };
@@ -121,6 +123,8 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
+ 5F7CB41E1553ACC600C183CF /* FBLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBLogger.h; sourceTree = "<group>"; };
+ 5F7CB41F1553ACC600C183CF /* FBLogger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBLogger.m; sourceTree = "<group>"; };
8409694B1541F41100479AD9 /* FBOpenGraphAction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBOpenGraphAction.h; sourceTree = "<group>"; };
840FDCBB152B5D9200F9C927 /* FBFrictionlessRequestSettings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBFrictionlessRequestSettings.h; sourceTree = "<group>"; };
840FDCBC152B5D9200F9C927 /* FBFrictionlessRequestSettings.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBFrictionlessRequestSettings.m; sourceTree = "<group>"; };
@@ -304,6 +308,8 @@
E2B99CA515489FC5002AEA86 /* FBGraphObjectTableView.m */,
84E0C9F81535ED5A00778DA4 /* FBGraphUser.h */,
84E2DA27153492DE007F886C /* FBGraphPlace.h */,
+ 5F7CB41E1553ACC600C183CF /* FBLogger.h */,
+ 5F7CB41F1553ACC600C183CF /* FBLogger.m */,
AEA93B0611D5293B000A4545 /* FBLoginDialog.h */,
AEA93B0711D5293B000A4545 /* FBLoginDialog.m */,
8409694B1541F41100479AD9 /* FBOpenGraphAction.h */,
@@ -432,6 +438,7 @@
E2B99CA615489FC5002AEA86 /* FBGraphObjectTableView.h in Headers */,
E2B99CF11549CD7F002AEA86 /* FBGraphObjectTableDataSource.h in Headers */,
E2B99CF51549E02A002AEA86 /* FBGraphObjectTableSelection.h in Headers */,
+ 5F7CB4201553ACC600C183CF /* FBLogger.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -589,6 +596,7 @@
E2B99CA715489FC5002AEA86 /* FBGraphObjectTableView.m in Sources */,
E2B99CF21549CD7F002AEA86 /* FBGraphObjectTableDataSource.m in Sources */,
E2B99CF61549E02A002AEA86 /* FBGraphObjectTableSelection.m in Sources */,
+ 5F7CB4211553ACC600C183CF /* FBLogger.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Please sign in to comment.
Something went wrong with that request. Please try again.