android: add logging
This commit is contained in:
parent
9848610ea2
commit
f767b5fdef
10 changed files with 160 additions and 30 deletions
|
@ -1,6 +1,9 @@
|
|||
cmake_minimum_required(VERSION 3.8)
|
||||
|
||||
add_library(citra-android SHARED
|
||||
logging/log.cpp
|
||||
logging/logcat_backend.cpp
|
||||
logging/logcat_backend.h
|
||||
native_interface.cpp
|
||||
native_interface.h
|
||||
ui/main/main_activity.cpp
|
||||
|
|
15
src/android/app/src/main/cpp/logging/log.cpp
Normal file
15
src/android/app/src/main/cpp/logging/log.cpp
Normal file
|
@ -0,0 +1,15 @@
|
|||
#include "common/logging/log.h"
|
||||
#include "native_interface.h"
|
||||
|
||||
namespace Log {
|
||||
extern "C" {
|
||||
JNICALL void Java_org_citra_1emu_citra_LOG_logEntry(JNIEnv* env, jclass type, jint level,
|
||||
jstring file_name, jint line_number,
|
||||
jstring function, jstring msg) {
|
||||
using CitraJNI::GetJString;
|
||||
FmtLogMessage(Class::Frontend, static_cast<Level>(level), GetJString(env, file_name).data(),
|
||||
static_cast<unsigned int>(line_number), GetJString(env, function).data(),
|
||||
GetJString(env, msg).data());
|
||||
}
|
||||
}
|
||||
} // namespace Log
|
38
src/android/app/src/main/cpp/logging/logcat_backend.cpp
Normal file
38
src/android/app/src/main/cpp/logging/logcat_backend.cpp
Normal file
|
@ -0,0 +1,38 @@
|
|||
// Copyright 2019 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <android/log.h>
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/text_formatter.h"
|
||||
#include "logcat_backend.h"
|
||||
|
||||
namespace Log {
|
||||
void LogcatBackend::Write(const Entry& entry) {
|
||||
android_LogPriority priority;
|
||||
switch (entry.log_level) {
|
||||
case Level::Trace:
|
||||
priority = ANDROID_LOG_VERBOSE;
|
||||
break;
|
||||
case Level::Debug:
|
||||
priority = ANDROID_LOG_DEBUG;
|
||||
break;
|
||||
case Level::Info:
|
||||
priority = ANDROID_LOG_INFO;
|
||||
break;
|
||||
case Level::Warning:
|
||||
priority = ANDROID_LOG_WARN;
|
||||
break;
|
||||
case Level::Error:
|
||||
priority = ANDROID_LOG_ERROR;
|
||||
break;
|
||||
case Level::Critical:
|
||||
priority = ANDROID_LOG_FATAL;
|
||||
break;
|
||||
case Level::Count:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
__android_log_print(priority, "citra", "%s\n", FormatLogMessage(entry).c_str());
|
||||
}
|
||||
} // namespace Log
|
22
src/android/app/src/main/cpp/logging/logcat_backend.h
Normal file
22
src/android/app/src/main/cpp/logging/logcat_backend.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
// Copyright 2019 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/logging/backend.h"
|
||||
|
||||
namespace Log {
|
||||
class LogcatBackend : public Backend {
|
||||
public:
|
||||
static const char* Name() {
|
||||
return "Logcat";
|
||||
}
|
||||
|
||||
const char* GetName() const override {
|
||||
return Name();
|
||||
}
|
||||
|
||||
void Write(const Entry& entry) override;
|
||||
};
|
||||
} // namespace Log
|
|
@ -2,14 +2,30 @@
|
|||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/common_paths.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/filter.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/settings.h"
|
||||
#include "logging/logcat_backend.h"
|
||||
#include "native_interface.h"
|
||||
|
||||
namespace MainActivity {
|
||||
extern "C" {
|
||||
JNICALL void Java_org_citra_1emu_citra_ui_main_MainActivity_initUserPath(JNIEnv* env, jclass type,
|
||||
jstring path) {
|
||||
FileUtil::SetUserPath(CitraJNI::GetJString(env, path));
|
||||
FileUtil::SetUserPath(CitraJNI::GetJString(env, path) + '/');
|
||||
}
|
||||
|
||||
JNICALL void Java_org_citra_1emu_citra_ui_main_MainActivity_initLogging(JNIEnv* env, jclass type) {
|
||||
Log::Filter log_filter(Log::Level::Debug);
|
||||
log_filter.ParseFilterString(Settings::values.log_filter);
|
||||
Log::SetGlobalFilter(log_filter);
|
||||
|
||||
const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir);
|
||||
FileUtil::CreateFullPath(log_dir);
|
||||
Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE));
|
||||
Log::AddBackend(std::make_unique<Log::LogcatBackend>());
|
||||
}
|
||||
};
|
||||
}; // namespace MainActivity
|
||||
|
|
41
src/android/app/src/main/java/org/citra_emu/citra/LOG.java
Normal file
41
src/android/app/src/main/java/org/citra_emu/citra/LOG.java
Normal file
|
@ -0,0 +1,41 @@
|
|||
package org.citra_emu.citra;
|
||||
|
||||
public class LOG {
|
||||
|
||||
private interface LOG_LEVEL {
|
||||
int TRACE = 0, DEBUG = 1, INFO = 2, WARNING = 3, ERROR = 4, CRITICAL = 5;
|
||||
}
|
||||
|
||||
public static void TRACE(String msg, Object... args) {
|
||||
LOG(LOG_LEVEL.TRACE, msg, args);
|
||||
}
|
||||
|
||||
public static void DEBUG(String msg, Object... args) {
|
||||
LOG(LOG_LEVEL.DEBUG, msg, args);
|
||||
}
|
||||
|
||||
public static void INFO(String msg, Object... args) {
|
||||
LOG(LOG_LEVEL.INFO, msg, args);
|
||||
}
|
||||
|
||||
public static void WARNING(String msg, Object... args) {
|
||||
LOG(LOG_LEVEL.WARNING, msg, args);
|
||||
}
|
||||
|
||||
public static void ERROR(String msg, Object... args) {
|
||||
LOG(LOG_LEVEL.ERROR, msg, args);
|
||||
}
|
||||
|
||||
public static void CRITICAL(String msg, Object... args) {
|
||||
LOG(LOG_LEVEL.CRITICAL, msg, args);
|
||||
}
|
||||
|
||||
private static void LOG(int level, String msg, Object... args) {
|
||||
StackTraceElement trace = Thread.currentThread().getStackTrace()[4];
|
||||
logEntry(level, trace.getFileName(), trace.getLineNumber(), trace.getMethodName(),
|
||||
String.format(msg, args));
|
||||
}
|
||||
|
||||
private static native void logEntry(int level, String file_name, int line_number,
|
||||
String function, String message);
|
||||
}
|
|
@ -18,7 +18,7 @@ import org.citra_emu.citra.utils.PermissionUtil;
|
|||
public final class MainActivity extends AppCompatActivity {
|
||||
|
||||
// Java enums suck
|
||||
private interface PermissionCodes { int INIT_USER_PATH = 0; }
|
||||
private interface PermissionCodes { int INITIALIZE = 0; }
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
|
@ -26,16 +26,17 @@ public final class MainActivity extends AppCompatActivity {
|
|||
setContentView(R.layout.activity_main);
|
||||
|
||||
PermissionUtil.verifyPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE,
|
||||
PermissionCodes.INIT_USER_PATH);
|
||||
PermissionCodes.INITIALIZE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
|
||||
@NonNull int[] grantResults) {
|
||||
switch (requestCode) {
|
||||
case PermissionCodes.INIT_USER_PATH:
|
||||
case PermissionCodes.INITIALIZE:
|
||||
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
initUserPath(FileUtil.getUserPath().toString());
|
||||
initLogging();
|
||||
} else {
|
||||
AlertDialog.Builder dialog =
|
||||
new AlertDialog.Builder(this)
|
||||
|
@ -45,7 +46,7 @@ public final class MainActivity extends AppCompatActivity {
|
|||
.setPositiveButton("OK", (dialogInterface, which) -> {
|
||||
PermissionUtil.verifyPermission(
|
||||
MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE,
|
||||
PermissionCodes.INIT_USER_PATH);
|
||||
PermissionCodes.INITIALIZE);
|
||||
});
|
||||
dialog.show();
|
||||
}
|
||||
|
@ -53,4 +54,5 @@ public final class MainActivity extends AppCompatActivity {
|
|||
}
|
||||
|
||||
private static native void initUserPath(String path);
|
||||
private static native void initLogging();
|
||||
}
|
||||
|
|
|
@ -259,7 +259,7 @@ Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsign
|
|||
entry.timestamp = duration_cast<std::chrono::microseconds>(steady_clock::now() - time_origin);
|
||||
entry.log_class = log_class;
|
||||
entry.log_level = log_level;
|
||||
entry.filename = Common::TrimSourcePath(filename);
|
||||
entry.filename = Common::TrimSourcePath(filename, {R"(\.\.)", "src"}).data();
|
||||
entry.line_num = line_nr;
|
||||
entry.function = function;
|
||||
entry.message = std::move(message);
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <codecvt>
|
||||
#include <cstdlib>
|
||||
#include <locale>
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
#include "common/common_paths.h"
|
||||
#include "common/logging/log.h"
|
||||
|
@ -210,25 +211,17 @@ std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, std::size_t
|
|||
return std::string(buffer, len);
|
||||
}
|
||||
|
||||
const char* TrimSourcePath(const char* path, const char* root) {
|
||||
const char* p = path;
|
||||
|
||||
while (*p != '\0') {
|
||||
const char* next_slash = p;
|
||||
while (*next_slash != '\0' && *next_slash != '/' && *next_slash != '\\') {
|
||||
++next_slash;
|
||||
}
|
||||
|
||||
bool is_src = Common::ComparePartialString(p, next_slash, root);
|
||||
p = next_slash;
|
||||
|
||||
if (*p != '\0') {
|
||||
++p;
|
||||
}
|
||||
if (is_src) {
|
||||
path = p;
|
||||
}
|
||||
std::string TrimSourcePath(const std::string& file_path, const std::vector<std::string>& roots) {
|
||||
// match from beginning of path to dir sep
|
||||
std::string regex_src = R"(.*([\/\\]|^)()";
|
||||
// plus the last occurrence of any root
|
||||
for (auto root = roots.begin(); root < roots.end() - 1; ++root) {
|
||||
regex_src += '(' + *root + ")|";
|
||||
}
|
||||
return path;
|
||||
regex_src += '(' + roots.back() + ')';
|
||||
// plus dir sep
|
||||
regex_src += R"()[\/\\])";
|
||||
std::regex regex(regex_src);
|
||||
return std::regex_replace(file_path, regex, "");
|
||||
}
|
||||
} // namespace Common
|
||||
|
|
|
@ -69,11 +69,11 @@ std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, std::size_t
|
|||
* intended to be used to strip a system-specific build directory from the `__FILE__` macro,
|
||||
* leaving only the path relative to the sources root.
|
||||
*
|
||||
* @param path The input file path as a null-terminated string
|
||||
* @param root The name of the root source directory as a null-terminated string. Path up to and
|
||||
* including the last occurrence of this name will be stripped
|
||||
* @return A pointer to the same string passed as `path`, but starting at the trimmed portion
|
||||
* @param path The input file path as a string
|
||||
* @param roots The name of the root source directorys as a vector of strings. Path up to and
|
||||
* including the last occurrence of these names will be stripped
|
||||
* @return The trimmed path as a string
|
||||
*/
|
||||
const char* TrimSourcePath(const char* path, const char* root = "src");
|
||||
std::string TrimSourcePath(const std::string& file_path, const std::vector<std::string>& roots);
|
||||
|
||||
} // namespace Common
|
||||
|
|
Loading…
Reference in a new issue