Skip to content

Commit 29e9416

Browse files
authored
[Sessions] Log events to console command line flag, fix encoding (#10428)
1 parent 09ae075 commit 29e9416

File tree

8 files changed

+303
-48
lines changed

8 files changed

+303
-48
lines changed
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
//
2+
// Copyright 2022 Google LLC
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
import Foundation
17+
18+
class DevEventConsoleLogger: EventGDTLoggerProtocol {
19+
private let commandLineArgument = "-FIRSessionsDebugEvents"
20+
21+
func logEvent(event: SessionStartEvent, completion: @escaping (Result<Void, Error>) -> Void) {
22+
if !ProcessInfo.processInfo.arguments.contains(commandLineArgument) {
23+
return
24+
}
25+
26+
let proto = event.encodeDecodeEvent()
27+
prettyPrint(proto: proto)
28+
}
29+
30+
func prettyPrint(proto: firebase_appquality_sessions_SessionEvent) {
31+
let logOutput = """
32+
Logging Session Event due to \"\(commandLineArgument)\" command line argument
33+
Session Event:
34+
event_type: \(proto.event_type)
35+
session_data
36+
session_id: \(proto.session_data.session_id.description)
37+
previous_session_id: \(proto.session_data.previous_session_id.description)
38+
event_timestamp_us: \(proto.session_data.event_timestamp_us)
39+
data_collection_status
40+
crashlytics: \(proto.session_data.data_collection_status.crashlytics)
41+
performance: \(proto.session_data.data_collection_status.performance)
42+
application_info
43+
app_id: \(proto.application_info.app_id.description)
44+
device_model: \(proto.application_info.device_model.description)
45+
development_platform_name: \(proto.application_info.development_platform_name.description)
46+
development_platform_version: \(proto.application_info.development_platform_version
47+
.description)
48+
session_sdk_version: \(proto.application_info.session_sdk_version.description)
49+
apple_app_info
50+
bundle_short_version: \(proto.application_info.apple_app_info.bundle_short_version
51+
.description)
52+
network_connection_info
53+
network_type: \(proto.application_info.apple_app_info.network_connection_info
54+
.network_type.rawValue)
55+
mobile_subtype: \(proto.application_info.apple_app_info.network_connection_info
56+
.mobile_subtype.rawValue)
57+
os_name: \(proto.application_info.apple_app_info.os_name.description)
58+
mcc_mnc: \(proto.application_info.apple_app_info.mcc_mnc.description)
59+
"""
60+
61+
Logger.logInfo(logOutput)
62+
}
63+
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
//
2+
// Copyright 2022 Google LLC
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
import Foundation
17+
18+
///
19+
/// These extensions allows us to console log properties of our Session Events
20+
/// proto for development and debugging purposes without having to call decode
21+
/// on each field manually. Instead you can read `<field>.description`.
22+
///
23+
24+
extension firebase_appquality_sessions_EventType: CustomStringConvertible {
25+
public var description: String {
26+
switch self {
27+
case firebase_appquality_sessions_EventType_SESSION_START:
28+
return "SESSION_START"
29+
case firebase_appquality_sessions_EventType_EVENT_TYPE_UNKNOWN:
30+
return "UNKNOWN"
31+
default:
32+
return "Unrecognized EventType. Please update the firebase_appquality_sessions_EventType CustomStringConvertible extension"
33+
}
34+
}
35+
}
36+
37+
extension firebase_appquality_sessions_DataCollectionState: CustomStringConvertible {
38+
public var description: String {
39+
switch self {
40+
case firebase_appquality_sessions_DataCollectionState_COLLECTION_ENABLED:
41+
return "ENABLED"
42+
case firebase_appquality_sessions_DataCollectionState_COLLECTION_SAMPLED:
43+
return "SAMPLED"
44+
case firebase_appquality_sessions_DataCollectionState_COLLECTION_UNKNOWN:
45+
return "UNKNOWN"
46+
case firebase_appquality_sessions_DataCollectionState_COLLECTION_DISABLED:
47+
return "DISABLED"
48+
case firebase_appquality_sessions_DataCollectionState_COLLECTION_DISABLED_REMOTE:
49+
return "DISABLED_REMOTE"
50+
case firebase_appquality_sessions_DataCollectionState_COLLECTION_SDK_NOT_INSTALLED:
51+
return "SDK_NOT_INSTALLED"
52+
default:
53+
return "Unrecognized DataCollectionState. Please update the firebase_appquality_sessions_DataCollectionState CustomStringConvertible extension"
54+
}
55+
}
56+
}
57+
58+
extension firebase_appquality_sessions_OsName: CustomStringConvertible {
59+
public var description: String {
60+
switch self {
61+
case firebase_appquality_sessions_OsName_IOS:
62+
return "IOS"
63+
case firebase_appquality_sessions_OsName_IPADOS:
64+
return "IPADOS"
65+
case firebase_appquality_sessions_OsName_TVOS:
66+
return "TVOS"
67+
case firebase_appquality_sessions_OsName_IOS_ON_MAC:
68+
return "IOS_ON_MAC"
69+
case firebase_appquality_sessions_OsName_MACOS:
70+
return "MACOS"
71+
case firebase_appquality_sessions_OsName_MACCATALYST:
72+
return "MACCATALYST"
73+
case firebase_appquality_sessions_OsName_WATCHOS:
74+
return "WATCHOS"
75+
case firebase_appquality_sessions_OsName_UNKNOWN_OSNAME:
76+
return "UNKNOWN_OSNAME"
77+
case firebase_appquality_sessions_OsName_UNSPECIFIED:
78+
return "UNSPECIFIED"
79+
default:
80+
return "Unrecognized OsName. Please update the firebase_appquality_sessions_OsName CustomStringConvertible extension"
81+
}
82+
}
83+
}
84+
85+
extension UnsafeMutablePointer<pb_bytes_array_t>: CustomStringConvertible {
86+
public var description: String {
87+
let decoded = FIRSESDecodeString(self)
88+
if decoded.count == 0 {
89+
return "<EMPTY>"
90+
}
91+
return decoded
92+
}
93+
}
94+
95+
// For an optional field
96+
extension UnsafeMutablePointer<pb_bytes_array_t>?: CustomStringConvertible {
97+
public var description: String {
98+
guard let this = self else {
99+
return "<NULL>"
100+
}
101+
return this.description
102+
}
103+
}

FirebaseSessions/Sources/EventGDTLogger.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,12 @@ protocol EventGDTLoggerProtocol {
2828
///
2929
class EventGDTLogger: EventGDTLoggerProtocol {
3030
let googleDataTransport: GoogleDataTransportProtocol
31+
let devEventConsoleLogger: EventGDTLoggerProtocol
3132

32-
init(googleDataTransport: GoogleDataTransportProtocol) {
33+
init(googleDataTransport: GoogleDataTransportProtocol,
34+
devEventConsoleLogger: EventGDTLoggerProtocol = DevEventConsoleLogger()) {
3335
self.googleDataTransport = googleDataTransport
36+
self.devEventConsoleLogger = devEventConsoleLogger
3437
}
3538

3639
/// Logs the event to FireLog, taking into account debugging cases such as running
@@ -44,6 +47,8 @@ class EventGDTLogger: EventGDTLoggerProtocol {
4447
gdtEvent.qosTier = GDTCOREventQoS.qoSFast
4548
#endif // targetEnvironment(simulator)
4649

50+
devEventConsoleLogger.logEvent(event: event) { _ in }
51+
4752
googleDataTransport.logGDTEvent(event: gdtEvent, completion: completion)
4853
}
4954
}

FirebaseSessions/Sources/NanoPB/FIRSESNanoPBHelpers.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@
3030

3131
NS_ASSUME_NONNULL_BEGIN
3232

33+
/// Returns an error associated with the istream. Written in Objective-C because Swift does not
34+
/// support C language macros
35+
NSString* FIRSESPBGetError(pb_istream_t istream);
36+
3337
// It seems impossible to specify the nullability of the `fields` parameter below,
3438
// yet the compiler complains that it's missing a nullability specifier. Google
3539
// yields no results at this time.
@@ -50,6 +54,14 @@ pb_bytes_array_t* _Nullable FIRSESEncodeData(NSData* _Nullable data);
5054
/// @param string The string to encode as pb_bytes.
5155
pb_bytes_array_t* _Nullable FIRSESEncodeString(NSString* _Nullable string);
5256

57+
/// Decodes an array of nanopb bytes into an NSData object
58+
/// @param pbData nanopb data
59+
NSData* FIRSESDecodeData(pb_bytes_array_t* pbData);
60+
61+
/// Decodes an array of nanopb bytes into an NSString object
62+
/// @param pbData nanopb data
63+
NSString* FIRSESDecodeString(pb_bytes_array_t* pbData);
64+
5365
/// Checks if 2 nanopb arrays are equal
5466
/// @param array array to check
5567
/// @param expected expected value of the array

FirebaseSessions/Sources/NanoPB/FIRSESNanoPBHelpers.m

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@
3131
userInfo:@{@"NSLocalizedDescriptionKey" : description}];
3232
}
3333

34+
NSString *FIRSESPBGetError(pb_istream_t istream) {
35+
return [NSString stringWithCString:PB_GET_ERROR(&istream) encoding:NSASCIIStringEncoding];
36+
}
37+
3438
// It seems impossible to specify the nullability of the `fields` parameter below,
3539
// yet the compiler complains that it's missing a nullability specifier. Google
3640
// yields no results at this time.
@@ -93,10 +97,30 @@
9397
string = nil;
9498
}
9599
NSString *stringToEncode = string ? string : @"";
96-
NSData *stringBytes = [stringToEncode dataUsingEncoding:NSUTF8StringEncoding];
100+
// There was a bug where length 32 strings were sometimes null after encoding
101+
// and decoding. I found that this was due to the null terminator sometimes not
102+
// being included. This was fixed by using `cStringUsingEncoding` instead of
103+
// `dataUsingEncoding` because `cStringUsingEncoding` includes the null
104+
// terminator of a c string.
105+
const char *cStr = [stringToEncode cStringUsingEncoding:NSUTF8StringEncoding];
106+
// `strlen` does not include the null terminator, so we must add 1 here.
107+
NSData *stringBytes = [NSData dataWithBytes:cStr length:strlen(cStr) + 1];
97108
return FIRSESEncodeData(stringBytes);
98109
}
99110

