Browse Source

Using RFC3339Nano in notices and iOS framework

Amir Khan 8 years ago
parent
commit
b962745f7e

+ 2 - 1
MobileLibrary/iOS/PsiphonTunnel/PsiphonTunnel/PsiphonTunnel.h

@@ -141,9 +141,10 @@ typedef NS_ENUM(NSInteger, PsiphonConnectionState)
 /*!
  Gets runtime errors info that may be useful for debugging.
  @param message  The diagnostic message string.
+ @param timestamp RFC3339 encoded timestamp.
  Swift: @code func onDiagnosticMessage(_ message: String) @endcode
  */
-- (void)onDiagnosticMessage:(NSString * _Nonnull)message;
+- (void)onDiagnosticMessage:(NSString * _Nonnull)message withTimestamp:(NSString * _Nonnull)timestamp;
 
 /*!
  Called when the tunnel is in the process of connecting.

+ 23 - 4
MobileLibrary/iOS/PsiphonTunnel/PsiphonTunnel/PsiphonTunnel.m

@@ -31,7 +31,6 @@
 #import <resolv.h>
 #import <netdb.h>
 
-
 #define GOOGLE_DNS_1 @"8.8.4.4"
 #define GOOGLE_DNS_2 @"8.8.8.8"
 
@@ -63,6 +62,11 @@
     NSString *secondaryGoogleDNS;
     _Atomic BOOL useInitialDNS; // initialDNSCache validity flag.
     NSArray<NSString *> *initialDNSCache;  // This cache becomes void if internetReachabilityChanged is called.
+    
+    // Log timestamp formatter
+    // Note: NSDateFormatter is threadsafe.
+    NSDateFormatter *rfc3339Formatter;
+    
 }
 
 - (id)init {
@@ -92,6 +96,15 @@
     self->initialDNSCache = [self getDNSServers];
     atomic_init(&self->useInitialDNS, [self->initialDNSCache count] > 0);
 
+    // RFC3339 formatter.
+    NSLocale *enUSPOSIXLocale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"];
+    rfc3339Formatter = [[NSDateFormatter alloc] init];
+    [rfc3339Formatter setLocale:enUSPOSIXLocale];
+    
+    // Example: notice time format from Go code: "2006-01-02T15:04:05.999999999Z07:00"
+    [rfc3339Formatter setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ss.SSSSSSSSSZZZZZ"];
+    [rfc3339Formatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
+    
     return self;
 }
 
@@ -785,9 +798,10 @@
         }
         
         NSString *dataStr = [[[SBJson4Writer alloc] init] stringWithObject:data];
+        NSString *timestampStr = notice[@"timestamp"];
 
         NSString *diagnosticMessage = [NSString stringWithFormat:@"%@: %@", noticeType, dataStr];
-        [self logMessage:diagnosticMessage];
+        [self logMessage:diagnosticMessage withTimestamp:timestampStr];
     }
 }
 
@@ -984,9 +998,14 @@
 }
 
 - (void)logMessage:(NSString *)message {
-    if ([self.tunneledAppDelegate respondsToSelector:@selector(onDiagnosticMessage:)]) {
+    NSString *timestamp = [rfc3339Formatter stringFromDate:[NSDate date]];
+    [self logMessage:message withTimestamp:timestamp];
+}
+
+- (void)logMessage:(NSString *)message withTimestamp:(NSString * _Nonnull)timestamp {
+    if ([self.tunneledAppDelegate respondsToSelector:@selector(onDiagnosticMessage:withTimestamp:)]) {
         dispatch_async(self->callbackQueue, ^{
-            [self.tunneledAppDelegate onDiagnosticMessage:message];
+            [self.tunneledAppDelegate onDiagnosticMessage:message withTimestamp:timestamp];
         });
     }
 }

+ 4 - 4
psiphon/notice.go

@@ -64,7 +64,7 @@ func GetEmitDiagnoticNotices() bool {
 //
 // Notices are encoded in JSON. Here's an example:
 //
-// {"data":{"message":"shutdown operate tunnel"},"noticeType":"Info","showUser":false,"timestamp":"2015-01-28T17:35:13Z"}
+// {"data":{"message":"shutdown operate tunnel"},"noticeType":"Info","showUser":false,"timestamp":"2006-01-02T15:04:05.999999999Z07:00"}
 //
 // All notices have the following fields:
 // - "noticeType": the type of notice, which indicates the meaning of the notice along with what's in the data payload.
@@ -73,7 +73,7 @@ func GetEmitDiagnoticNotices() bool {
 // - "showUser": whether the information should be displayed to the user. For example, this flag is set for "SocksProxyPortInUse"
 // as the user should be informed that their configured choice of listening port could not be used. Core clients should
 // anticipate that the core will add additional "showUser"=true notices in the future and emit at least the raw notice.
-// - "timestamp": UTC timezone, RFC3339 format timestamp for notice event
+// - "timestamp": UTC timezone, RFC3339Nano format timestamp for notice event
 //
 // See the Notice* functions for details on each notice meaning and payload.
 //
@@ -100,7 +100,7 @@ func outputNotice(noticeType string, noticeFlags uint32, args ...interface{}) {
 	obj["noticeType"] = noticeType
 	obj["showUser"] = (noticeFlags&noticeShowUser != 0)
 	obj["data"] = noticeData
-	obj["timestamp"] = time.Now().UTC().Format(time.RFC3339)
+	obj["timestamp"] = time.Now().UTC().Format(time.RFC3339Nano)
 	for i := 0; i < len(args)-1; i += 2 {
 		name, ok := args[i].(string)
 		value := args[i+1]
@@ -123,7 +123,7 @@ func outputNotice(noticeType string, noticeFlags uint32, args ...interface{}) {
 		obj["data"] = map[string]interface{}{
 			"message": fmt.Sprintf("Marshal notice failed: %s", common.ContextError(err)),
 		}
-		obj["timestamp"] = time.Now().UTC().Format(time.RFC3339)
+		obj["timestamp"] = time.Now().UTC().Format(time.RFC3339Nano)
 		encodedJson, err := json.Marshal(obj)
 		if err == nil {
 			output = string(encodedJson)