K4AViewer: Add API logging pane (#229)

Add a logging pane to K4AViewer that displays the API logs.
This commit is contained in:
Billy Price 2019-04-10 12:52:06 -07:00 коммит произвёл GitHub
Родитель ee7a6fbbb9
Коммит 79cad4f660
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
7 изменённых файлов: 374 добавлений и 1 удалений

Просмотреть файл

@ -14,6 +14,7 @@ set(SOURCE_FILES
k4aimudatagraph.cpp
k4aimusamplesource.cpp
k4aimuwindow.cpp
k4alogdockcontrol.cpp
k4amicrophone.cpp
k4amicrophonelistener.cpp
k4apointcloudrenderer.cpp
@ -26,6 +27,7 @@ set(SOURCE_FILES
k4avideowindow.cpp
k4aviewer.cpp
k4aviewerimage.cpp
k4aviewerlogmanager.cpp
k4aviewererrormanager.cpp
k4aviewersettingsmanager.cpp
k4awindowmanager.cpp

Просмотреть файл

@ -0,0 +1,170 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
// Associated header
//
#include "k4alogdockcontrol.h"
// System headers
//
#include <sstream>
// Library headers
//
// Project headers
//
#include "k4aimguiextensions.h"
using namespace k4aviewer;
namespace
{
// Maximum number of log entries to keep in memory
//
constexpr static size_t MaxLines = 10000;
// clang-format off
// Get a string representation of a log level suitable for printing
// in the log box (fields are fixed-width)
//
const char *LogLevelToString(k4a_log_level_t logLevel)
{
switch(logLevel)
{
case K4A_LOG_LEVEL_CRITICAL: return "critical";
case K4A_LOG_LEVEL_ERROR: return "error ";
case K4A_LOG_LEVEL_WARNING: return "warning ";
case K4A_LOG_LEVEL_INFO: return "info ";
case K4A_LOG_LEVEL_TRACE: return "trace ";
default: return "[unknown]";
}
}
const ImVec4 &LogLevelToColor(k4a_log_level_t logLevel)
{
static const ImVec4 critical = ImVec4(1.f, 0.f, 0.f, 1.f);
static const ImVec4 error = ImVec4(1.f, .3f, 0.f, 1.f);
static const ImVec4 warning = ImVec4(1.f, 1.f, 0.f, 1.f);
static const ImVec4 info = ImVec4(1.f, 1.f, 1.f, 1.f);
static const ImVec4 trace = ImVec4(.5f, .5f, .5f, 1.f);
switch(logLevel)
{
case K4A_LOG_LEVEL_CRITICAL: return critical;
case K4A_LOG_LEVEL_ERROR: return error;
case K4A_LOG_LEVEL_WARNING: return warning;
case K4A_LOG_LEVEL_INFO: return info;
case K4A_LOG_LEVEL_TRACE: return trace;
default: return warning;
}
}
// String mappings used for the combo box used to select error levels
//
const std::vector<std::pair<k4a_log_level_t, std::string>> LogLevelLabels = {
{K4A_LOG_LEVEL_CRITICAL, "Critical"},
{K4A_LOG_LEVEL_ERROR, "Error"},
{K4A_LOG_LEVEL_WARNING, "Warning"},
{K4A_LOG_LEVEL_INFO, "Info"},
{K4A_LOG_LEVEL_TRACE, "Trace"}
};
// clang-format on
} // namespace
K4ALogDockControl::K4ALogDockControl() : m_logListener(std::make_shared<LogListener>())
{
K4AViewerLogManager::Instance().RegisterListener(m_logListener);
}
void K4ALogDockControl::LogListener::Log(k4a_log_level_t severity, const char *file, int line, const char *msg)
{
if (severity > m_minSeverity)
{
return;
}
std::lock_guard<std::mutex> lock(m_mutex);
m_entries.emplace_back(severity, file, line, msg);
if (m_entries.size() > MaxLines)
{
m_entries.pop_front();
}
m_updated = true;
}
K4ADockControlStatus K4ALogDockControl::Show()
{
ImGui::BeginGroup();
if (ImGui::Button("Clear Log"))
{
std::lock_guard<std::mutex> lock(m_logListener->m_mutex);
m_logListener->m_entries.clear();
}
ImGui::SameLine();
const bool copy = ImGui::Button("Copy Log to Clipboard");
m_logListener->m_updated |= ImGuiExtensions::K4AComboBox("Severity",
"",
ImGuiComboFlags_None,
LogLevelLabels,
&m_logListener->m_minSeverity);
m_logListener->m_updated |= ImGui::InputText("Search", &m_filterString[0], m_filterString.size());
m_logListener->m_updated |= ImGui::Checkbox("Show line info", &m_showLineInfo);
ImGui::EndGroup();
ImGui::SameLine();
ImGui::BeginChild("LogTextScrollArea", ImVec2(0, 0), false, ImGuiWindowFlags_HorizontalScrollbar);
if (copy)
{
ImGui::LogToClipboard();
}
bool updated = false;
{
std::lock_guard<std::mutex> lock(m_logListener->m_mutex);
for (const LogEntry &entry : m_logListener->m_entries)
{
std::stringstream lineBuilder;
lineBuilder << "[ " << LogLevelToString(entry.Severity) << " ] ";
if (m_showLineInfo)
{
lineBuilder << "( " << entry.File << ":" << entry.Line << " ) ";
}
lineBuilder << ": " << entry.Msg.c_str();
std::string lineStr = lineBuilder.str();
if (m_filterString[0] != '\0' && lineStr.find(&m_filterString[0]) == std::string::npos)
{
continue;
}
ImGui::TextColored(LogLevelToColor(entry.Severity), "%s", lineStr.c_str());
}
updated = m_logListener->m_updated;
m_logListener->m_updated = false;
}
if (copy)
{
ImGui::LogFinish();
}
if (updated)
{
ImGui::SetScrollHere(1.0f);
}
ImGui::EndChild();
return K4ADockControlStatus::Ok;
}

Просмотреть файл

@ -0,0 +1,74 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#ifndef K4ALOGDOCKCONTROL_H
#define K4ALOGDOCKCONTROL_H
// System headers
//
#include <array>
#include <list>
#include <mutex>
#include <string>
// Library headers
//
// Project headers
//
#include "ik4adockcontrol.h"
#include "k4aviewerlogmanager.h"
namespace k4aviewer
{
class K4ALogDockControl : public IK4ADockControl
{
public:
K4ALogDockControl();
~K4ALogDockControl() override = default;
K4ALogDockControl(K4ALogDockControl &) = delete;
K4ALogDockControl(K4ALogDockControl &&) = delete;
K4ALogDockControl operator=(K4ALogDockControl &) = delete;
K4ALogDockControl operator=(K4ALogDockControl &&) = delete;
K4ADockControlStatus Show() override;
private:
struct LogEntry
{
LogEntry() = default;
LogEntry(k4a_log_level_t severity, const char *file, int line, const char *msg) :
Severity(severity),
File(file),
Line(line),
Msg(msg)
{
}
k4a_log_level_t Severity;
std::string File;
int Line;
std::string Msg;
};
struct LogListener : public IK4AViewerLogListener
{
void Log(k4a_log_level_t severity, const char *file, int line, const char *msg) override;
~LogListener() override = default;
std::list<LogEntry> m_entries;
bool m_updated = false;
std::mutex m_mutex;
k4a_log_level_t m_minSeverity = K4A_LOG_LEVEL_WARNING;
};
std::shared_ptr<LogListener> m_logListener;
std::array<char, 100> m_filterString = { { '\0' } };
bool m_showLineInfo = false;
};
} // namespace k4aviewer
#endif

Просмотреть файл

@ -17,6 +17,7 @@
// Project headers
//
#include "k4aaudiomanager.h"
#include "k4alogdockcontrol.h"
#include "k4asourceselectiondockcontrol.h"
#include "k4aviewererrormanager.h"
#include "k4aviewerutil.h"
@ -145,6 +146,7 @@ K4AViewer::K4AViewer(const K4AViewerOptions &args)
}
K4AWindowManager::Instance().PushLeftDockControl(std14::make_unique<K4ASourceSelectionDockControl>());
K4AWindowManager::Instance().PushBottomDockControl(std14::make_unique<K4ALogDockControl>());
}
K4AViewer::~K4AViewer()

Просмотреть файл

@ -0,0 +1,74 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
// Associated header
//
#include "k4aviewerlogmanager.h"
// System headers
//
#include <utility>
#include <vector>
// Library headers
//
// Project headers
//
using namespace k4aviewer;
K4AViewerLogManager &K4AViewerLogManager::Instance()
{
static K4AViewerLogManager instance;
return instance;
}
K4AViewerLogManager::K4AViewerLogManager()
{
if (k4a_set_debug_message_handler(&LoggerCallback, this, K4A_LOG_LEVEL_TRACE) != K4A_RESULT_SUCCEEDED)
{
Log(K4A_LOG_LEVEL_ERROR, __FILE__, __LINE__, "Failed to initialize K4A logging!");
}
}
K4AViewerLogManager::~K4AViewerLogManager()
{
(void)k4a_set_debug_message_handler(nullptr, nullptr, K4A_LOG_LEVEL_TRACE);
}
void K4AViewerLogManager::Log(k4a_log_level_t severity, const char *file, int line, const char *msg)
{
std::lock_guard<std::mutex> lock(m_mutex);
for (auto wpListener = m_listeners.begin(); wpListener != m_listeners.end();)
{
std::shared_ptr<IK4AViewerLogListener> spListener = wpListener->lock();
if (spListener)
{
spListener->Log(severity, file, line, msg);
++wpListener;
}
else
{
auto toDelete = wpListener;
++wpListener;
m_listeners.erase(toDelete);
}
}
}
void K4AViewerLogManager::RegisterListener(std::shared_ptr<IK4AViewerLogListener> listener)
{
std::lock_guard<std::mutex> lock(m_mutex);
m_listeners.emplace_back(std::move(listener));
}
void K4AViewerLogManager::LoggerCallback(void *context,
k4a_log_level_t level,
const char *file,
int line,
const char *msg)
{
K4AViewerLogManager *instance = reinterpret_cast<K4AViewerLogManager *>(context);
instance->Log(level, file, line, msg);
}

Просмотреть файл

@ -0,0 +1,51 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#ifndef K4AVIEWERLOGMANAGER_H
#define K4AVIEWERLOGMANAGER_H
// System headers
//
#include <list>
#include <memory>
#include <mutex>
#include <sstream>
// Library headers
//
#include <k4a/k4a.hpp>
// Project headers
//
namespace k4aviewer
{
class IK4AViewerLogListener
{
public:
virtual void Log(k4a_log_level_t severity, const char *file, int line, const char *msg) = 0;
virtual ~IK4AViewerLogListener() = default;
};
class K4AViewerLogManager
{
public:
static K4AViewerLogManager &Instance();
void Log(k4a_log_level_t severity, const char *file, int line, const char *msg);
void RegisterListener(std::shared_ptr<IK4AViewerLogListener> listener);
private:
K4AViewerLogManager();
~K4AViewerLogManager();
static void LoggerCallback(void *context, k4a_log_level_t level, const char *file, int line, const char *msg);
std::mutex m_mutex;
std::list<std::weak_ptr<IK4AViewerLogListener>> m_listeners;
};
} // namespace k4aviewer
#endif

Просмотреть файл

@ -61,7 +61,7 @@ private:
ImVec2 m_regionSize = ImVec2(0.f, 0.f);
// The actual size/location of the dock window, in absolute window coordinates.
// Must be within by m_viewRegion*
// Must be within by m_region*
//
ImVec2 m_size = ImVec2(0.f, 0.f);