Rageshake Service (#70)

This commit is contained in:
ismailgulek
2022-06-06 12:38:07 +03:00
committed by GitHub
parent 1e7243fcf9
commit fa0721b160
57 changed files with 3010 additions and 32 deletions
+179
View File
@@ -0,0 +1,179 @@
//
// 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
import SwiftyBeaver
/// Various MXLog configuration options. Used in conjunction with `MXLog.configure()`
@objc public class MXLogConfiguration: NSObject {
/// the desired log level. `.verbose` by default.
@objc public var logLevel: MXLogLevel = MXLogLevel.verbose
/// whether logs should be written directly to files. `false` by default.
@objc public var redirectLogsToFiles: Bool = false
/// the maximum total space to use for log files in bytes. `100MB` by default.
@objc public var logFilesSizeLimit: UInt = 100 * 1024 * 1024 // 100MB
/// the maximum number of log files to use before rolling. `50` by default.
@objc public var maxLogFilesCount: UInt = 50
/// the subname for log files. Files will be named as 'console-[subLogName].log'. `nil` by default
@objc public var subLogName: String?
}
/// MXLog logging levels. Use .none to disable logging entirely.
@objc public enum MXLogLevel: UInt {
case none
case verbose
case debug
case info
case warning
case error
}
private var logger: SwiftyBeaver.Type = {
let logger = SwiftyBeaver.self
MXLog.configureLogger(logger, withConfiguration: MXLogConfiguration())
return logger
}()
/**
Logging utility that provies multiple logging levels as well as file output and rolling.
Its purpose is to provide a common entry for customizing logging and should be used throughout the code.
Please see `MXLog.h` for Objective-C options.
*/
@objc public class MXLog: NSObject {
/// Method used to customize MXLog's behavior.
/// Called automatically when first accessing the logger with the default values.
/// Please see `MXLogConfiguration` for all available options.
/// - Parameters:
/// - configuration: the `MXLogConfiguration` instance to use
@objc static public func configure(_ configuration: MXLogConfiguration) {
configureLogger(logger, withConfiguration: configuration)
}
public static func verbose(_ message: @autoclosure () -> Any,
_ file: String = #file,
_ function: String = #function,
line: Int = #line,
context: Any? = nil) {
logger.verbose(message(), file, function, line: line, context: context)
}
@available(swift, obsoleted: 5.4)
@objc public static func logVerbose(_ message: String, file: String, function: String, line: Int) {
logger.verbose(message, file, function, line: line)
}
public static func debug(_ message: @autoclosure () -> Any,
_ file: String = #file,
_ function: String = #function,
line: Int = #line,
context: Any? = nil) {
logger.debug(message(), file, function, line: line, context: context)
}
@available(swift, obsoleted: 5.4)
@objc public static func logDebug(_ message: String, file: String, function: String, line: Int) {
logger.debug(message, file, function, line: line)
}
public static func info(_ message: @autoclosure () -> Any,
_ file: String = #file,
_ function: String = #function,
line: Int = #line,
context: Any? = nil) {
logger.info(message(), file, function, line: line, context: context)
}
@available(swift, obsoleted: 5.4)
@objc public static func logInfo(_ message: String, file: String, function: String, line: Int) {
logger.info(message, file, function, line: line)
}
public static func warning(_ message: @autoclosure () -> Any, _
file: String = #file,
_ function: String = #function,
line: Int = #line,
context: Any? = nil) {
logger.warning(message(), file, function, line: line, context: context)
}
@available(swift, obsoleted: 5.4)
@objc public static func logWarning(_ message: String, file: String, function: String, line: Int) {
logger.warning(message, file, function, line: line)
}
public static func error(_ message: @autoclosure () -> Any,
_ file: String = #file,
_ function: String = #function,
line: Int = #line,
context: Any? = nil) {
logger.error(message(), file, function, line: line, context: context)
}
@available(swift, obsoleted: 5.4)
@objc public static func logError(_ message: String, file: String, function: String, line: Int) {
logger.error(message, file, function, line: line)
}
// MARK: - Private
fileprivate static func configureLogger(_ logger: SwiftyBeaver.Type, withConfiguration configuration: MXLogConfiguration) {
if let subLogName = configuration.subLogName {
MXLogger.setSubLogName(subLogName)
}
MXLogger.redirectNSLog(toFiles: configuration.redirectLogsToFiles,
numberOfFiles: configuration.maxLogFilesCount,
sizeLimit: configuration.logFilesSizeLimit)
guard configuration.logLevel != .none else {
return
}
let consoleDestination = ConsoleDestination()
consoleDestination.useNSLog = true
consoleDestination.asynchronously = false
consoleDestination.format = "$M"
consoleDestination.levelColor.verbose = ""
consoleDestination.levelColor.debug = ""
consoleDestination.levelColor.info = ""
consoleDestination.levelColor.warning = "⚠️"
consoleDestination.levelColor.error = "🚨"
switch configuration.logLevel {
case .verbose:
consoleDestination.minLevel = .verbose
case .debug:
consoleDestination.minLevel = .debug
case .info:
consoleDestination.minLevel = .info
case .warning:
consoleDestination.minLevel = .warning
case .error:
consoleDestination.minLevel = .error
case .none:
break
}
logger.removeAllDestinations()
logger.addDestination(consoleDestination)
}
}
+114
View File
@@ -0,0 +1,114 @@
/*
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
+392
View File
@@ -0,0 +1,392 @@
/*
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 <UIKit/UIKit.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:@"[MXLogger] redirectNSLogToFiles: removeItemAtPath: %@\n", nsLogPathOlder];
NSError *error;
[fileManager removeItemAtPath:nsLogPathOlder error:&error];
if (error)
{
[log appendFormat:@"[MXLogger] ERROR: removeItemAtPath: %@. Error: %@\n", nsLogPathOlder, error];
}
}
// Temp log
[log appendFormat:@"[MXLogger] redirectNSLogToFiles: moveItemAtPath: %@ toPath: %@\n", nsLogPathCurrent, nsLogPathOlder];
NSError *error;
[fileManager moveItemAtPath:nsLogPathCurrent toPath:nsLogPathOlder error:&error];
if (error)
{
[log appendFormat:@"[MXLogger] ERROR: 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(@"[MXLogger] 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(@"[MXLogger] 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: %@\nMatrix SDK 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];
NSLog(@"[MXLogger] handleUncaughtException:\n%@", description);
// MXLogError(@"[MXLogger] handleUncaughtException:\n%@", description);
}
// 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] applicationGroupContainerURL];
// 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(@"[MXLogger] 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(@"[MXLogger] 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(@"[MXLogger] removeFilesAfterSizeLimit: No need: %@ for a limit of %@\n",
// [NSByteCountFormatter stringFromByteCount:logSize countStyle:NSByteCountFormatterCountStyleBinary],
// [NSByteCountFormatter stringFromByteCount:sizeLimit countStyle:NSByteCountFormatterCountStyleBinary]);
}
}
@end