mirror of
https://github.com/element-hq/element-x-ios.git
synced 2025-03-10 13:37:11 +00:00
#166: Re-write MXLogger in Swift
* Add logging tests that cover file rotation. * Re-write MXLogger in Swift.
This commit is contained in:
parent
77a0d5a58f
commit
ba684d8efb
@ -74,7 +74,6 @@
|
||||
297CD0A27C87B0C50FF192EE /* RoomTimelineViewFactoryProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEE384418EB1FEDFA62C9CD0 /* RoomTimelineViewFactoryProtocol.swift */; };
|
||||
29E20505F321071E8375F99B /* BuildSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 263B3B811C2B900F12C6F695 /* BuildSettings.swift */; };
|
||||
29EE1791E0AFA1ABB7F23D2F /* SwiftyBeaver in Frameworks */ = {isa = PBXBuildFile; productRef = A981A4CA233FB5C13B9CA690 /* SwiftyBeaver */; };
|
||||
2A90D9F91A836E30B7D78838 /* MXLogObjcWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 54E438DBCBDC7A41B95DDDD9 /* MXLogObjcWrapper.m */; };
|
||||
2B9AEEC12B1BBE5BD61D0F5E /* UserSessionFlowCoordinatorStateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3429142FE11930422E7CC1A0 /* UserSessionFlowCoordinatorStateMachine.swift */; };
|
||||
2BA59D0AEFB4B82A2EC2A326 /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 78A5A8DE1E2B09C978C7F3B0 /* KeychainAccess */; };
|
||||
2BAA5B222856068158D0B3C6 /* MatrixRustSDK in Frameworks */ = {isa = PBXBuildFile; productRef = B1E8B697DF78FE7F61FC6CA4 /* MatrixRustSDK */; };
|
||||
@ -139,10 +138,10 @@
|
||||
563A05B43207D00A6B698211 /* OIDCService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9010EE0CC913D095887EF36E /* OIDCService.swift */; };
|
||||
56F0A22972A3BB519DA2261C /* HomeScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24F5530B2212862FA4BEFF2D /* HomeScreenViewModelProtocol.swift */; };
|
||||
5C8AFBF168A41E20835F3B86 /* LoginScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DB34B0C74CD242FED9DD069 /* LoginScreenUITests.swift */; };
|
||||
5D2AF8C0DF872E7985F8FE54 /* TimelineDeliveryStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5AC06FC11B6638F7BF1670E /* TimelineDeliveryStatusView.swift */; };
|
||||
5D430CDE11EAC3E8E6B80A66 /* RoomTimelineViewFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FEE631F3A4AFDC6652DD9DA /* RoomTimelineViewFactory.swift */; };
|
||||
5D7960B32C350FA93F48D02B /* OnboardingModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB33A751BFDA223BDD106EC0 /* OnboardingModels.swift */; };
|
||||
5D9F0695DC6C0057F85C12B6 /* UserNotificationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1113CA0A67B4AA227AAFB63B /* UserNotificationController.swift */; };
|
||||
5DA73F4329252C3A00651C22 /* TimelineDeliveryStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DA73F4229252C3A00651C22 /* TimelineDeliveryStatusView.swift */; };
|
||||
5E0F2E612718BB4397A6D40A /* TextRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9E785D5137510481733A3E8 /* TextRoomTimelineView.swift */; };
|
||||
5E540CAEF764D7FBD8D80776 /* VideoPlayerModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A3FC45B7643298BF361CEB1 /* VideoPlayerModels.swift */; };
|
||||
5F1FDE49DFD0C680386E48F9 /* TemplateViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B80895CE021B49847BD7D74 /* TemplateViewModelProtocol.swift */; };
|
||||
@ -288,6 +287,7 @@
|
||||
B4AAB3257A83B73F53FB2689 /* StateStoreViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F3DFE5B444F131648066F05 /* StateStoreViewModel.swift */; };
|
||||
B5111BAF5F601C139EBBD8BB /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 01C4C7DB37597D7D8379511A /* Assets.xcassets */; };
|
||||
B5903E48CF43259836BF2DBF /* EncryptedRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56C1BCB9E83B09A45387FCA2 /* EncryptedRoomTimelineView.swift */; };
|
||||
B66757D0254843162595B25D /* MXLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = A34A814CBD56230BC74FFCF4 /* MXLogger.swift */; };
|
||||
B6DA66EFC13A90846B625836 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 91DE43B8815918E590912DDA /* InfoPlist.strings */; };
|
||||
B6DF6B6FA8734B70F9BF261E /* BlurHashDecode.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5272BC4A60B6AD7553BACA1 /* BlurHashDecode.swift */; };
|
||||
B6F92EBE04D4AABF30B9E73A /* AnalyticsPromptModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA8BA82CF99D843FEF680E91 /* AnalyticsPromptModels.swift */; };
|
||||
@ -297,7 +297,6 @@
|
||||
BB01CC19C3D3322308D1B2CF /* ServerSelectionViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 167521635A1CC27624FCEB7F /* ServerSelectionViewModel.swift */; };
|
||||
BB4C6F362F75933DDDE30F3E /* InfoPlist.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A901D95158B02CA96C79C7F /* InfoPlist.swift */; };
|
||||
BB6B0B91CE11E06330017000 /* SessionVerificationScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AC1A01C3A745BDF1D3697D3 /* SessionVerificationScreen.swift */; };
|
||||
BCC3EDB7AD0902797CB4BBC2 /* MXLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = EF188681D6B6068CFAEAFC3F /* MXLogger.m */; };
|
||||
BCEC41FB1F2BB663183863E4 /* LoginServerInfoSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D379E13DD9D987470A3C70C /* LoginServerInfoSection.swift */; };
|
||||
BFD1AC03B6F8C5F5897D5B55 /* ReversedScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2DE30233B57761F8AFEB415 /* ReversedScrollView.swift */; };
|
||||
C3522917C0C367C403429EEC /* CoordinatorProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B251F5B4511D1CA0BA8361FE /* CoordinatorProtocol.swift */; };
|
||||
@ -409,7 +408,6 @@
|
||||
04BBC9E08250EF92ADE89CFD /* sr-Latn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sr-Latn"; path = "sr-Latn.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
||||
054F469E433864CC6FE6EE8E /* ServerSelectionUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerSelectionUITests.swift; sourceTree = "<group>"; };
|
||||
057B747CF045D3C6C30EAB2C /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = fi; path = fi.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||
0776771332259AB1C9661430 /* MXLog.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXLog.h; sourceTree = "<group>"; };
|
||||
077D7C3BE199B6E5DDEC07EC /* AppCoordinatorStateMachine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppCoordinatorStateMachine.swift; sourceTree = "<group>"; };
|
||||
086B997409328F091EBA43CE /* RoomScreenUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomScreenUITests.swift; sourceTree = "<group>"; };
|
||||
08F64963396A6A23538EFCEC /* is */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = is; path = is.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||
@ -515,7 +513,6 @@
|
||||
39EBB6903EFD4236B8D11A42 /* fr-CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "fr-CA"; path = "fr-CA.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
||||
3ACBDC1D28EFB7789EB467E0 /* MockRoomProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockRoomProxy.swift; sourceTree = "<group>"; };
|
||||
3B5B535DA49C54523FF7A412 /* nn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nn; path = nn.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
3BFEC43A1A0769F1EAC62873 /* MXLogObjcWrapper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXLogObjcWrapper.h; sourceTree = "<group>"; };
|
||||
3CDF9E55650D6035D6536538 /* nb-NO */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "nb-NO"; path = "nb-NO.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
||||
3D487C1185D658F8B15B8F55 /* SettingsViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewModelTests.swift; sourceTree = "<group>"; };
|
||||
3D4DD336905C72F95EAF34B7 /* ElementX-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ElementX-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
@ -571,18 +568,15 @@
|
||||
536E72DCBEEC4A1FE66CFDCE /* target.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = target.yml; sourceTree = "<group>"; };
|
||||
541542F5AC323709D8563458 /* AnalyticsPrompt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsPrompt.swift; sourceTree = "<group>"; };
|
||||
5445FCE0CE15E634FDC1A2E2 /* AnalyticsService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsService.swift; sourceTree = "<group>"; };
|
||||
54E438DBCBDC7A41B95DDDD9 /* MXLogObjcWrapper.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXLogObjcWrapper.m; sourceTree = "<group>"; };
|
||||
55BC11560C8A2598964FFA4C /* bs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bs; path = bs.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
55D7187F6B0C0A651AC3DFFA /* in */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = in; path = in.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
55EA4B03F92F31EAA83B3F7B /* FilePreviewModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilePreviewModels.swift; sourceTree = "<group>"; };
|
||||
55F30E764BED111C81739844 /* SoftLogoutUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoftLogoutUITests.swift; sourceTree = "<group>"; };
|
||||
56C1BCB9E83B09A45387FCA2 /* EncryptedRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncryptedRoomTimelineView.swift; sourceTree = "<group>"; };
|
||||
5773C86AF04AEF26515AD00C /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
5872785B9C7934940146BFBA /* MXLogger.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXLogger.h; sourceTree = "<group>"; };
|
||||
5B2F9D5C39A4494D19F33E38 /* SettingsViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||
5D26A086A8278D39B5756D6F /* project.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = project.yml; sourceTree = "<group>"; };
|
||||
5D2D0A6F1ABC99D29462FB84 /* AuthenticationCoordinatorUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationCoordinatorUITests.swift; sourceTree = "<group>"; };
|
||||
5DA73F4229252C3A00651C22 /* TimelineDeliveryStatusView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimelineDeliveryStatusView.swift; sourceTree = "<group>"; };
|
||||
5F12E996BFBEB43815189ABF /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = uk; path = uk.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||
5F4134FEFE4EB55759017408 /* UserSessionProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSessionProtocol.swift; sourceTree = "<group>"; };
|
||||
5FF214969B25BFCBF87B908B /* bn-BD */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "bn-BD"; path = "bn-BD.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
||||
@ -699,6 +693,7 @@
|
||||
A2B6433F516F1E6DFA0E2D89 /* vls */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vls; path = vls.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
A3004DFA1B10951962787D90 /* VideoPlayerViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoPlayerViewModelTests.swift; sourceTree = "<group>"; };
|
||||
A30A1758E2B73EF38E7C42F8 /* ServerSelectionModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerSelectionModels.swift; sourceTree = "<group>"; };
|
||||
A34A814CBD56230BC74FFCF4 /* MXLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXLogger.swift; sourceTree = "<group>"; };
|
||||
A40C19719687984FD9478FBE /* Task.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Task.swift; sourceTree = "<group>"; };
|
||||
A436057DBEA1A23CA8CB1FD7 /* UIFont+AttributedStringBuilder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIFont+AttributedStringBuilder.h"; sourceTree = "<group>"; };
|
||||
A443FAE2EE820A5790C35C8D /* et */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = et; path = et.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
@ -801,6 +796,7 @@
|
||||
D31DC8105C6233E5FFD9B84C /* element-x-ios */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "element-x-ios"; path = .; sourceTree = SOURCE_ROOT; };
|
||||
D33116993D54FADC0C721C1F /* Application.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Application.swift; sourceTree = "<group>"; };
|
||||
D4DA544B2520BFA65D6DB4BB /* target.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = target.yml; sourceTree = "<group>"; };
|
||||
D5AC06FC11B6638F7BF1670E /* TimelineDeliveryStatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineDeliveryStatusView.swift; sourceTree = "<group>"; };
|
||||
D653265D006E708E4E51AD64 /* HomeScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenCoordinator.swift; sourceTree = "<group>"; };
|
||||
D67CBAFA48ED0B6FCE74F88F /* lt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lt; path = lt.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
D6CA5F386C7701C129398945 /* AuthenticationCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationCoordinator.swift; sourceTree = "<group>"; };
|
||||
@ -844,7 +840,6 @@
|
||||
EEAF1C75771D9DC75877F4B4 /* MessageTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageTimelineItem.swift; sourceTree = "<group>"; };
|
||||
EEE384418EB1FEDFA62C9CD0 /* RoomTimelineViewFactoryProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineViewFactoryProtocol.swift; sourceTree = "<group>"; };
|
||||
EF1593DD87F974F8509BB619 /* ElementAnimations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElementAnimations.swift; sourceTree = "<group>"; };
|
||||
EF188681D6B6068CFAEAFC3F /* MXLogger.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXLogger.m; sourceTree = "<group>"; };
|
||||
EFF7BF82A950B91BC5469E91 /* ViewFrameReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewFrameReader.swift; sourceTree = "<group>"; };
|
||||
EFFD3200F9960D4996159F10 /* BugReportServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BugReportServiceTests.swift; sourceTree = "<group>"; };
|
||||
F012CB5EE3F2B67359F6CC52 /* target.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = target.yml; sourceTree = "<group>"; };
|
||||
@ -943,12 +938,8 @@
|
||||
06501F0E978B2D5C92771DC7 /* Logging */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0776771332259AB1C9661430 /* MXLog.h */,
|
||||
111B698739E3410E2CDB7144 /* MXLog.swift */,
|
||||
5872785B9C7934940146BFBA /* MXLogger.h */,
|
||||
EF188681D6B6068CFAEAFC3F /* MXLogger.m */,
|
||||
3BFEC43A1A0769F1EAC62873 /* MXLogObjcWrapper.h */,
|
||||
54E438DBCBDC7A41B95DDDD9 /* MXLogObjcWrapper.m */,
|
||||
A34A814CBD56230BC74FFCF4 /* MXLogger.swift */,
|
||||
);
|
||||
path = Logging;
|
||||
sourceTree = "<group>";
|
||||
@ -1832,7 +1823,7 @@
|
||||
CCD48459CA34A1928EC7A26A /* Supplementary */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5DA73F4229252C3A00651C22 /* TimelineDeliveryStatusView.swift */,
|
||||
D5AC06FC11B6638F7BF1670E /* TimelineDeliveryStatusView.swift */,
|
||||
351E89CE2ED9B73C5CC47955 /* TimelineReactionsView.swift */,
|
||||
);
|
||||
path = Supplementary;
|
||||
@ -2531,14 +2522,12 @@
|
||||
49E9B99CB6A275C7744351F0 /* LoginViewModel.swift in Sources */,
|
||||
2F30EFEB7BD39242D1AD96F3 /* LoginViewModelProtocol.swift in Sources */,
|
||||
B94368839BDB69172E28E245 /* MXLog.swift in Sources */,
|
||||
2A90D9F91A836E30B7D78838 /* MXLogObjcWrapper.m in Sources */,
|
||||
BCC3EDB7AD0902797CB4BBC2 /* MXLogger.m in Sources */,
|
||||
B66757D0254843162595B25D /* MXLogger.swift in Sources */,
|
||||
67C05C50AD734283374605E3 /* MatrixEntityRegex.swift in Sources */,
|
||||
656427D3C59554E03ECD898E /* MediaPlayerCoordinator.swift in Sources */,
|
||||
485A7A97076C7D19104BDC1D /* MediaPlayerModels.swift in Sources */,
|
||||
8D332A24CD23B4216E33EC5C /* MediaPlayerScreen.swift in Sources */,
|
||||
46F8817A235DC41228128BE7 /* MediaPlayerViewModel.swift in Sources */,
|
||||
5DA73F4329252C3A00651C22 /* TimelineDeliveryStatusView.swift in Sources */,
|
||||
80997E933A5B2C0868D80B45 /* MediaPlayerViewModelProtocol.swift in Sources */,
|
||||
EA1E7949533E19C6D862680A /* MediaProvider.swift in Sources */,
|
||||
7002C55A4C917F3715765127 /* MediaProviderProtocol.swift in Sources */,
|
||||
@ -2646,6 +2635,7 @@
|
||||
D85D4FA590305180B4A41795 /* Tests.swift in Sources */,
|
||||
7963F98CDFDEAC75E072BD81 /* TextRoomTimelineItem.swift in Sources */,
|
||||
5E0F2E612718BB4397A6D40A /* TextRoomTimelineView.swift in Sources */,
|
||||
5D2AF8C0DF872E7985F8FE54 /* TimelineDeliveryStatusView.swift in Sources */,
|
||||
157E5FDDF419C0B2CA7E2C28 /* TimelineItemBubbledStylerView.swift in Sources */,
|
||||
01CB8ACFA5E143E89C168CA8 /* TimelineItemContextMenu.swift in Sources */,
|
||||
4D970CB606276717B43E2332 /* TimelineItemList.swift in Sources */,
|
||||
|
@ -1,49 +0,0 @@
|
||||
//
|
||||
// Copyright 2021 The Matrix.org Foundation C.I.C
|
||||
//
|
||||
// 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 "MXLogObjcWrapper.h"
|
||||
|
||||
#define MXLogVerbose(message, ...) { \
|
||||
[MXLogObjcWrapper logVerbose:[NSString stringWithFormat: message, ##__VA_ARGS__] file:@__FILE__ function:[NSString stringWithFormat:@"%s", __FUNCTION__] line:__LINE__]; \
|
||||
}
|
||||
|
||||
#define MXLogDebug(message, ...) { \
|
||||
[MXLogObjcWrapper logDebug:[NSString stringWithFormat: message, ##__VA_ARGS__] file:@__FILE__ function:[NSString stringWithFormat:@"%s", __FUNCTION__] line:__LINE__]; \
|
||||
}
|
||||
|
||||
#define MXLogInfo(message, ...) { \
|
||||
[MXLogObjcWrapper logInfo:[NSString stringWithFormat: message, ##__VA_ARGS__] file:@__FILE__ function:[NSString stringWithFormat:@"%s", __FUNCTION__] line:__LINE__]; \
|
||||
}
|
||||
|
||||
#define MXLogWarning(message, ...) { \
|
||||
[MXLogObjcWrapper logWarning:[NSString stringWithFormat: message, ##__VA_ARGS__] file:@__FILE__ function:[NSString stringWithFormat:@"%s", __FUNCTION__] line:__LINE__]; \
|
||||
}
|
||||
|
||||
#define MXLogError(message) { \
|
||||
[MXLogObjcWrapper logError:message file:@__FILE__ function:[NSString stringWithFormat:@"%s", __FUNCTION__] line:__LINE__ context:nil]; \
|
||||
}
|
||||
|
||||
#define MXLogErrorDetails(message, details) { \
|
||||
[MXLogObjcWrapper logError:message file:@__FILE__ function:[NSString stringWithFormat:@"%s", __FUNCTION__] line:__LINE__ context:details]; \
|
||||
}
|
||||
|
||||
#define MXLogFailure(message) { \
|
||||
[MXLogObjcWrapper logFailure:message file:@__FILE__ function:[NSString stringWithFormat:@"%s", __FUNCTION__] line:__LINE__ context:nil]; \
|
||||
}
|
||||
|
||||
#define MXLogFailureDetails(message, details) { \
|
||||
[MXLogObjcWrapper logFailure:message file:@__FILE__ function:[NSString stringWithFormat:@"%s", __FUNCTION__] line:__LINE__ context:details]; \
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
//
|
||||
// Copyright 2021 The Matrix.org Foundation C.I.C
|
||||
//
|
||||
// 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>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
An `MXLog` Objective-C wrapper used to allow calling Swift methods from the variadic macros defined in `MXLog.h`
|
||||
*/
|
||||
@interface MXLogObjcWrapper : NSObject
|
||||
|
||||
+ (void)logVerbose:(NSString *)message file:(NSString *)file function:(NSString *)function line:(NSUInteger)line;
|
||||
|
||||
+ (void)logDebug:(NSString *)message file:(NSString *)file function:(NSString *)function line:(NSUInteger)line;
|
||||
|
||||
+ (void)logInfo:(NSString *)message file:(NSString *)file function:(NSString *)function line:(NSUInteger)line;
|
||||
|
||||
+ (void)logWarning:(NSString *)message file:(NSString *)file function:(NSString *)function line:(NSUInteger)line;
|
||||
|
||||
+ (void)logError:(NSString *)message file:(NSString *)file function:(NSString *)function line:(NSUInteger)line context:(nullable id)context;
|
||||
|
||||
+ (void)logFailure:(NSString *)message file:(NSString *)file function:(NSString *)function line:(NSUInteger)line context:(nullable id)context;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
@ -1,52 +0,0 @@
|
||||
//
|
||||
// Copyright 2021 The Matrix.org Foundation C.I.C
|
||||
//
|
||||
// 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 "MXLogObjcWrapper.h"
|
||||
#import "GeneratedInterface-Swift.h"
|
||||
|
||||
@implementation MXLogObjcWrapper
|
||||
|
||||
+ (void)logVerbose:(NSString *)message file:(NSString *)file function:(NSString *)function line:(NSUInteger)line
|
||||
{
|
||||
[MXLog logVerbose:message file:file function:function line:line];
|
||||
}
|
||||
|
||||
+ (void)logDebug:(NSString *)message file:(NSString *)file function:(NSString *)function line:(NSUInteger)line
|
||||
{
|
||||
[MXLog logDebug:message file:file function:function line:line];
|
||||
}
|
||||
|
||||
+ (void)logInfo:(NSString *)message file:(NSString *)file function:(NSString *)function line:(NSUInteger)line
|
||||
{
|
||||
[MXLog logInfo:message file:file function:function line:line];
|
||||
}
|
||||
|
||||
+ (void)logWarning:(NSString *)message file:(NSString *)file function:(NSString *)function line:(NSUInteger)line
|
||||
{
|
||||
[MXLog logWarning:message file:file function:function line:line];
|
||||
}
|
||||
|
||||
+ (void)logError:(NSString *)message file:(NSString *)file function:(NSString *)function line:(NSUInteger)line context:(id)context
|
||||
{
|
||||
[MXLog logError:message file:file function:function line:line context:context];
|
||||
}
|
||||
|
||||
+ (void)logFailure:(NSString *)message file:(NSString *)file function:(NSString *)function line:(NSUInteger)line context:(id)context
|
||||
{
|
||||
[MXLog logFailure:message file:file function:function line:line context:context];
|
||||
}
|
||||
|
||||
@end
|
@ -1,114 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
Copyright 2020 The Matrix.org Foundation C.I.C
|
||||
|
||||
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>
|
||||
|
||||
/**
|
||||
The `MXLogger` tool redirects NSLog output into a fixed pool of files.
|
||||
Another log file is used every time [MXLogger redirectNSLogToFiles:YES]
|
||||
is called. The pool contains 3 files.
|
||||
|
||||
`MXLogger` can track and log uncatched exceptions or crashes.
|
||||
*/
|
||||
@interface MXLogger : NSObject
|
||||
|
||||
/**
|
||||
Redirect NSLog output to MXLogger files.
|
||||
|
||||
It is advised to condition this redirection in '#if (!isatty(STDERR_FILENO))' block to enable
|
||||
it only when the device is not attached to the debugger.
|
||||
|
||||
@param redirectNSLogToFiles YES to enable the redirection.
|
||||
*/
|
||||
+ (void)redirectNSLogToFiles:(BOOL)redirectNSLogToFiles;
|
||||
|
||||
/**
|
||||
Redirect NSLog output to MXLogger files.
|
||||
|
||||
It is advised to condition this redirection in '#if (!isatty(STDERR_FILENO))' block to enable
|
||||
it only when the device is not attached to the debugger.
|
||||
|
||||
@param redirectNSLogToFiles YES to enable the redirection.
|
||||
@param numberOfFiles number of files to keep (default is 10).
|
||||
*/
|
||||
+ (void)redirectNSLogToFiles:(BOOL)redirectNSLogToFiles numberOfFiles:(NSUInteger)numberOfFiles;
|
||||
|
||||
/**
|
||||
Redirect NSLog output to MXLogger files.
|
||||
|
||||
@param redirectNSLogToFiles YES to enable the redirection.
|
||||
@param numberOfFiles number of files to keep (default is 10).
|
||||
@param sizeLimit size limit of log files in bytes. 0 means no limitation, the default value for other methods
|
||||
*/
|
||||
+ (void)redirectNSLogToFiles:(BOOL)redirectNSLogToFiles numberOfFiles:(NSUInteger)numberOfFiles sizeLimit:(NSUInteger)sizeLimit;
|
||||
|
||||
/**
|
||||
Delete all log files.
|
||||
*/
|
||||
+ (void)deleteLogFiles;
|
||||
|
||||
/**
|
||||
Get the list of all log files.
|
||||
|
||||
@return files of
|
||||
*/
|
||||
+ (NSArray<NSString*>*)logFiles;
|
||||
|
||||
/**
|
||||
Make `MXLogger` catch and log unmanaged exceptions or application crashes.
|
||||
|
||||
When such error happens, `MXLogger` stores the application stack trace into a file
|
||||
just before the application leaves. The path of this file is provided by [MXLogger crashLog].
|
||||
|
||||
@param logCrashes YES to enable the catch.
|
||||
*/
|
||||
+ (void)logCrashes:(BOOL)logCrashes;
|
||||
|
||||
/**
|
||||
Set the app build version.
|
||||
It will be reported in crash report.
|
||||
*/
|
||||
+ (void)setBuildVersion:(NSString*)buildVersion;
|
||||
|
||||
/**
|
||||
Set a sub name for namespacing log files.
|
||||
|
||||
A sub name must be set when running from an app extension because extensions can
|
||||
run in parallel to the app.
|
||||
It must be called before `redirectNSLogToFiles`.
|
||||
|
||||
@param subLogName the subname for log files. Files will be named as 'console-[subLogName].log'
|
||||
Default is nil.
|
||||
*/
|
||||
+ (void)setSubLogName:(NSString*)subLogName;
|
||||
|
||||
/**
|
||||
If any, get the file containing the last application crash log.
|
||||
|
||||
Only one crash log is stored at a time. The best moment for the app to handle it is the
|
||||
at its next startup.
|
||||
|
||||
@return the crash log file. nil if there is none.
|
||||
*/
|
||||
+ (NSString*)crashLog;
|
||||
|
||||
/**
|
||||
Delete the crash log file.
|
||||
*/
|
||||
+ (void)deleteCrashLog;
|
||||
|
||||
@end
|
@ -1,392 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
Copyright 2020 The Matrix.org Foundation C.I.C
|
||||
|
||||
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 "MXLogger.h"
|
||||
#import "MXLog.h"
|
||||
#import "GeneratedInterface-Swift.h"
|
||||
|
||||
// stderr so it can be restored
|
||||
int stderrSave = 0;
|
||||
|
||||
static NSString *buildVersion;
|
||||
static NSString *subLogName;
|
||||
|
||||
#define MXLOGGER_CRASH_LOG @"crash.log"
|
||||
|
||||
@implementation MXLogger
|
||||
|
||||
#pragma mark - NSLog redirection
|
||||
+ (void)redirectNSLogToFiles:(BOOL)redirectNSLogToFiles
|
||||
{
|
||||
[self redirectNSLogToFiles:redirectNSLogToFiles numberOfFiles:10];
|
||||
}
|
||||
|
||||
+ (void)redirectNSLogToFiles:(BOOL)redirectNSLogToFiles numberOfFiles:(NSUInteger)numberOfFiles
|
||||
{
|
||||
[self redirectNSLogToFiles:redirectNSLogToFiles numberOfFiles:numberOfFiles sizeLimit:0];
|
||||
}
|
||||
|
||||
+ (void)redirectNSLogToFiles:(BOOL)redirectNSLogToFiles numberOfFiles:(NSUInteger)numberOfFiles sizeLimit:(NSUInteger)sizeLimit
|
||||
{
|
||||
if (redirectNSLogToFiles)
|
||||
{
|
||||
NSMutableString *log = [NSMutableString string];
|
||||
|
||||
// Default subname
|
||||
if (!subLogName)
|
||||
{
|
||||
subLogName = @"";
|
||||
}
|
||||
|
||||
// Set log location
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSString *logsFolderPath = [MXLogger logsFolderPath];
|
||||
|
||||
// Do a circular buffer based on X files
|
||||
for (NSInteger index = numberOfFiles - 2; index >= 0; index--)
|
||||
{
|
||||
NSString *nsLogPathOlder;
|
||||
NSString *nsLogPathCurrent;
|
||||
|
||||
if (index == 0)
|
||||
{
|
||||
nsLogPathOlder = [NSString stringWithFormat:@"console%@.1.log", subLogName];
|
||||
nsLogPathCurrent = [NSString stringWithFormat:@"console%@.log", subLogName];
|
||||
}
|
||||
else
|
||||
{
|
||||
nsLogPathOlder = [NSString stringWithFormat:@"console%@.%tu.log", subLogName, index + 1];
|
||||
nsLogPathCurrent = [NSString stringWithFormat:@"console%@.%tu.log", subLogName, index];
|
||||
}
|
||||
|
||||
nsLogPathOlder = [logsFolderPath stringByAppendingPathComponent:nsLogPathOlder];
|
||||
nsLogPathCurrent = [logsFolderPath stringByAppendingPathComponent:nsLogPathCurrent];
|
||||
|
||||
if ([fileManager fileExistsAtPath:nsLogPathCurrent])
|
||||
{
|
||||
if ([fileManager fileExistsAtPath:nsLogPathOlder])
|
||||
{
|
||||
// Temp log
|
||||
[log appendFormat:@"removeItemAtPath: %@\n", nsLogPathOlder];
|
||||
|
||||
NSError *error;
|
||||
[fileManager removeItemAtPath:nsLogPathOlder error:&error];
|
||||
if (error)
|
||||
{
|
||||
[log appendFormat:@"removeItemAtPath: %@. Error: %@\n", nsLogPathOlder, error];
|
||||
}
|
||||
}
|
||||
|
||||
// Temp log
|
||||
[log appendFormat:@"moveItemAtPath: %@ toPath: %@\n", nsLogPathCurrent, nsLogPathOlder];
|
||||
|
||||
NSError *error;
|
||||
[fileManager moveItemAtPath:nsLogPathCurrent toPath:nsLogPathOlder error:&error];
|
||||
if (error)
|
||||
{
|
||||
[log appendFormat:@"moveItemAtPath: %@ toPath: %@. Error: %@\n", nsLogPathCurrent, nsLogPathOlder, error];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Save stderr so it can be restored.
|
||||
stderrSave = dup(STDERR_FILENO);
|
||||
|
||||
NSString *nsLogPath = [logsFolderPath stringByAppendingPathComponent:[NSString stringWithFormat:@"console%@.log", subLogName]];
|
||||
freopen([nsLogPath fileSystemRepresentation], "w+", stderr);
|
||||
|
||||
MXLogDebug(@"redirectNSLogToFiles: YES");
|
||||
if (log.length)
|
||||
{
|
||||
// We can now log into files
|
||||
MXLogDebug(@"%@", log);
|
||||
}
|
||||
|
||||
[self removeExtraFilesFromCount:numberOfFiles];
|
||||
|
||||
if (sizeLimit > 0)
|
||||
{
|
||||
[self removeFilesAfterSizeLimit:sizeLimit];
|
||||
}
|
||||
}
|
||||
else if (stderrSave)
|
||||
{
|
||||
// Flush before restoring stderr
|
||||
fflush(stderr);
|
||||
|
||||
// Now restore stderr, so new output goes to console.
|
||||
dup2(stderrSave, STDERR_FILENO);
|
||||
close(stderrSave);
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)deleteLogFiles
|
||||
{
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
for (NSString *logFile in [self logFiles])
|
||||
{
|
||||
[fileManager removeItemAtPath:logFile error:nil];
|
||||
}
|
||||
}
|
||||
|
||||
+ (NSArray<NSString*>*)logFiles
|
||||
{
|
||||
NSMutableArray *logFiles = [NSMutableArray array];
|
||||
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSString *logsFolderPath = [MXLogger logsFolderPath];
|
||||
|
||||
NSDirectoryEnumerator *dirEnum = [fileManager enumeratorAtPath:logsFolderPath];
|
||||
|
||||
// Find all *.log files
|
||||
NSString *file = nil;
|
||||
while ((file = [dirEnum nextObject]))
|
||||
{
|
||||
if ([[file lastPathComponent] hasPrefix:@"console"])
|
||||
{
|
||||
NSString *logPath = [logsFolderPath stringByAppendingPathComponent:file];
|
||||
[logFiles addObject:logPath];
|
||||
}
|
||||
}
|
||||
|
||||
MXLogDebug(@"logFiles: %@", logFiles);
|
||||
|
||||
return logFiles;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Exceptions and crashes
|
||||
// Exceptions uncaught by try catch block are handled here
|
||||
static void handleUncaughtException(NSException *exception)
|
||||
{
|
||||
[MXLogger logCrashes:NO];
|
||||
|
||||
// Extract running app information
|
||||
NSDictionary* infoDict = [[NSBundle mainBundle] infoDictionary];
|
||||
NSString* appVersion;
|
||||
NSString* app, *appId;
|
||||
|
||||
app = infoDict[@"CFBundleExecutable"];
|
||||
appId = infoDict[@"CFBundleIdentifier"];
|
||||
|
||||
if ([infoDict objectForKey:@"CFBundleVersion"])
|
||||
{
|
||||
appVersion = [NSString stringWithFormat:@"%@ (r%@)", [infoDict objectForKey:@"CFBundleShortVersionString"], [infoDict objectForKey:@"CFBundleVersion"]];
|
||||
}
|
||||
else
|
||||
{
|
||||
appVersion = [infoDict objectForKey:@"CFBundleShortVersionString"];
|
||||
}
|
||||
|
||||
// Build the crash log
|
||||
#if TARGET_OS_IPHONE
|
||||
NSString *model = [[UIDevice currentDevice] model];
|
||||
NSString *version = [[UIDevice currentDevice] systemVersion];
|
||||
#elif TARGET_OS_OSX
|
||||
NSString *model = @"Mac";
|
||||
NSString *version = [[NSProcessInfo processInfo] operatingSystemVersionString];
|
||||
#endif
|
||||
NSArray *backtrace = [exception callStackSymbols];
|
||||
NSString *description = [NSString stringWithFormat:@"%.0f - %@\n%@\nApplication: %@ (%@)\nApplication version: %@\nBuild: %@\n%@ %@\n\nMain thread: %@\n%@\n",
|
||||
[[NSDate date] timeIntervalSince1970],
|
||||
[NSDate date],
|
||||
exception.description,
|
||||
app, appId,
|
||||
appVersion,
|
||||
buildVersion,
|
||||
model, version,
|
||||
[NSThread isMainThread] ? @"YES" : @"NO",
|
||||
backtrace];
|
||||
|
||||
// Write to the crash log file
|
||||
[MXLogger deleteCrashLog];
|
||||
NSString *crashLog = crashLogPath();
|
||||
[description writeToFile:crashLog
|
||||
atomically:NO
|
||||
encoding:NSStringEncodingConversionAllowLossy
|
||||
error:nil];
|
||||
|
||||
MXLogErrorDetails(@"handleUncaughtException", @{
|
||||
@"description": description ?: @"unknown"
|
||||
});
|
||||
}
|
||||
|
||||
// Signals emitted by the app are handled here
|
||||
static void handleSignal(int signalValue)
|
||||
{
|
||||
// Throw a custom Objective-C exception
|
||||
// The Objective-C runtime will then be able to build a readable call stack in handleUncaughtException
|
||||
[NSException raise:@"Signal detected" format:@"Signal detected: %d", signalValue];
|
||||
}
|
||||
|
||||
+ (void)logCrashes:(BOOL)logCrashes
|
||||
{
|
||||
if (logCrashes)
|
||||
{
|
||||
// Handle not managed exceptions by ourselves
|
||||
NSSetUncaughtExceptionHandler(&handleUncaughtException);
|
||||
|
||||
// Register signal event (seg fault & cie)
|
||||
signal(SIGABRT, handleSignal);
|
||||
signal(SIGILL, handleSignal);
|
||||
signal(SIGSEGV, handleSignal);
|
||||
signal(SIGFPE, handleSignal);
|
||||
signal(SIGBUS, handleSignal);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Disable crash handling
|
||||
NSSetUncaughtExceptionHandler(NULL);
|
||||
signal(SIGABRT, SIG_DFL);
|
||||
signal(SIGILL, SIG_DFL);
|
||||
signal(SIGSEGV, SIG_DFL);
|
||||
signal(SIGFPE, SIG_DFL);
|
||||
signal(SIGBUS, SIG_DFL);
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)setBuildVersion:(NSString *)theBuildVersion
|
||||
{
|
||||
buildVersion = theBuildVersion;
|
||||
}
|
||||
|
||||
+ (void)setSubLogName:(NSString *)theSubLogName
|
||||
{
|
||||
subLogName = [NSString stringWithFormat:@"-%@", theSubLogName];
|
||||
}
|
||||
|
||||
// Return the path of the crash log file
|
||||
static NSString* crashLogPath(void)
|
||||
{
|
||||
return [[MXLogger logsFolderPath] stringByAppendingPathComponent:MXLOGGER_CRASH_LOG];
|
||||
}
|
||||
|
||||
+ (NSString*)crashLog
|
||||
{
|
||||
NSString *exceptionLog;
|
||||
|
||||
NSString *crashLog = crashLogPath();
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
if([fileManager fileExistsAtPath:crashLog])
|
||||
{
|
||||
exceptionLog = crashLog;
|
||||
}
|
||||
return exceptionLog;
|
||||
}
|
||||
|
||||
+ (void)deleteCrashLog
|
||||
{
|
||||
NSString *crashLog = crashLogPath();
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
if([fileManager fileExistsAtPath:crashLog])
|
||||
{
|
||||
[fileManager removeItemAtPath:crashLog error:nil];
|
||||
}
|
||||
}
|
||||
|
||||
// The folder where logs are stored
|
||||
+ (NSString*)logsFolderPath
|
||||
{
|
||||
NSString *logsFolderPath = nil;
|
||||
|
||||
NSURL *sharedContainerURL = [[NSFileManager defaultManager] appGroupContainerURL];
|
||||
if (sharedContainerURL)
|
||||
{
|
||||
logsFolderPath = [sharedContainerURL path];
|
||||
}
|
||||
else
|
||||
{
|
||||
NSArray<NSURL *> *paths = [[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask];
|
||||
logsFolderPath = paths[0].path;
|
||||
}
|
||||
|
||||
return logsFolderPath;
|
||||
}
|
||||
|
||||
|
||||
// If [self redirectNSLogToFiles: numberOfFiles:] is called with a lower numberOfFiles we need to do some cleanup
|
||||
+ (void)removeExtraFilesFromCount:(NSUInteger)count
|
||||
{
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSString *logsFolderPath = [MXLogger logsFolderPath];
|
||||
|
||||
NSUInteger index = count;
|
||||
do
|
||||
{
|
||||
NSString *fileName = [NSString stringWithFormat:@"console%@.%tu.log", subLogName, index];
|
||||
NSString *logFile = [logsFolderPath stringByAppendingPathComponent:fileName];
|
||||
|
||||
if ([fileManager fileExistsAtPath:logFile])
|
||||
{
|
||||
[fileManager removeItemAtPath:logFile error:nil];
|
||||
MXLogDebug(@"removeExtraFilesFromCount: %@. removeItemAtPath: %@\n", @(count), logFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (index++);
|
||||
}
|
||||
|
||||
+ (void)removeFilesAfterSizeLimit:(NSUInteger)sizeLimit
|
||||
{
|
||||
NSUInteger logSize = 0;
|
||||
BOOL removeFiles = NO;
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSString *logsFolderPath = [MXLogger logsFolderPath];
|
||||
|
||||
// Start from console.1.log. Do not consider console.log. It should be almost empty
|
||||
NSUInteger index = 0;
|
||||
while (++index)
|
||||
{
|
||||
NSString *fileName = [NSString stringWithFormat:@"console%@.%tu.log", subLogName, index];
|
||||
NSString *logFile = [logsFolderPath stringByAppendingPathComponent:fileName];
|
||||
|
||||
if ([fileManager fileExistsAtPath:logFile])
|
||||
{
|
||||
logSize += [fileManager attributesOfItemAtPath:logFile error:nil].fileSize;
|
||||
|
||||
if (logSize >= sizeLimit)
|
||||
{
|
||||
removeFiles = YES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (removeFiles)
|
||||
{
|
||||
MXLogDebug(@"removeFilesAfterSizeLimit: Remove files from index %@ because logs are too large (%@ for a limit of %@)\n",
|
||||
@(index),
|
||||
[NSByteCountFormatter stringFromByteCount:logSize countStyle:NSByteCountFormatterCountStyleBinary],
|
||||
[NSByteCountFormatter stringFromByteCount:sizeLimit countStyle:NSByteCountFormatterCountStyleBinary]);
|
||||
[self removeExtraFilesFromCount:index];
|
||||
}
|
||||
else
|
||||
{
|
||||
MXLogDebug(@"removeFilesAfterSizeLimit: No need: %@ for a limit of %@\n",
|
||||
[NSByteCountFormatter stringFromByteCount:logSize countStyle:NSByteCountFormatterCountStyleBinary],
|
||||
[NSByteCountFormatter stringFromByteCount:sizeLimit countStyle:NSByteCountFormatterCountStyleBinary]);
|
||||
}
|
||||
|
||||
}
|
||||
@end
|
327
ElementX/Sources/Other/Logging/MXLogger.swift
Normal file
327
ElementX/Sources/Other/Logging/MXLogger.swift
Normal file
@ -0,0 +1,327 @@
|
||||
//
|
||||
// Copyright 2022 New Vector Ltd
|
||||
//
|
||||
// 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 UIKit
|
||||
|
||||
/// The `MXLogger` tool redirects NSLog output into a fixed pool of files.
|
||||
/// Another log file is used every time `MXLogger redirectNSLog(toFiles: true)`
|
||||
/// is called. The pool contains 3 files.
|
||||
///
|
||||
/// `MXLogger` can track and log uncaught exceptions or crashes.
|
||||
class MXLogger {
|
||||
/// stderr so it can be restored.
|
||||
static var stderrSave: Int32 = 0
|
||||
|
||||
private enum Constants {
|
||||
/// The filename used for the crash log.
|
||||
static let crashLogFileName = "crash.log"
|
||||
}
|
||||
|
||||
/// Redirect NSLog output to MXLogger files.
|
||||
///
|
||||
/// It is advised to condition this redirection in `#if (!isatty(STDERR_FILENO))` block to enable
|
||||
/// it only when the device is not attached to the debugger.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - redirectToFiles: `true` to enable the redirection.
|
||||
/// - numberOfFiles: number of files to keep (default is 10).
|
||||
/// - sizeLimit: size limit of log files in bytes. 0 means no limitation, the default value for other methods
|
||||
static func redirectNSLog(toFiles redirectToFiles: Bool, numberOfFiles: UInt = 10, sizeLimit: UInt = 0) {
|
||||
if redirectToFiles {
|
||||
var tempLog = ""
|
||||
|
||||
// Do a circular buffer based on X files
|
||||
for index in (0...(numberOfFiles - 2)).reversed() {
|
||||
rotateLog(at: index, tempLog: &tempLog)
|
||||
}
|
||||
|
||||
// Save stderr so it can be restored.
|
||||
stderrSave = dup(STDERR_FILENO)
|
||||
|
||||
let nsLogURL = logURL(for: "console\(subLogName).log")
|
||||
freopen((nsLogURL as NSURL).fileSystemRepresentation, "w+", stderr)
|
||||
|
||||
MXLog.debug("redirectNSLogToFiles: true")
|
||||
if !tempLog.isEmpty {
|
||||
// We can now log into files
|
||||
MXLog.debug(tempLog)
|
||||
}
|
||||
|
||||
removeExtraFiles(from: numberOfFiles)
|
||||
|
||||
if sizeLimit > 0 {
|
||||
removeFiles(after: sizeLimit)
|
||||
}
|
||||
} else if stderrSave > 0 {
|
||||
// Flush before restoring stderr
|
||||
fflush(stderr)
|
||||
|
||||
// Now restore stderr, so new output goes to console.
|
||||
dup2(stderrSave, STDERR_FILENO)
|
||||
close(stderrSave)
|
||||
}
|
||||
}
|
||||
|
||||
private static func rotateLog(at index: UInt, tempLog: inout String) {
|
||||
let fileManager = FileManager.default
|
||||
|
||||
let currentURL: URL
|
||||
let newURL: URL
|
||||
|
||||
if index == 0 {
|
||||
currentURL = logURL(for: String("console\(subLogName).log"))
|
||||
newURL = logURL(for: String("console\(subLogName).1.log"))
|
||||
} else {
|
||||
currentURL = logURL(for: String("console\(subLogName).\(index).log"))
|
||||
newURL = logURL(for: String("console\(subLogName).\(index + 1).log"))
|
||||
}
|
||||
|
||||
guard fileManager.fileExists(atPath: currentURL.path()) else { return }
|
||||
|
||||
if fileManager.fileExists(atPath: newURL.path()) {
|
||||
// Temp log
|
||||
tempLog.append("removeItemAt: \(newURL)\n")
|
||||
|
||||
do {
|
||||
try fileManager.removeItem(at: newURL)
|
||||
} catch {
|
||||
tempLog.append("removeItemAt: \(newURL). Error: \(error)\n")
|
||||
}
|
||||
}
|
||||
|
||||
// Temp log
|
||||
tempLog.append("moveItemAt: \(currentURL) to: \(newURL)\n")
|
||||
|
||||
do {
|
||||
try fileManager.moveItem(at: currentURL, to: newURL)
|
||||
} catch {
|
||||
tempLog.append("moveItemAt: \(currentURL) to: \(newURL). Error: \(error)\n")
|
||||
}
|
||||
}
|
||||
|
||||
private static func logURL(for fileName: String) -> URL {
|
||||
MXLogger.logsFolderURL.appending(path: fileName)
|
||||
}
|
||||
|
||||
/// Delete all log files.
|
||||
static func deleteLogFiles() {
|
||||
let fileManager = FileManager.default
|
||||
for logFileURL in logFiles {
|
||||
try? fileManager.removeItem(at: logFileURL)
|
||||
}
|
||||
}
|
||||
|
||||
/// The list of all log file URLs.
|
||||
static var logFiles: [URL] {
|
||||
var logFiles = [URL]()
|
||||
|
||||
let fileManager = FileManager.default
|
||||
let enumerator = fileManager.enumerator(at: logsFolderURL, includingPropertiesForKeys: nil)
|
||||
|
||||
// Find all *.log files
|
||||
while let logURL = enumerator?.nextObject() as? URL {
|
||||
if logURL.lastPathComponent.hasPrefix("console") {
|
||||
logFiles.append(logURL)
|
||||
}
|
||||
}
|
||||
|
||||
MXLog.debug("logFiles: \(logFiles)")
|
||||
|
||||
return logFiles
|
||||
}
|
||||
|
||||
// MARK: - Exceptions and crashes
|
||||
|
||||
/// Exceptions uncaught by try catch block are handled here
|
||||
static func handleUncaughtException(_ exception: NSException) {
|
||||
MXLogger.logCrashes(false)
|
||||
|
||||
// Extract running app information
|
||||
let app = ElementInfoPlist.cfBundleExecutable
|
||||
let appId = ElementInfoPlist.cfBundleIdentifier
|
||||
let appVersion = "\(ElementInfoPlist.cfBundleShortVersionString) (r\(ElementInfoPlist.cfBundleVersion))"
|
||||
|
||||
// Build the crash log
|
||||
let model = UIDevice.current.model
|
||||
let version = UIDevice.current.systemVersion
|
||||
|
||||
let backtrace = exception.callStackSymbols
|
||||
let description = String(format: "%.0f - %@\n%@\nApplication: %@ (%@)\nApplication version: %@\nBuild: %@\n%@ %@\n\nMain thread: %@\n%@\n",
|
||||
Date.now.timeIntervalSince1970,
|
||||
NSDate(),
|
||||
exception.description,
|
||||
app, appId,
|
||||
appVersion,
|
||||
buildVersion ?? "Unknown",
|
||||
model, version,
|
||||
Thread.isMainThread ? "true" : "false",
|
||||
backtrace)
|
||||
|
||||
// Write to the crash log file
|
||||
MXLogger.deleteCrashLog()
|
||||
let crashLog = crashLogURL
|
||||
try? description.write(to: crashLog, atomically: false, encoding: .utf8)
|
||||
|
||||
MXLog.error("handleUncaughtException", context: ["description": description])
|
||||
}
|
||||
|
||||
// Signals emitted by the app are handled here
|
||||
private static func handleSignal(_ signalValue: Int32) {
|
||||
// Throw a custom Objective-C exception
|
||||
// The Objective-C runtime will then be able to build a readable call stack in handleUncaughtException
|
||||
withVaList([signalValue]) { NSException.raise(.init("Signal detected"), format: "Signal detected: %d", arguments: $0) }
|
||||
}
|
||||
|
||||
/// Make `MXLogger` catch and log unmanaged exceptions or application crashes.
|
||||
///
|
||||
/// When such error happens, `MXLogger` stores the application stack trace into a file
|
||||
/// just before the application leaves. The path of this file is provided by `MXLogger.crashLog`.
|
||||
///
|
||||
/// - Parameter enabled: `true` to enable the catch.
|
||||
static func logCrashes(_ enabled: Bool) {
|
||||
if enabled {
|
||||
// Handle not managed exceptions by ourselves
|
||||
NSSetUncaughtExceptionHandler { exception in
|
||||
MXLogger.handleUncaughtException(exception)
|
||||
}
|
||||
|
||||
// Register signal event (seg fault & cie)
|
||||
signal(SIGABRT) { MXLogger.handleSignal($0) }
|
||||
signal(SIGILL) { MXLogger.handleSignal($0) }
|
||||
signal(SIGSEGV) { MXLogger.handleSignal($0) }
|
||||
signal(SIGFPE) { MXLogger.handleSignal($0) }
|
||||
signal(SIGBUS) { MXLogger.handleSignal($0) }
|
||||
signal(SIGABRT) { MXLogger.handleSignal($0) }
|
||||
} else {
|
||||
// Disable crash handling
|
||||
NSSetUncaughtExceptionHandler(nil)
|
||||
signal(SIGABRT, SIG_DFL)
|
||||
signal(SIGILL, SIG_DFL)
|
||||
signal(SIGSEGV, SIG_DFL)
|
||||
signal(SIGFPE, SIG_DFL)
|
||||
signal(SIGBUS, SIG_DFL)
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the app build version.
|
||||
/// It will be reported in crash report.
|
||||
static var buildVersion: String?
|
||||
|
||||
/// Set a sub name for namespacing log files.
|
||||
///
|
||||
/// A sub name must be set when running from an app extension because extensions can
|
||||
/// run in parallel to the app.
|
||||
/// It must be called before `redirectNSLog(toFiles)`.
|
||||
///
|
||||
/// - Parameter name: the subname for log files. Files will be named as `console-[subLogName].log`
|
||||
/// Default is nil.
|
||||
static func setSubLogName(_ name: String) {
|
||||
if name.isEmpty {
|
||||
subLogName = ""
|
||||
} else {
|
||||
subLogName = "-\(name)"
|
||||
}
|
||||
}
|
||||
|
||||
private static var subLogName = ""
|
||||
|
||||
/// The URL used for a crash log file.
|
||||
static var crashLogURL: URL {
|
||||
MXLogger.logsFolderURL.appending(path: Constants.crashLogFileName)
|
||||
}
|
||||
|
||||
/// The URL of the file containing the last application crash if one exists or `nil` if there is none.
|
||||
///
|
||||
/// Only one crash log is stored at a time. The best moment for the app to handle it is the
|
||||
/// at its next startup.
|
||||
static var crashLog: URL? {
|
||||
let crashLogURL = MXLogger.crashLogURL
|
||||
let fileManager = FileManager.default
|
||||
guard fileManager.fileExists(atPath: crashLogURL.path()) else { return nil }
|
||||
|
||||
return crashLogURL
|
||||
}
|
||||
|
||||
/// Delete the crash log file.
|
||||
static func deleteCrashLog() {
|
||||
let crashLog = MXLogger.crashLogURL
|
||||
let fileManager = FileManager.default
|
||||
|
||||
if fileManager.fileExists(atPath: crashLog.path()) {
|
||||
try? fileManager.removeItem(at: crashLog)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
/// The folder where logs are stored
|
||||
private static var logsFolderURL: URL {
|
||||
FileManager.default.appGroupContainerURL ?? URL.documentsDirectory
|
||||
}
|
||||
|
||||
/// If `self.redirectNSLog(toFiles:numberOfFiles:)` is called with a lower numberOfFiles we need to do some cleanup.
|
||||
private static func removeExtraFiles(from count: UInt) {
|
||||
let fileManager = FileManager.default
|
||||
|
||||
for index in count... {
|
||||
let fileName = "console\(subLogName).\(index).log"
|
||||
let logFile = logURL(for: fileName)
|
||||
|
||||
if fileManager.fileExists(atPath: logFile.path()) {
|
||||
try? fileManager.removeItem(at: logFile)
|
||||
MXLog.debug("removeExtraFilesFromCount: \(count). removeItemAt: \(logFile)\n")
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// If `redirectNSLog(toFiles:sizeLimit:)` is called with a size limit, we may need to do some cleanup.
|
||||
private static func removeFiles(after sizeLimit: UInt) {
|
||||
var logSize: UInt = 0
|
||||
var indexExceedingSizeLimit: Int?
|
||||
let fileManager = FileManager.default
|
||||
|
||||
// Start from console.1.log. Do not consider console.log. It should be almost empty
|
||||
for index in 1... {
|
||||
let fileName = "console\(subLogName).\(index).log"
|
||||
let logFile = logURL(for: fileName)
|
||||
|
||||
if fileManager.fileExists(atPath: logFile.path()) {
|
||||
if let attributes = try? fileManager.attributesOfItem(atPath: logFile.path()), let fileSize = attributes[.size] as? UInt {
|
||||
logSize += fileSize
|
||||
}
|
||||
|
||||
if logSize >= sizeLimit {
|
||||
indexExceedingSizeLimit = index
|
||||
break
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
let logSizeString = logSize.formatted(.byteCount(style: .binary))
|
||||
let sizeLimitString = sizeLimit.formatted(.byteCount(style: .binary))
|
||||
|
||||
if let indexExceedingSizeLimit {
|
||||
MXLog.debug("removeFilesAfterSizeLimit: Remove files from index \(indexExceedingSizeLimit) because logs are too large (\(logSizeString) for a limit of \(sizeLimitString)\n")
|
||||
removeExtraFiles(from: UInt(indexExceedingSizeLimit))
|
||||
} else {
|
||||
MXLog.debug("removeFilesAfterSizeLimit: No need: \(logSizeString) for a limit of \(sizeLimitString)\n")
|
||||
}
|
||||
}
|
||||
}
|
@ -61,7 +61,7 @@ class BugReportService: BugReportServiceProtocol {
|
||||
// also enable logging crashes, to send them with bug reports
|
||||
MXLogger.logCrashes(true)
|
||||
// set build version for logger
|
||||
MXLogger.setBuildVersion(ElementInfoPlist.cfBundleShortVersionString)
|
||||
MXLogger.buildVersion = ElementInfoPlist.cfBundleShortVersionString
|
||||
}
|
||||
|
||||
// MARK: - BugReportServiceProtocol
|
||||
@ -180,12 +180,11 @@ class BugReportService: BugReportServiceProtocol {
|
||||
MXLog.debug("zipFiles: includeLogs: \(includeLogs), includeCrashLog: \(includeCrashLog)")
|
||||
|
||||
var filesToCompress: [URL] = []
|
||||
if includeLogs, let logFiles = MXLogger.logFiles() {
|
||||
let urls = logFiles.compactMap { URL(fileURLWithPath: $0) }
|
||||
filesToCompress.append(contentsOf: urls)
|
||||
if includeLogs {
|
||||
filesToCompress.append(contentsOf: MXLogger.logFiles)
|
||||
}
|
||||
if includeCrashLog, let crashLogFile = MXLogger.crashLog() {
|
||||
filesToCompress.append(URL(fileURLWithPath: crashLogFile))
|
||||
if includeCrashLog, let crashLogFile = MXLogger.crashLog {
|
||||
filesToCompress.append(crashLogFile)
|
||||
}
|
||||
|
||||
var totalSize = 0
|
||||
|
@ -15,6 +15,7 @@
|
||||
//
|
||||
|
||||
import Combine
|
||||
import Foundation
|
||||
import MatrixRustSDK
|
||||
|
||||
struct MockClientProxy: ClientProxyProtocol {
|
||||
|
@ -15,6 +15,7 @@
|
||||
//
|
||||
|
||||
import Combine
|
||||
import Foundation
|
||||
import MatrixRustSDK
|
||||
|
||||
struct MockRoomProxy: RoomProxyProtocol {
|
||||
|
@ -15,6 +15,7 @@
|
||||
//
|
||||
|
||||
import Combine
|
||||
import Foundation
|
||||
import MatrixRustSDK
|
||||
|
||||
private class RoomTimelineListener: TimelineListener {
|
||||
|
@ -14,6 +14,7 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import MatrixRustSDK
|
||||
|
||||
/// A light wrapper around timeline items returned from Rust.
|
||||
|
@ -1,4 +1,3 @@
|
||||
//
|
||||
// Use this file to import your target's public headers that you would like to expose to Swift.
|
||||
//
|
||||
#import "MXLogger.h"
|
||||
|
@ -23,7 +23,7 @@ class LoggingTests: XCTestCase {
|
||||
}
|
||||
|
||||
override func setUpWithError() throws {
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
MXLogger.deleteLogFiles()
|
||||
}
|
||||
|
||||
override func tearDownWithError() throws {
|
||||
@ -31,12 +31,7 @@ class LoggingTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testFileLogging() throws {
|
||||
MXLogger.deleteLogFiles()
|
||||
guard let logFiles = MXLogger.logFiles() else {
|
||||
XCTFail(Constants.genericFailure)
|
||||
return
|
||||
}
|
||||
XCTAssertTrue(logFiles.isEmpty)
|
||||
XCTAssertTrue(MXLogger.logFiles.isEmpty)
|
||||
|
||||
let log = UUID().uuidString
|
||||
|
||||
@ -44,22 +39,105 @@ class LoggingTests: XCTestCase {
|
||||
configuration.redirectLogsToFiles = true
|
||||
MXLog.configure(configuration)
|
||||
MXLog.debug(log)
|
||||
guard let logFile = MXLogger.logFiles().first else {
|
||||
guard let logFile = MXLogger.logFiles.first else {
|
||||
XCTFail(Constants.genericFailure)
|
||||
return
|
||||
}
|
||||
|
||||
let content = try String(contentsOfFile: logFile)
|
||||
let content = try String(contentsOf: logFile)
|
||||
XCTAssert(content.contains(log))
|
||||
}
|
||||
|
||||
func testFileRotationOnLaunch() throws {
|
||||
// Given a fresh launch with no logs.
|
||||
XCTAssertTrue(MXLogger.logFiles.isEmpty)
|
||||
|
||||
// When launching the app 5 times.
|
||||
let launchCount = 5
|
||||
for index in 0..<launchCount {
|
||||
let configuration = MXLogConfiguration()
|
||||
configuration.redirectLogsToFiles = true
|
||||
MXLog.configure(configuration) // This call is only made at app launch.
|
||||
MXLog.debug("Launch \(index + 1)")
|
||||
}
|
||||
|
||||
// Then 5 log files should be created each with the correct contents.
|
||||
let logFiles = MXLogger.logFiles
|
||||
|
||||
XCTAssertEqual(logFiles.count, launchCount, "The number of log files should match the number of launches.")
|
||||
try verifyContents(of: logFiles, after: launchCount)
|
||||
}
|
||||
|
||||
func testMaxLogFileCount() throws {
|
||||
// Given a fresh launch with no logs.
|
||||
XCTAssertTrue(MXLogger.logFiles.isEmpty)
|
||||
|
||||
// When launching the app 10 times, with a maxLogCount of 5.
|
||||
let launchCount = 10
|
||||
let logFileCount = 5
|
||||
for index in 0..<launchCount {
|
||||
let configuration = MXLogConfiguration()
|
||||
configuration.maxLogFilesCount = UInt(logFileCount)
|
||||
configuration.redirectLogsToFiles = true
|
||||
MXLog.configure(configuration) // This call is only made at app launch.
|
||||
MXLog.debug("Launch \(index + 1)")
|
||||
}
|
||||
|
||||
// Then only 5 log files should be stored on disk, with the contents of launches 6 to 10.
|
||||
let logFiles = MXLogger.logFiles
|
||||
|
||||
XCTAssertEqual(logFiles.count, logFileCount, "The number of log files should match the number of launches.")
|
||||
try verifyContents(of: logFiles, after: launchCount)
|
||||
}
|
||||
|
||||
func testLogFileSizeLimit() throws {
|
||||
// Given a fresh launch with no logs.
|
||||
XCTAssertTrue(MXLogger.logFiles.isEmpty)
|
||||
|
||||
// When launching the app 10 times, with a max total log size of 25KB and logging ~5KB data each time.
|
||||
let launchCount = 10
|
||||
let logFileSizeLimit = 25 * 1024
|
||||
for index in 0..<launchCount {
|
||||
let configuration = MXLogConfiguration()
|
||||
configuration.logFilesSizeLimit = UInt(logFileSizeLimit)
|
||||
configuration.redirectLogsToFiles = true
|
||||
MXLog.configure(configuration) // This call is only made at app launch.
|
||||
MXLog.debug("Launch \(index + 1)")
|
||||
|
||||
// Add ~5KB of logs
|
||||
for _ in 0..<5 {
|
||||
let string = [String](repeating: "a", count: 1024).joined()
|
||||
MXLog.debug(string)
|
||||
}
|
||||
}
|
||||
|
||||
// Then only the most recent log files should be stored on disk.
|
||||
let logFiles = MXLogger.logFiles
|
||||
|
||||
XCTAssertGreaterThan(logFiles.count, 0, "There should be at least one log file created.")
|
||||
XCTAssertLessThan(logFiles.count, launchCount, "Some of the log files should have been removed trimmed.")
|
||||
try verifyContents(of: logFiles, after: launchCount)
|
||||
}
|
||||
|
||||
/// Verifies that the log files all contain the correct `Launch #` based on the index
|
||||
/// in the file name and the number of launches of the app.
|
||||
func verifyContents(of logFiles: [URL], after launchCount: Int) throws {
|
||||
for logFile in logFiles {
|
||||
let regex = /\d+/
|
||||
let fileIndex = logFile.lastPathComponent.firstMatch(of: regex)?.0 ?? "0"
|
||||
|
||||
guard let index = Int(fileIndex) else {
|
||||
XCTFail(Constants.genericFailure)
|
||||
return
|
||||
}
|
||||
|
||||
let content = try String(contentsOf: logFile)
|
||||
XCTAssertTrue(content.contains("Launch \(launchCount - index)"), "The log files should be for the most recent launches in reverse chronological order.")
|
||||
}
|
||||
}
|
||||
|
||||
func testLogLevels() throws {
|
||||
MXLogger.deleteLogFiles()
|
||||
guard let logFiles = MXLogger.logFiles() else {
|
||||
XCTFail(Constants.genericFailure)
|
||||
return
|
||||
}
|
||||
XCTAssert(logFiles.isEmpty)
|
||||
XCTAssert(MXLogger.logFiles.isEmpty)
|
||||
|
||||
let log = UUID().uuidString
|
||||
|
||||
@ -68,22 +146,17 @@ class LoggingTests: XCTestCase {
|
||||
configuration.redirectLogsToFiles = true
|
||||
MXLog.configure(configuration)
|
||||
MXLog.debug(log)
|
||||
guard let logFile = MXLogger.logFiles().first else {
|
||||
guard let logFile = MXLogger.logFiles.first else {
|
||||
XCTFail(Constants.genericFailure)
|
||||
return
|
||||
}
|
||||
|
||||
let content = try String(contentsOfFile: logFile)
|
||||
let content = try String(contentsOf: logFile)
|
||||
XCTAssertFalse(content.contains(log))
|
||||
}
|
||||
|
||||
func testSubLogName() {
|
||||
MXLogger.deleteLogFiles()
|
||||
guard let logFiles = MXLogger.logFiles() else {
|
||||
XCTFail(Constants.genericFailure)
|
||||
return
|
||||
}
|
||||
XCTAssert(logFiles.isEmpty)
|
||||
XCTAssert(MXLogger.logFiles.isEmpty)
|
||||
|
||||
let subLogName = "nse"
|
||||
|
||||
@ -92,11 +165,11 @@ class LoggingTests: XCTestCase {
|
||||
configuration.redirectLogsToFiles = true
|
||||
MXLog.configure(configuration)
|
||||
MXLog.debug(UUID().uuidString)
|
||||
guard let logFile = MXLogger.logFiles().first else {
|
||||
guard let logFile = MXLogger.logFiles.first else {
|
||||
XCTFail(Constants.genericFailure)
|
||||
return
|
||||
}
|
||||
|
||||
XCTAssertTrue(logFile.contains(subLogName))
|
||||
XCTAssertTrue(logFile.lastPathComponent.contains(subLogName))
|
||||
}
|
||||
}
|
||||
|
1
changelog.d/166.change
Normal file
1
changelog.d/166.change
Normal file
@ -0,0 +1 @@
|
||||
Re-write MXLogger in Swift.
|
Loading…
x
Reference in New Issue
Block a user