From ab61e68aea6b963e720833760be301e2ac1c13ff Mon Sep 17 00:00:00 2001 From: Michael Haney Date: Mon, 10 Dec 2018 14:30:29 -0800 Subject: [PATCH 1/8] Add timekeeping infrastructure. --- .../GoogleDataLogger/Classes/GDLLogClock.h | 40 +++++++++++++++++++ .../GoogleDataLogger/Classes/GDLLogClock.m | 21 ++++++++++ 2 files changed, 61 insertions(+) create mode 100644 GoogleDataLogger/GoogleDataLogger/Classes/GDLLogClock.h create mode 100644 GoogleDataLogger/GoogleDataLogger/Classes/GDLLogClock.m diff --git a/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogClock.h b/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogClock.h new file mode 100644 index 00000000000..107b4a8a4db --- /dev/null +++ b/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogClock.h @@ -0,0 +1,40 @@ +/* + * Copyright 2018 Google + * + * 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 + +NS_ASSUME_NONNULL_BEGIN + +/** A struct to hold data pertaining to a snapshot in time. */ +typedef struct { + /** The current time in millis. */ + int64_t timeMillis; + + /** The device uptime in millis. */ + int64_t uptimeMillis; + + /** The timezone offset in millis. */ + int64_t timezoneOffsetMillis; +} GDLLogClockSnapshot; + +/** This class manages the device clock and produces snapshots of the current time. */ +@interface GDLLogClock : NSObject + +// TODO(mikehaney24): - (GDLLogClockSnapshot)snapshot; + +@end + +NS_ASSUME_NONNULL_END diff --git a/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogClock.m b/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogClock.m new file mode 100644 index 00000000000..0935d8ba841 --- /dev/null +++ b/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogClock.m @@ -0,0 +1,21 @@ +/* + * Copyright 2018 Google + * + * 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 "GDLLogClock.h" + +@implementation GDLLogClock + +@end From aa3a009eae37407cb9c7398bfa51506e61081209 Mon Sep 17 00:00:00 2001 From: Michael Haney Date: Mon, 10 Dec 2018 14:30:49 -0800 Subject: [PATCH 2/8] Add the log proto protocol. --- .../GoogleDataLogger/Classes/GDLLogProto.h | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 GoogleDataLogger/GoogleDataLogger/Classes/GDLLogProto.h diff --git a/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogProto.h b/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogProto.h new file mode 100644 index 00000000000..e9fc41b458c --- /dev/null +++ b/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogProto.h @@ -0,0 +1,23 @@ +/* + * Copyright 2018 Google + * + * 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 + +@protocol GDLLogProto + +- (NSData *)protoBytes; + +@end From b538fa899d5c9c8ef06913f03ee841ef4918e678 Mon Sep 17 00:00:00 2001 From: Michael Haney Date: Mon, 10 Dec 2018 14:32:06 -0800 Subject: [PATCH 3/8] Flesh out the log event a bit. --- .../GoogleDataLogger/Classes/GDLLogEvent.h | 48 +++++++++++++++++++ .../GoogleDataLogger/Classes/GDLLogEvent.m | 9 ++++ .../GoogleDataLogger/Classes/GDLLogger.m | 2 +- GoogleDataLogger/Tests/GDLLogEventTest.m | 3 +- 4 files changed, 60 insertions(+), 2 deletions(-) diff --git a/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogEvent.h b/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogEvent.h index def5422e038..2e057de8d55 100644 --- a/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogEvent.h +++ b/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogEvent.h @@ -16,6 +16,54 @@ #import +#import "GDLLogClock.h" +#import "GDLLogProto.h" + +NS_ASSUME_NONNULL_BEGIN + +/** The different possible log quality of service specifiers. High values indicate high priority. */ +typedef NS_ENUM(NSInteger, GDLLogQoS) { + /** The QoS tier wasn't set. */ + GDLLogQoSUnknown = 0, + + /** This log is internal telemetry data that should not be sent on its own if possible. */ + GDLLogQoSTelemetry = 1, + + /** This log should be sent, but in a batch only roughly once per day. */ + GDLLogQoSDaily = 2, + + /** This log should be sent when requested by the uploader. */ + GDLLogQosDefault = 3, + + /** This log should be sent immediately along with any other data that can be batched. */ + GDLLogQoSFast = 4 +}; + @interface GDLLogEvent : NSObject +/** The log map identifier, to allow backends to map the extension property to a proto. */ +@property(readonly, nonatomic) NSString *logMapID; + +/** The log object itself, encapsulated in the transport of your choice, as long as it implements + * the GDLLogProto protocol. */ +@property(nonatomic) id extension; + +/** The quality of service tier this log belongs to. */ +@property(nonatomic) GDLLogQoS qosTier; + +/** The clock snapshot at the time of logging. */ +@property(nonatomic) GDLLogClockSnapshot clockSnapshot; + +// Please use the designated initializer. +- (instancetype)init NS_UNAVAILABLE; + +/** Initializes an instance using the given logMapID. + * + * @param logMapID The log map identifier. + * @return An instance of this class. + */ +- (instancetype)initWithLogMapID:(NSString *)logMapID NS_DESIGNATED_INITIALIZER; + @end + +NS_ASSUME_NONNULL_END diff --git a/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogEvent.m b/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogEvent.m index 813af083efb..5d408e4c093 100644 --- a/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogEvent.m +++ b/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogEvent.m @@ -18,4 +18,13 @@ @implementation GDLLogEvent +- (instancetype)initWithLogMapID:(NSString *)logMapID { + NSAssert(logMapID.length > 0, @"Please give a valid log map ID"); + self = [super init]; + if (self) { + _logMapID = logMapID; + } + return self; +} + @end diff --git a/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogger.m b/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogger.m index 70f4e921d69..f5176ed54bf 100644 --- a/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogger.m +++ b/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogger.m @@ -60,7 +60,7 @@ - (void)logDataEvent:(GDLLogEvent *)logEvent { } - (GDLLogEvent *)newEvent { - return [[GDLLogEvent alloc] init]; + return [[GDLLogEvent alloc] initWithLogMapID:_logMapID]; } @end diff --git a/GoogleDataLogger/Tests/GDLLogEventTest.m b/GoogleDataLogger/Tests/GDLLogEventTest.m index 9d8acc4caa0..131dd61c515 100644 --- a/GoogleDataLogger/Tests/GDLLogEventTest.m +++ b/GoogleDataLogger/Tests/GDLLogEventTest.m @@ -25,7 +25,8 @@ @interface GDLLogEventTest : XCTestCase @implementation GDLLogEventTest - (void)testInit { - XCTAssertNotNil([[GDLLogEvent alloc] init]); + XCTAssertNotNil([[GDLLogEvent alloc] initWithLogMapID:@"1"]); + XCTAssertThrows([[GDLLogEvent alloc] initWithLogMapID:@""]); } @end From 4a602238b50cc98cc7e8a0ee7327e04764d976d7 Mon Sep 17 00:00:00 2001 From: Michael Haney Date: Mon, 10 Dec 2018 14:32:47 -0800 Subject: [PATCH 4/8] Flesh out the log writer. --- .../GoogleDataLogger/Classes/GDLLogWriter.h | 18 ++++++++ .../GoogleDataLogger/Classes/GDLLogWriter.m | 43 ++++++++++++++++++- GoogleDataLogger/Tests/GDLLogWriterTest.m | 36 ++++++++++++++++ 3 files changed, 96 insertions(+), 1 deletion(-) diff --git a/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogWriter.h b/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogWriter.h index c11dffe9f32..96c1abd63b9 100644 --- a/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogWriter.h +++ b/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogWriter.h @@ -16,7 +16,25 @@ #import +@class GDLLogEvent; + +@protocol GDLLogTransformer; + /** Manages the writing and log-time transforming of logs. */ @interface GDLLogWriter : NSObject +/** Instantiates or returns the log writer singleton. + * + * @return The singleton instance of the log writer. + */ ++ (instancetype)sharedInstance; + +/** Writes the result of applying the given transformers' -transform method on the given log. + * + * @param log The log to apply transformers on. + * @param logTransformers The list of transformers to apply. + */ +- (void)writeLog:(GDLLogEvent *)log + afterApplyingTransformers:(nullable NSArray> *)logTransformers; + @end diff --git a/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogWriter.m b/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogWriter.m index 1f7453dbfce..28b600e419f 100644 --- a/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogWriter.m +++ b/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogWriter.m @@ -16,6 +16,47 @@ #import "GDLLogWriter.h" -@implementation GDLLogWriter : NSObject +#import + +#import "GDLLogStorage.h" + +@implementation GDLLogWriter { + /** The queue on which all work will occur. */ + dispatch_queue_t _logWritingQueue; +} + +// This class doesn't have to be a singleton, but allocating an instance for every logger could be +// wasteful. ++ (instancetype)sharedInstance { + static GDLLogWriter *logWriter; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + logWriter = [[self alloc] init]; + }); + return logWriter; +} + +- (instancetype)init { + self = [super init]; + if (self) { + _logWritingQueue = dispatch_queue_create("com.google.GDLLogWriter", DISPATCH_QUEUE_SERIAL); + } + return self; +} + +- (void)writeLog:(GDLLogEvent *)log + afterApplyingTransformers:(NSArray> *)logTransformers { + NSAssert(log, @"You can't write a nil log"); + dispatch_async(_logWritingQueue, ^{ + GDLLogEvent *transformedLog = log; + for (id transformer in logTransformers) { + transformedLog = [transformer transform:transformedLog]; + if (!transformedLog) { + return; + } + } + // TODO(mikehaney24): [[GDLLogStorage sharedInstance] storeLog:transformedLog]; + }); +} @end diff --git a/GoogleDataLogger/Tests/GDLLogWriterTest.m b/GoogleDataLogger/Tests/GDLLogWriterTest.m index 914b7e62ec4..dc358326e81 100644 --- a/GoogleDataLogger/Tests/GDLLogWriterTest.m +++ b/GoogleDataLogger/Tests/GDLLogWriterTest.m @@ -16,8 +16,23 @@ #import +#import + +#import "GDLLogEvent.h" #import "GDLLogWriter.h" +@interface GDLLogWriterTestNilingTransformer : NSObject + +@end + +@implementation GDLLogWriterTestNilingTransformer + +- (GDLLogEvent *)transform:(GDLLogEvent *)logEvent { + return nil; +} + +@end + @interface GDLLogWriterTest : XCTestCase @end @@ -29,4 +44,25 @@ - (void)testInit { XCTAssertNotNil([[GDLLogWriter alloc] init]); } +/** Tests the pointer equality of result of the -sharedInstance method. */ +- (void)testSharedInstance { + XCTAssertEqual([GDLLogWriter sharedInstance], [GDLLogWriter sharedInstance]); +} + +- (void)testWriteLogWithoutTransformers { + GDLLogWriter *writer = [GDLLogWriter sharedInstance]; + GDLLogEvent *log = [[GDLLogEvent alloc] initWithLogMapID:@"1"]; + XCTAssertNoThrow([writer writeLog:log afterApplyingTransformers:nil]); + // TODO(mikehaney24): Assert that storage contains the log. +} + +- (void)testWriteLogWithTransformersThatNilTheLog { + GDLLogWriter *writer = [GDLLogWriter sharedInstance]; + GDLLogEvent *log = [[GDLLogEvent alloc] initWithLogMapID:@"2"]; + NSArray> *transformers = + @[ [[GDLLogWriterTestNilingTransformer alloc] init] ]; + XCTAssertNoThrow([writer writeLog:log afterApplyingTransformers:transformers]); + // TODO(mikehaney24): Assert that storage does not contain the log. +} + @end From 66870e638b4cadd8f13eea1274c50708c03daa32 Mon Sep 17 00:00:00 2001 From: Michael Haney Date: Mon, 10 Dec 2018 14:58:09 -0800 Subject: [PATCH 5/8] Put in comments for the log proto protocol. --- GoogleDataLogger/GoogleDataLogger/Classes/GDLLogProto.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogProto.h b/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogProto.h index e9fc41b458c..2f424bd0b9c 100644 --- a/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogProto.h +++ b/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogProto.h @@ -16,8 +16,15 @@ #import +/** This protocol defines the common interface that log protos should implement regardless of the + * underlying transport technology (protobuf, nanopb, etc). + */ @protocol GDLLogProto +/** Returns the serialized proto bytes of the implementing log proto. + * + * @return the serialized proto bytes of the implementing log proto. + */ - (NSData *)protoBytes; @end From d0728ed16f5ef592f06fc96a3c2593b90c01ea9c Mon Sep 17 00:00:00 2001 From: Michael Haney Date: Mon, 10 Dec 2018 19:17:37 -0800 Subject: [PATCH 6/8] Move queue to a private header and update the TODO. --- .../GoogleDataLogger/Classes/GDLLogWriter.m | 6 ++--- .../Classes/Private/GDLLogWriter_Private.h | 24 +++++++++++++++++++ GoogleDataLogger/Tests/GDLLogWriterTest.m | 9 +++++-- 3 files changed, 33 insertions(+), 6 deletions(-) create mode 100644 GoogleDataLogger/GoogleDataLogger/Classes/Private/GDLLogWriter_Private.h diff --git a/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogWriter.m b/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogWriter.m index 28b600e419f..208e1954fc8 100644 --- a/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogWriter.m +++ b/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogWriter.m @@ -15,15 +15,13 @@ */ #import "GDLLogWriter.h" +#import "GDLLogWriter_Private.h" #import #import "GDLLogStorage.h" -@implementation GDLLogWriter { - /** The queue on which all work will occur. */ - dispatch_queue_t _logWritingQueue; -} +@implementation GDLLogWriter // This class doesn't have to be a singleton, but allocating an instance for every logger could be // wasteful. diff --git a/GoogleDataLogger/GoogleDataLogger/Classes/Private/GDLLogWriter_Private.h b/GoogleDataLogger/GoogleDataLogger/Classes/Private/GDLLogWriter_Private.h new file mode 100644 index 00000000000..6d9061222b7 --- /dev/null +++ b/GoogleDataLogger/GoogleDataLogger/Classes/Private/GDLLogWriter_Private.h @@ -0,0 +1,24 @@ +/* + * Copyright 2018 Google + * + * 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 "GDLLogWriter.h" + +@interface GDLLogWriter () + +/** The queue on which all work will occur. */ +@property(nonatomic) dispatch_queue_t logWritingQueue; + +@end diff --git a/GoogleDataLogger/Tests/GDLLogWriterTest.m b/GoogleDataLogger/Tests/GDLLogWriterTest.m index dc358326e81..7906cf2fe23 100644 --- a/GoogleDataLogger/Tests/GDLLogWriterTest.m +++ b/GoogleDataLogger/Tests/GDLLogWriterTest.m @@ -20,6 +20,7 @@ #import "GDLLogEvent.h" #import "GDLLogWriter.h" +#import "GDLLogWriter_Private.h" @interface GDLLogWriterTestNilingTransformer : NSObject @@ -53,7 +54,9 @@ - (void)testWriteLogWithoutTransformers { GDLLogWriter *writer = [GDLLogWriter sharedInstance]; GDLLogEvent *log = [[GDLLogEvent alloc] initWithLogMapID:@"1"]; XCTAssertNoThrow([writer writeLog:log afterApplyingTransformers:nil]); - // TODO(mikehaney24): Assert that storage contains the log. + dispatch_sync(writer.logWritingQueue, ^{ + // TODO(mikehaney24): Assert that storage contains the log. + }); } - (void)testWriteLogWithTransformersThatNilTheLog { @@ -62,7 +65,9 @@ - (void)testWriteLogWithTransformersThatNilTheLog { NSArray> *transformers = @[ [[GDLLogWriterTestNilingTransformer alloc] init] ]; XCTAssertNoThrow([writer writeLog:log afterApplyingTransformers:transformers]); - // TODO(mikehaney24): Assert that storage does not contain the log. + dispatch_sync(writer.logWritingQueue, ^{ + // TODO(mikehaney24): Assert that storage does not contain the log. + }); } @end From 3a962dc3da28814141b7c7271f2ff5b5be9f836d Mon Sep 17 00:00:00 2001 From: Michael Haney Date: Mon, 10 Dec 2018 19:18:18 -0800 Subject: [PATCH 7/8] Add comment about the QoS tier --- GoogleDataLogger/GoogleDataLogger/Classes/GDLLogEvent.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogEvent.h b/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogEvent.h index 2e057de8d55..64eee5411ca 100644 --- a/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogEvent.h +++ b/GoogleDataLogger/GoogleDataLogger/Classes/GDLLogEvent.h @@ -23,7 +23,7 @@ NS_ASSUME_NONNULL_BEGIN /** The different possible log quality of service specifiers. High values indicate high priority. */ typedef NS_ENUM(NSInteger, GDLLogQoS) { - /** The QoS tier wasn't set. */ + /** The QoS tier wasn't set, and won't ever be sent. */ GDLLogQoSUnknown = 0, /** This log is internal telemetry data that should not be sent on its own if possible. */ From 0912bb665d53cd125e698813bad12976888e8551 Mon Sep 17 00:00:00 2001 From: Michael Haney Date: Mon, 10 Dec 2018 19:51:32 -0800 Subject: [PATCH 8/8] Fix style --- GoogleDataLogger/Tests/GDLLogWriterTest.m | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/GoogleDataLogger/Tests/GDLLogWriterTest.m b/GoogleDataLogger/Tests/GDLLogWriterTest.m index 7906cf2fe23..ee902f872d7 100644 --- a/GoogleDataLogger/Tests/GDLLogWriterTest.m +++ b/GoogleDataLogger/Tests/GDLLogWriterTest.m @@ -55,8 +55,8 @@ - (void)testWriteLogWithoutTransformers { GDLLogEvent *log = [[GDLLogEvent alloc] initWithLogMapID:@"1"]; XCTAssertNoThrow([writer writeLog:log afterApplyingTransformers:nil]); dispatch_sync(writer.logWritingQueue, ^{ - // TODO(mikehaney24): Assert that storage contains the log. - }); + // TODO(mikehaney24): Assert that storage contains the log. + }); } - (void)testWriteLogWithTransformersThatNilTheLog { @@ -66,8 +66,8 @@ - (void)testWriteLogWithTransformersThatNilTheLog { @[ [[GDLLogWriterTestNilingTransformer alloc] init] ]; XCTAssertNoThrow([writer writeLog:log afterApplyingTransformers:transformers]); dispatch_sync(writer.logWritingQueue, ^{ - // TODO(mikehaney24): Assert that storage does not contain the log. - }); + // TODO(mikehaney24): Assert that storage does not contain the log. + }); } @end