111+
NSData *FIRSESDecodeData(pb_bytes_array_t *pbData) {
112+
NSData *data = [NSData dataWithBytes:&(pbData->bytes) length:pbData->size];
113+
return data;
114+
}
115+
116+
NSString *FIRSESDecodeString(pb_bytes_array_t *pbData) {
117+
if (pbData->size == 0) {
118+
return @"";
119+
}
120+
NSData *data = FIRSESDecodeData(pbData);
121+
return [NSString stringWithCString:[data bytes] encoding:NSUTF8StringEncoding];
122+
}
123+
100124
BOOL FIRSESIsPBArrayEqual(pb_bytes_array_t *_Nullable array, pb_bytes_array_t *_Nullable expected) {
101125
// Treat the empty string as the same as a missing field
102126
if (array == nil) {

FirebaseSessions/Sources/SessionStartEvent.swift

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,14 @@ class SessionStartEvent: NSObject, GDTCOREventDataObject {
3636
proto.session_data.previous_session_id = makeProtoStringOrNil(identifiers.previousSessionID)
3737
proto.session_data.event_timestamp_us = time.timestampUS
3838

39-
// `which_platform_info` tells nanopb which oneof we're choosing to fill in for our proto
40-
proto.application_info.which_platform_info = FIRSESGetAppleApplicationInfoTag()
4139
proto.application_info.app_id = makeProtoString(appInfo.appID)
4240
proto.application_info.session_sdk_version = makeProtoString(appInfo.sdkVersion)
4341
// proto.application_info.device_model = makeProtoString(appInfo.deviceModel)
4442
// proto.application_info.development_platform_name;
4543
// proto.application_info.development_platform_version;
4644

45+
// `which_platform_info` tells nanopb which oneof we're choosing to fill in for our proto
46+
proto.application_info.which_platform_info = FIRSESGetAppleApplicationInfoTag()
4747
proto.application_info.apple_app_info.bundle_short_version = makeProtoString(appInfo.bundleID)
4848
// proto.application_info.apple_app_info.network_connection_info
4949
proto.application_info.apple_app_info.os_name = convertOSName(osName: appInfo.osName)
@@ -104,4 +104,24 @@ class SessionStartEvent: NSObject, GDTCOREventDataObject {
104104
return firebase_appquality_sessions_OsName_UNKNOWN_OSNAME
105105
}
106106
}
107+
108+
/// Encodes the proto in this SessionStartEvent to Data, and then decodes the Data back into
109+
/// the proto object and returns the decoded proto. This is used for validating encoding works
110+
/// and should not be used in production code.
111+
func encodeDecodeEvent() -> firebase_appquality_sessions_SessionEvent {
112+
let transportBytes = self.transportBytes()
113+
var proto = firebase_appquality_sessions_SessionEvent()
114+
var fields = firebase_appquality_sessions_SessionEvent_fields
115+
116+
let bytes = (transportBytes as NSData).bytes
117+
var istream: pb_istream_t = pb_istream_from_buffer(bytes, transportBytes.count)
118+
119+
if !pb_decode(&istream, &fields.0, &proto) {
120+
let errorMessage = FIRSESPBGetError(istream)
121+
if errorMessage.count > 0 {
122+
Logger.logInfo("Failed to decode transportBytes: \(errorMessage)")
123+
}
124+
}
125+
return proto
126+
}
107127
}

FirebaseSessions/Tests/TestApp/AppQualityDevApp.xcodeproj/xcshareddata/xcschemes/AppQualityDevApp_iOS.xcscheme

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@
7272
</BuildableReference>
7373
</BuildableProductRunnable>
7474
<CommandLineArguments>
75+
<CommandLineArgument
76+
argument = "-FIRSessionsDebugEvents"
77+
isEnabled = "YES">
78+
</CommandLineArgument>
7579
<CommandLineArgument
7680
argument = "-FIRDebugEnabled"
7781
isEnabled = "YES">

0 commit comments

Comments
 (0)