K4AViewer: Add API logging pane (#229)
Add a logging pane to K4AViewer that displays the API logs.
This commit is contained in:
Родитель
ee7a6fbbb9
Коммит
79cad4f660
|
@ -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);
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче