android: add logging

This commit is contained in:
BreadFish64 2019-02-15 20:37:36 -06:00
parent 9848610ea2
commit f767b5fdef
10 changed files with 160 additions and 30 deletions

View file

@ -1,6 +1,9 @@
cmake_minimum_required(VERSION 3.8) cmake_minimum_required(VERSION 3.8)
add_library(citra-android SHARED add_library(citra-android SHARED
logging/log.cpp
logging/logcat_backend.cpp
logging/logcat_backend.h
native_interface.cpp native_interface.cpp
native_interface.h native_interface.h
ui/main/main_activity.cpp ui/main/main_activity.cpp

View 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

View 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

View 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

View file

@ -2,14 +2,30 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include "common/common_paths.h"
#include "common/file_util.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" #include "native_interface.h"
namespace MainActivity { namespace MainActivity {
extern "C" { extern "C" {
JNICALL void Java_org_citra_1emu_citra_ui_main_MainActivity_initUserPath(JNIEnv* env, jclass type, JNICALL void Java_org_citra_1emu_citra_ui_main_MainActivity_initUserPath(JNIEnv* env, jclass type,
jstring path) { 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 }; // namespace MainActivity

View 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);
}

View file

@ -18,7 +18,7 @@ import org.citra_emu.citra.utils.PermissionUtil;
public final class MainActivity extends AppCompatActivity { public final class MainActivity extends AppCompatActivity {
// Java enums suck // Java enums suck
private interface PermissionCodes { int INIT_USER_PATH = 0; } private interface PermissionCodes { int INITIALIZE = 0; }
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
@ -26,16 +26,17 @@ public final class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main); setContentView(R.layout.activity_main);
PermissionUtil.verifyPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE, PermissionUtil.verifyPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE,
PermissionCodes.INIT_USER_PATH); PermissionCodes.INITIALIZE);
} }
@Override @Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) { @NonNull int[] grantResults) {
switch (requestCode) { switch (requestCode) {
case PermissionCodes.INIT_USER_PATH: case PermissionCodes.INITIALIZE:
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
initUserPath(FileUtil.getUserPath().toString()); initUserPath(FileUtil.getUserPath().toString());
initLogging();
} else { } else {
AlertDialog.Builder dialog = AlertDialog.Builder dialog =
new AlertDialog.Builder(this) new AlertDialog.Builder(this)
@ -45,7 +46,7 @@ public final class MainActivity extends AppCompatActivity {
.setPositiveButton("OK", (dialogInterface, which) -> { .setPositiveButton("OK", (dialogInterface, which) -> {
PermissionUtil.verifyPermission( PermissionUtil.verifyPermission(
MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE, MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE,
PermissionCodes.INIT_USER_PATH); PermissionCodes.INITIALIZE);
}); });
dialog.show(); dialog.show();
} }
@ -53,4 +54,5 @@ public final class MainActivity extends AppCompatActivity {
} }
private static native void initUserPath(String path); private static native void initUserPath(String path);
private static native void initLogging();
} }

View file

@ -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.timestamp = duration_cast<std::chrono::microseconds>(steady_clock::now() - time_origin);
entry.log_class = log_class; entry.log_class = log_class;
entry.log_level = log_level; entry.log_level = log_level;
entry.filename = Common::TrimSourcePath(filename); entry.filename = Common::TrimSourcePath(filename, {R"(\.\.)", "src"}).data();
entry.line_num = line_nr; entry.line_num = line_nr;
entry.function = function; entry.function = function;
entry.message = std::move(message); entry.message = std::move(message);

View file

@ -7,6 +7,7 @@
#include <codecvt> #include <codecvt>
#include <cstdlib> #include <cstdlib>
#include <locale> #include <locale>
#include <regex>
#include <sstream> #include <sstream>
#include "common/common_paths.h" #include "common/common_paths.h"
#include "common/logging/log.h" #include "common/logging/log.h"
@ -210,25 +211,17 @@ std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, std::size_t
return std::string(buffer, len); return std::string(buffer, len);
} }
const char* TrimSourcePath(const char* path, const char* root) { std::string TrimSourcePath(const std::string& file_path, const std::vector<std::string>& roots) {
const char* p = path; // match from beginning of path to dir sep
std::string regex_src = R"(.*([\/\\]|^)()";
while (*p != '\0') { // plus the last occurrence of any root
const char* next_slash = p; for (auto root = roots.begin(); root < roots.end() - 1; ++root) {
while (*next_slash != '\0' && *next_slash != '/' && *next_slash != '\\') { regex_src += '(' + *root + ")|";
++next_slash;
}
bool is_src = Common::ComparePartialString(p, next_slash, root);
p = next_slash;
if (*p != '\0') {
++p;
}
if (is_src) {
path = p;
}
} }
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 } // namespace Common

View file

@ -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, * intended to be used to strip a system-specific build directory from the `__FILE__` macro,
* leaving only the path relative to the sources root. * leaving only the path relative to the sources root.
* *
* @param path The input file path as a null-terminated string * @param path The input file path as a string
* @param root The name of the root source directory as a null-terminated string. Path up to and * @param roots The name of the root source directorys as a vector of strings. Path up to and
* including the last occurrence of this name will be stripped * including the last occurrence of these names will be stripped
* @return A pointer to the same string passed as `path`, but starting at the trimmed portion * @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 } // namespace Common