Installation
The library is designed to be statically compiled into your project.
Simply copy the files to your project and add them to your compilation
sources. After that, add an include directive for UVKLog.h
in one of your files and you're done
Additional Features
Compiling for a library
Compiling the logger as part of a larger library and want to export the symbols? To do that you need to:
- Define the
UVK_LOG_EXPORT_FROM_LIBRARY
- Make sure the
UVK_LIB_COMPILE
macro is defined when compiling the larger library
When compiling with UVK_LIB_COMPILE
, you export your
symbols for Windows using __declspec(dllexport)
, and when
it's disabled, it uses __declspec(dllimport)
.
No instant error termination
Handling errors will be explained below. Most of the time, an error
will crash the application immediately, except if the
NO_INSTANT_CRASH
macro is defined. It does the
following:
- If defined: inserts a call to
std::cin.get()
, before callingstd::terminate
- If not defined: does nothing
This is useful in debug cases where you want to check the last frame of your running program before completely crashing.
Enabling the dear imgui console widget
To enable the dear imgui console widget do the following:
- Include
UVKLogImGui.h
- Have
imgui.h
in your include path - Define the
UVK_LOG_IMGUI
macro
This widget provides a simple console interface where every log is recorded to a buffer, which you can view in dear imgui. Additionally, you can also add your own commands. Using the widget is explained below.
Standard Logging
There is a single class you can use for all your logging needs, this
class is the Logger
class, part of the UVKLog
namespace in the UVKLog.h
header. The Logger
class contains the following static functions that you can use for
logging:
void setCrashOnError(bool bError)
- If set to true, every time you call the log function with an error as the type it will terminate the applicationvoid setCurrentLogFile(const char* file)
- Changes the current file we're logging to and or creates a new filevoid setLogOperation(LogOperations op)
- Sets the current log operationvoid log(const char* message, LogType type, args&&... argv)
- Logs a message. Given a message, a log type and an optional list of templated variadic arguments, will log a message using the current log operation
Log Operations
Log operations are set using the
void setLogOperation(LogOperations op)
static member
function of the Logger
class. To change the log operation,
pass this function an argument of type LogOperations
. This
type is the following enum:
enum LogOperations
{
// This is the default operation
,
UVK_LOG_OPERATION_TERMINAL,
UVK_LOG_OPERATION_FILE,
UVK_LOG_OPERATION_FILE_AND_TERMINAL};
as you can see, we have 3 log operations:
TERMINAL
- default operationFILE
FILE_AND_TERMINAL
.
Note
Using the setCurrentLogFile
function while in the
TERMINAL
operation will not change the operation to
FILE
or FILE_AND_TERMINAL
. You're responsible
for changing your operation type!
Log Types
There are currently 6 types of log messages that you can pass to the
log function. They're stored in the LogType
enum, which
looks like this:
enum LogType
{
= 1,
UVK_LOG_TYPE_WARNING = 2,
UVK_LOG_TYPE_ERROR = 4,
UVK_LOG_TYPE_NOTE = 0,
UVK_LOG_TYPE_SUCCESS = 3
UVK_LOG_TYPE_MESSAGE };
The types other than ERROR
don't have any functionality,
other than being displayed with a different title and colour in the
terminal/GUI console.
By default, the ERROR
type also works like the others,
but you can enable termination on errors, which will terminate your
process when an error is logged.
The different log types are represented with different colours in the terminal/GUI console. Here is a cheat sheet:
- SUCCESS - Green
- NOTE - Blue
- MESSAGE - White/Grey
- WARNING - Yellow/Orange
- ERROR - Red
Logging
To log a message simply use the Logger::log
function by
passing a message, a LogType
, and an optional list of
templated variadic arguments.
For example, here is how you can print Hello, World!
as
a success:
::Logger::log("Hello, World!", UVK_LOG_TYPE_SUCCESS); UVKLog
Additionally, you can provide a list of optional templated variadic arguments.
Printing a mix of strings and numbers:
::Logger::log("Hello, World!", UVK_LOG_TYPE_SUCCESS, " Here we have a regular integer: ", 5, "; And here we have a float: ", 10.5f); UVKLog
Note
Any type that supports outputting to std::ostream
can be
used with the logger.
Using the dear imgui console widget
To use the console widget, first go back here to see how you can enable it.
Now that you have it enabled you can use the
UVKLog::ImGuiConsole
class:
- Create an instance of the class
- In you game loop, decide on how you want to display it
If you want a separate ImGui window, you can use the
void displayFull(bool& bOpen, bool* bInteractingWithTextbox)
member function.
The second argument is a bool*
that you can use to check
if the text box is currently active. If you don't need that
functionality you can leave it as nullptr
.
If you don't want a full window, you can also use the
void display(bool* bInteractingWithTextBox)
function. The
function takes the same bool*
for checking, whether you're
interacting with the textbox or not. If you're not using this feature
leave it as nullptr
.
Changing the colours
To change the colours you can use the
void setLogColour(ImVec4 colours, LogType type)
function.
Adding Commands
By default, there are 2 commands in the console, clear
and help
which are self-explanatory.
To add your own commands, you can initialise a struct of type
CommandType
, which looks like this:
struct CommandType
{
std::string cmd;
std::string cmdHint;
std::function<void(const std::string&)> func;
};
The cmd
string is the name of the command. We check if
the command passed in the text box starts with the contents of the
cmd
string.
The cmdHint
string defines the string to be shown in the
help message.
The func
function pointer is called when the command is
found. It takes a const std::string&
which you can use
to parse additional arguments.
After you have your struct initialised, you can pass it to the
UVKLog::ImGuiConsole::addCommand(const CommandType& cmd)
function.
After you have added this, your command is ready to be used. Example:
const UVKLog::CommandType customCommand =
{
.cmd = "test",
.cmdHint = "Just a test command",
.func = [&](const std::string&){ UVKLog::ImGuiConsole::addToMessageLog("Test", UVK_LOG_TYPE_MESSAGE); }
};
::ImGuiConsole::addCommand(customCommand); UVKLog
Adding messages to the log without formatting
Instead of using standard logging, you can also use the
UVKLog::ImGuiConsole::addToMessageLog(const std::string&, LogType type)
function. It adds a string with a log type for colour, but unlike the
normal log function, it doesn't have any formatting. Here is an
example:
::ImGuiConsole::addToMessageLog("Test", UVK_LOG_TYPE_MESSAGE); UVKLog
Utility classes
The Timer class
Together with the logging library and console widget, we also include
a Timer
class that is used to record how much time a task
takes.
Usage:
- Create an instance of the
Timer
class - Call the
start
function to start the timer - Call the
stop
function to stop the timer - Get the time using the
get
function
If you want to continue recording, call the stop
function again without calling the start
function. In
reality the start
function simply resets the timer, so not
calling it allows you to use the same timer object multiple times.
Here's an example:
;
Timer timer.start();
timer
();
doStuff
.stop()
timer::Logger::log("doStuff took ", UVK_LOG_TYPE_NOTE, timer.get(), " milliseconds!");
UVKLog
();
doStuff2
::Logger::log("doStuff and doStuff2 took ", UVK_LOG_TYPE_NOTE, timer.get(), " milliseconds!"); UVKLog