Initial commit
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
/.vs
|
||||||
|
x64
|
||||||
28
PeakMenu.sln
Normal file
28
PeakMenu.sln
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.12.35506.116 d17.12
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PeakMenu", "PeakMenu\PeakMenu.vcxproj", "{AF72AD5F-FAC1-489C-A022-36C08515D36B}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|x64 = Debug|x64
|
||||||
|
Debug|x86 = Debug|x86
|
||||||
|
Release|x64 = Release|x64
|
||||||
|
Release|x86 = Release|x86
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{AF72AD5F-FAC1-489C-A022-36C08515D36B}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{AF72AD5F-FAC1-489C-A022-36C08515D36B}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{AF72AD5F-FAC1-489C-A022-36C08515D36B}.Debug|x86.ActiveCfg = Debug|Win32
|
||||||
|
{AF72AD5F-FAC1-489C-A022-36C08515D36B}.Debug|x86.Build.0 = Debug|Win32
|
||||||
|
{AF72AD5F-FAC1-489C-A022-36C08515D36B}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{AF72AD5F-FAC1-489C-A022-36C08515D36B}.Release|x64.Build.0 = Release|x64
|
||||||
|
{AF72AD5F-FAC1-489C-A022-36C08515D36B}.Release|x86.ActiveCfg = Release|Win32
|
||||||
|
{AF72AD5F-FAC1-489C-A022-36C08515D36B}.Release|x86.Build.0 = Release|Win32
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
156
PeakMenu/PeakMenu.vcxproj
Normal file
156
PeakMenu/PeakMenu.vcxproj
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup Label="ProjectConfigurations">
|
||||||
|
<ProjectConfiguration Include="Debug|Win32">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|Win32">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Debug|x64">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|x64">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
</ItemGroup>
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<VCProjectVersion>17.0</VCProjectVersion>
|
||||||
|
<Keyword>Win32Proj</Keyword>
|
||||||
|
<ProjectGuid>{af72ad5f-fac1-489c-a022-36c08515d36b}</ProjectGuid>
|
||||||
|
<RootNamespace>PeakMenu</RootNamespace>
|
||||||
|
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||||
|
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||||
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||||
|
<ImportGroup Label="ExtensionSettings">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="Shared">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<PropertyGroup Label="UserMacros" />
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<ConformanceMode>true</ConformanceMode>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<ConformanceMode>true</ConformanceMode>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<ConformanceMode>true</ConformanceMode>
|
||||||
|
<LanguageStandard>stdcpp20</LanguageStandard>
|
||||||
|
<AdditionalIncludeDirectories>C:\Users\raefb\Documents\GitHub\PeakMenu\PeakMenu\g3log;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<ConformanceMode>true</ConformanceMode>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="g3log\crashhandler_windows.cpp" />
|
||||||
|
<ClCompile Include="g3log\filesink.cpp" />
|
||||||
|
<ClCompile Include="g3log\g3log.cpp" />
|
||||||
|
<ClCompile Include="g3log\logcapture.cpp" />
|
||||||
|
<ClCompile Include="g3log\loglevels.cpp" />
|
||||||
|
<ClCompile Include="g3log\logmessage.cpp" />
|
||||||
|
<ClCompile Include="g3log\logworker.cpp" />
|
||||||
|
<ClCompile Include="g3log\stacktrace_windows.cpp" />
|
||||||
|
<ClCompile Include="g3log\time.cpp" />
|
||||||
|
<ClCompile Include="main.cpp" />
|
||||||
|
<ClCompile Include="pointers.cpp" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="common.hpp" />
|
||||||
|
<ClInclude Include="g3log\filesinkhelper.ipp" />
|
||||||
|
<ClInclude Include="g3log\g2log.hpp" />
|
||||||
|
<ClInclude Include="logger.hpp" />
|
||||||
|
<ClInclude Include="memory.hpp" />
|
||||||
|
<ClInclude Include="offsets.hpp" />
|
||||||
|
<ClInclude Include="pointers.hpp" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
</ImportGroup>
|
||||||
|
</Project>
|
||||||
78
PeakMenu/PeakMenu.vcxproj.filters
Normal file
78
PeakMenu/PeakMenu.vcxproj.filters
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup>
|
||||||
|
<Filter Include="Source Files">
|
||||||
|
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||||
|
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="Header Files">
|
||||||
|
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||||
|
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="Resource Files">
|
||||||
|
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||||
|
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="Source Files\g3log">
|
||||||
|
<UniqueIdentifier>{06b8dc19-35a5-4810-9c1c-bc3255a43a94}</UniqueIdentifier>
|
||||||
|
</Filter>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="main.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="g3log\crashhandler_windows.cpp">
|
||||||
|
<Filter>Source Files\g3log</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="g3log\filesink.cpp">
|
||||||
|
<Filter>Source Files\g3log</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="g3log\g3log.cpp">
|
||||||
|
<Filter>Source Files\g3log</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="g3log\logcapture.cpp">
|
||||||
|
<Filter>Source Files\g3log</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="g3log\loglevels.cpp">
|
||||||
|
<Filter>Source Files\g3log</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="g3log\logmessage.cpp">
|
||||||
|
<Filter>Source Files\g3log</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="g3log\logworker.cpp">
|
||||||
|
<Filter>Source Files\g3log</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="g3log\stacktrace_windows.cpp">
|
||||||
|
<Filter>Source Files\g3log</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="g3log\time.cpp">
|
||||||
|
<Filter>Source Files\g3log</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="pointers.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="common.hpp">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="g3log\filesinkhelper.ipp">
|
||||||
|
<Filter>Source Files\g3log</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="g3log\g2log.hpp">
|
||||||
|
<Filter>Source Files\g3log</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="logger.hpp">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="memory.hpp">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="pointers.hpp">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="offsets.hpp">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
4
PeakMenu/PeakMenu.vcxproj.user
Normal file
4
PeakMenu/PeakMenu.vcxproj.user
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup />
|
||||||
|
</Project>
|
||||||
15
PeakMenu/common.hpp
Normal file
15
PeakMenu/common.hpp
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
namespace peak
|
||||||
|
{
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
inline bool isRunning{true};
|
||||||
|
inline HMODULE hModule;
|
||||||
|
inline HANDLE mainThread{};
|
||||||
|
inline DWORD mainThreadId{};
|
||||||
|
}
|
||||||
249
PeakMenu/g3log/crashhandler_windows.cpp
Normal file
249
PeakMenu/g3log/crashhandler_windows.cpp
Normal file
@@ -0,0 +1,249 @@
|
|||||||
|
/** ==========================================================================
|
||||||
|
* 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
||||||
|
* with no warranties. This code is yours to share, use and modify with no
|
||||||
|
* strings attached and no restrictions or obligations.
|
||||||
|
*
|
||||||
|
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
||||||
|
* ============================================================================*/
|
||||||
|
|
||||||
|
#if !(defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
|
||||||
|
#error "crashhandler_windows.cpp used but not on a windows system"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <process.h> // getpid
|
||||||
|
#include <windows.h>
|
||||||
|
#include <atomic>
|
||||||
|
#include <csignal>
|
||||||
|
#include <cstring>
|
||||||
|
#include <sstream>
|
||||||
|
#include "g3log/crashhandler.hpp"
|
||||||
|
#include "g3log/g3log.hpp"
|
||||||
|
#include "g3log/logcapture.hpp"
|
||||||
|
#include "g3log/stacktrace_windows.hpp"
|
||||||
|
|
||||||
|
#define getpid _getpid
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
std::atomic<bool> gBlockForFatal{true};
|
||||||
|
LPTOP_LEVEL_EXCEPTION_FILTER g_previous_unexpected_exception_handler = nullptr;
|
||||||
|
|
||||||
|
#if !(defined(DISABLE_FATAL_SIGNALHANDLING))
|
||||||
|
thread_local bool g_installed_thread_signal_handler = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !(defined(DISABLE_VECTORED_EXCEPTIONHANDLING))
|
||||||
|
void* g_vector_exception_handler = nullptr;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// called for fatal signals SIGABRT, SIGFPE, SIGSEGV, SIGILL, SIGTERM
|
||||||
|
void signalHandler(int signal_number) {
|
||||||
|
using namespace g3::internal;
|
||||||
|
std::string dump = stacktrace::stackdump();
|
||||||
|
|
||||||
|
std::ostringstream fatal_stream;
|
||||||
|
fatal_stream << "\n***** Received fatal signal " << g3::internal::exitReasonName(g3::internal::FATAL_SIGNAL, signal_number);
|
||||||
|
fatal_stream << "(" << signal_number << ")\tPID: " << getpid() << std::endl;
|
||||||
|
|
||||||
|
LogCapture trigger(FATAL_SIGNAL, static_cast<g3::SignalType>(signal_number), dump.c_str());
|
||||||
|
trigger.stream() << fatal_stream.str();
|
||||||
|
|
||||||
|
// Trigger debug break point, if we're in debug. This breakpoint CAN cause a slowdown when it happens.
|
||||||
|
// Be patient. The "Debug" dialog should pop-up eventually if you doing it in Visual Studio.
|
||||||
|
// For fatal signals only, not exceptions.
|
||||||
|
// This is a way to tell the IDE (if in dev mode) that it can stop at this breakpoint
|
||||||
|
// Note that at this time the fatal log event with stack trace is NOT yet flushed to the logger
|
||||||
|
// This call will do nothing unless we're in DEBUG and "DEBUG_BREAK_AT_FATAL_SIGNAL" is enabled
|
||||||
|
// ref: g3log/Options.cmake
|
||||||
|
#if (!defined(NDEBUG) && defined(DEBUG_BREAK_AT_FATAL_SIGNAL))
|
||||||
|
__debugbreak();
|
||||||
|
#endif
|
||||||
|
} // scope exit - message sent to LogWorker, wait to die...
|
||||||
|
|
||||||
|
// Unhandled exception catching
|
||||||
|
LONG WINAPI exceptionHandling(EXCEPTION_POINTERS* info, const std::string& handler) {
|
||||||
|
std::string dump = stacktrace::stackdump(info);
|
||||||
|
|
||||||
|
std::ostringstream fatal_stream;
|
||||||
|
const g3::SignalType exception_code = info->ExceptionRecord->ExceptionCode;
|
||||||
|
fatal_stream << "\n***** " << handler << ": Received fatal exception " << g3::internal::exitReasonName(g3::internal::FATAL_EXCEPTION, exception_code);
|
||||||
|
fatal_stream << "\tPID: " << getpid() << std::endl;
|
||||||
|
|
||||||
|
const auto fatal_id = static_cast<g3::SignalType>(exception_code);
|
||||||
|
LogCapture trigger(g3::internal::FATAL_EXCEPTION, fatal_id, dump.c_str());
|
||||||
|
trigger.stream() << fatal_stream.str();
|
||||||
|
// FATAL Exception: It doesn't necessarily stop here. we pass on continue search
|
||||||
|
// if no one else will catch that then it's goodbye anyhow.
|
||||||
|
// The RISK here is if someone is catching this and returning "EXCEPTION_EXECUTE_HANDLER"
|
||||||
|
// but does not shutdown then the software will be running with g3log shutdown.
|
||||||
|
// .... However... this must be seen as a bug from standard handling of fatal exceptions
|
||||||
|
// https://msdn.microsoft.com/en-us/library/6wxdsc38.aspx
|
||||||
|
return EXCEPTION_CONTINUE_SEARCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unhandled exception catching
|
||||||
|
LONG WINAPI unexpectedExceptionHandling(EXCEPTION_POINTERS* info) {
|
||||||
|
g3::internal::restoreFatalHandlingToDefault();
|
||||||
|
return exceptionHandling(info, "Unexpected Exception Handler");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Setup through (Windows API) AddVectoredExceptionHandler
|
||||||
|
/// Ref: http://blogs.msdn.com/b/zhanli/archive/2010/06/25/c-tips-addvectoredexceptionhandler-addvectoredcontinuehandler-and-setunhandledexceptionfilter.aspx
|
||||||
|
#if !(defined(DISABLE_VECTORED_EXCEPTIONHANDLING))
|
||||||
|
LONG WINAPI vectorExceptionHandling(PEXCEPTION_POINTERS p) {
|
||||||
|
const g3::SignalType exception_code = p->ExceptionRecord->ExceptionCode;
|
||||||
|
if (false == stacktrace::isKnownException(exception_code)) {
|
||||||
|
// The unknown exception is ignored. Since it is not a Windows
|
||||||
|
// fatal exception generated by the OS we leave the
|
||||||
|
// responsibility to deal with this by the client software.
|
||||||
|
return EXCEPTION_CONTINUE_SEARCH;
|
||||||
|
} else {
|
||||||
|
g3::internal::restoreFatalHandlingToDefault();
|
||||||
|
return exceptionHandling(p, "Vectored Exception Handler");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
} // end anonymous namespace
|
||||||
|
|
||||||
|
namespace g3 {
|
||||||
|
namespace internal {
|
||||||
|
// For windows exceptions this might ONCE be set to false, in case of a
|
||||||
|
// windows exceptions and not a signal
|
||||||
|
bool shouldBlockForFatalHandling() {
|
||||||
|
return gBlockForFatal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generate stackdump. Or in case a stackdump was pre-generated and
|
||||||
|
/// non-empty just use that one. i.e. the latter case is only for
|
||||||
|
/// Windows and test purposes
|
||||||
|
std::string stackdump(const char* dump) {
|
||||||
|
if (nullptr != dump && !std::string(dump).empty()) {
|
||||||
|
return {dump};
|
||||||
|
}
|
||||||
|
|
||||||
|
return stacktrace::stackdump();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// string representation of signal ID or Windows exception id
|
||||||
|
std::string exitReasonName(const LEVELS& level, g3::SignalType fatal_id) {
|
||||||
|
if (level == g3::internal::FATAL_EXCEPTION) {
|
||||||
|
return stacktrace::exceptionIdToText(fatal_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (fatal_id) {
|
||||||
|
case SIGABRT:
|
||||||
|
return "SIGABRT";
|
||||||
|
break;
|
||||||
|
case SIGFPE:
|
||||||
|
return "SIGFPE";
|
||||||
|
break;
|
||||||
|
case SIGSEGV:
|
||||||
|
return "SIGSEGV";
|
||||||
|
break;
|
||||||
|
case SIGILL:
|
||||||
|
return "SIGILL";
|
||||||
|
break;
|
||||||
|
case SIGTERM:
|
||||||
|
return "SIGTERM";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "UNKNOWN SIGNAL(" << fatal_id << ")";
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Triggered by g3log::LogWorker after receiving a FATAL trigger
|
||||||
|
// which is LOG(FATAL), CHECK(false) or a fatal signal our signalhandler caught.
|
||||||
|
// --- If LOG(FATAL) or CHECK(false) the signal_number will be SIGABRT
|
||||||
|
void exitWithDefaultSignalHandler(const LEVELS& level, g3::SignalType fatal_signal_id) {
|
||||||
|
restoreFatalHandlingToDefault();
|
||||||
|
// For windows exceptions we want to continue the possibility of
|
||||||
|
// exception handling now when the log and stacktrace are flushed
|
||||||
|
// to sinks. We therefore avoid to kill the process here. Instead
|
||||||
|
// it will be the exceptionHandling functions above that
|
||||||
|
// will let exception handling continue with: EXCEPTION_CONTINUE_SEARCH
|
||||||
|
if (g3::internal::FATAL_EXCEPTION == level) {
|
||||||
|
gBlockForFatal = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// for a signal however, we exit through that fatal signal
|
||||||
|
const int signal_number = static_cast<int>(fatal_signal_id);
|
||||||
|
raise(signal_number);
|
||||||
|
}
|
||||||
|
|
||||||
|
// FYI: Concept of async-signal-safe operations does not exist on windows
|
||||||
|
// we stick to perror for lack of better alternatives.
|
||||||
|
size_t writeErrorMessage(const char* message) {
|
||||||
|
perror(message);
|
||||||
|
return std::strlen(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore back to default fatal event handling
|
||||||
|
void restoreFatalHandlingToDefault() {
|
||||||
|
#if !(defined(DISABLE_FATAL_SIGNALHANDLING))
|
||||||
|
SetUnhandledExceptionFilter(g_previous_unexpected_exception_handler);
|
||||||
|
|
||||||
|
#if !(defined(DISABLE_VECTORED_EXCEPTIONHANDLING))
|
||||||
|
RemoveVectoredExceptionHandler(g_vector_exception_handler);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (SIG_ERR == signal(SIGABRT, SIG_DFL))
|
||||||
|
internal::writeErrorMessage("signal - SIGABRT");
|
||||||
|
|
||||||
|
if (SIG_ERR == signal(SIGFPE, SIG_DFL))
|
||||||
|
internal::writeErrorMessage("signal - SIGABRT");
|
||||||
|
|
||||||
|
if (SIG_ERR == signal(SIGSEGV, SIG_DFL))
|
||||||
|
internal::writeErrorMessage("signal - SIGABRT");
|
||||||
|
|
||||||
|
if (SIG_ERR == signal(SIGILL, SIG_DFL))
|
||||||
|
internal::writeErrorMessage("signal - SIGABRT");
|
||||||
|
|
||||||
|
if (SIG_ERR == signal(SIGTERM, SIG_DFL))
|
||||||
|
internal::writeErrorMessage("signal - SIGABRT");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void installSignalHandler() {
|
||||||
|
g3::installSignalHandlerForThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
/// SIGFPE, SIGILL, and SIGSEGV handling must be installed per thread
|
||||||
|
/// on Windows. This is automatically done if you do at least one LOG(...) call
|
||||||
|
/// you can also use this function call, per thread so make sure these three
|
||||||
|
/// fatal signals are covered in your thread (even if you don't do a LOG(...) call
|
||||||
|
void installSignalHandlerForThread() {
|
||||||
|
#if !(defined(DISABLE_FATAL_SIGNALHANDLING))
|
||||||
|
if (!g_installed_thread_signal_handler) {
|
||||||
|
g_installed_thread_signal_handler = true;
|
||||||
|
if (SIG_ERR == signal(SIGTERM, signalHandler))
|
||||||
|
internal::writeErrorMessage("signal - SIGTERM");
|
||||||
|
if (SIG_ERR == signal(SIGABRT, signalHandler))
|
||||||
|
internal::writeErrorMessage("signal - SIGABRT");
|
||||||
|
if (SIG_ERR == signal(SIGFPE, signalHandler))
|
||||||
|
internal::writeErrorMessage("signal - SIGFPE");
|
||||||
|
if (SIG_ERR == signal(SIGSEGV, signalHandler))
|
||||||
|
internal::writeErrorMessage("signal - SIGSEGV");
|
||||||
|
if (SIG_ERR == signal(SIGILL, signalHandler))
|
||||||
|
internal::writeErrorMessage("signal - SIGILL");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void installCrashHandler() {
|
||||||
|
internal::installSignalHandler();
|
||||||
|
g_previous_unexpected_exception_handler = SetUnhandledExceptionFilter(unexpectedExceptionHandling);
|
||||||
|
|
||||||
|
#if !(defined(DISABLE_VECTORED_EXCEPTIONHANDLING))
|
||||||
|
// const size_t kFirstExceptionHandler = 1;
|
||||||
|
// kFirstExceptionsHandler is kept here for documentation purposes.
|
||||||
|
// The last exception seems more like what we want.
|
||||||
|
const size_t kLastExceptionHandler = 0;
|
||||||
|
g_vector_exception_handler = AddVectoredExceptionHandler(kLastExceptionHandler, vectorExceptionHandling);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end namespace g3
|
||||||
117
PeakMenu/g3log/filesink.cpp
Normal file
117
PeakMenu/g3log/filesink.cpp
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
/** ==========================================================================
|
||||||
|
* 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
||||||
|
* with no warranties. This code is yours to share, use and modify with no
|
||||||
|
* strings attached and no restrictions or obligations.
|
||||||
|
*
|
||||||
|
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
||||||
|
* ============================================================================*/
|
||||||
|
|
||||||
|
#include "g3log/filesink.hpp"
|
||||||
|
#include <cassert>
|
||||||
|
#include <chrono>
|
||||||
|
#include "filesinkhelper.ipp"
|
||||||
|
|
||||||
|
namespace g3 {
|
||||||
|
using namespace internal;
|
||||||
|
|
||||||
|
FileSink::FileSink(const std::string& log_prefix, const std::string& log_directory, const std::string& logger_id, size_t write_to_log_every_x_message) :
|
||||||
|
_log_details_func(&LogMessage::DefaultLogDetailsToString),
|
||||||
|
_log_file_with_path(log_directory),
|
||||||
|
_log_prefix_backup(log_prefix),
|
||||||
|
_outptr(new std::ofstream),
|
||||||
|
_header("\t\tLOG format: [YYYY/MM/DD hh:mm:ss uuu* LEVEL FILE->FUNCTION:LINE] message\n\n\t\t(uuu*: microseconds fractions of the seconds value)\n\n"),
|
||||||
|
_firstEntry(true),
|
||||||
|
_write_counter(0),
|
||||||
|
_write_to_log_every_x_message(write_to_log_every_x_message) {
|
||||||
|
_log_prefix_backup = prefixSanityFix(log_prefix);
|
||||||
|
if (!isValidFilename(_log_prefix_backup)) {
|
||||||
|
std::cerr << "g3log: forced abort due to illegal log prefix [" << log_prefix << "]" << std::endl;
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string file_name = createLogFileName(_log_prefix_backup, logger_id);
|
||||||
|
_log_file_with_path = pathSanityFix(_log_file_with_path, file_name);
|
||||||
|
_outptr = createLogFile(_log_file_with_path);
|
||||||
|
|
||||||
|
if (!_outptr) {
|
||||||
|
std::cerr << "Cannot write log file to location, attempting current directory" << std::endl;
|
||||||
|
_log_file_with_path = "./" + file_name;
|
||||||
|
_outptr = createLogFile(_log_file_with_path);
|
||||||
|
}
|
||||||
|
assert(_outptr && "cannot open log file at startup");
|
||||||
|
}
|
||||||
|
|
||||||
|
FileSink::~FileSink() {
|
||||||
|
std::string exit_msg = {"g3log g3FileSink shutdown at: "};
|
||||||
|
auto now = std::chrono::system_clock::now();
|
||||||
|
exit_msg.append(localtime_formatted(now, internal::time_formatted)).append("\n");
|
||||||
|
|
||||||
|
// write anything buffered up and then end with the exit msg
|
||||||
|
filestream() << _write_buffer << exit_msg << std::flush;
|
||||||
|
|
||||||
|
exit_msg.append("Log file at: [").append(_log_file_with_path).append("]\n");
|
||||||
|
std::cerr << exit_msg << std::flush;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The actual log receiving function
|
||||||
|
void FileSink::fileWrite(LogMessageMover message) {
|
||||||
|
if (_firstEntry) {
|
||||||
|
addLogFileHeader();
|
||||||
|
_firstEntry = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto data = message.get().toString(_log_details_func);
|
||||||
|
|
||||||
|
_write_buffer.append(data);
|
||||||
|
if (++_write_counter % _write_to_log_every_x_message == 0) {
|
||||||
|
filestream() << _write_buffer << std::flush;
|
||||||
|
_write_buffer.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string FileSink::changeLogFile(const std::string& directory, const std::string& logger_id) {
|
||||||
|
|
||||||
|
auto now = std::chrono::system_clock::now();
|
||||||
|
auto now_formatted = g3::localtime_formatted(now, {internal::date_formatted + " " + internal::time_formatted});
|
||||||
|
|
||||||
|
std::string file_name = createLogFileName(_log_prefix_backup, logger_id);
|
||||||
|
std::string prospect_log = directory + file_name;
|
||||||
|
std::unique_ptr<std::ofstream> log_stream = createLogFile(prospect_log);
|
||||||
|
if (nullptr == log_stream) {
|
||||||
|
filestream() << "\n"
|
||||||
|
<< now_formatted << " Unable to change log file. Illegal filename or busy? Unsuccessful log name was: " << prospect_log;
|
||||||
|
return {}; // no success
|
||||||
|
}
|
||||||
|
|
||||||
|
addLogFileHeader();
|
||||||
|
std::ostringstream ss_change;
|
||||||
|
ss_change << "\n\tChanging log file from : " << _log_file_with_path;
|
||||||
|
ss_change << "\n\tto new location: " << prospect_log << "\n";
|
||||||
|
filestream() << now_formatted << ss_change.str();
|
||||||
|
ss_change.str("");
|
||||||
|
|
||||||
|
std::string old_log = _log_file_with_path;
|
||||||
|
_log_file_with_path = std::move(prospect_log);
|
||||||
|
_outptr = std::move(log_stream);
|
||||||
|
ss_change << "\n\tNew log file. The previous log file was at: ";
|
||||||
|
ss_change << old_log << "\n";
|
||||||
|
filestream() << now_formatted << ss_change.str();
|
||||||
|
return _log_file_with_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string FileSink::fileName() {
|
||||||
|
return _log_file_with_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSink::overrideLogDetails(LogMessage::LogDetailsFunc func) {
|
||||||
|
_log_details_func = func;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSink::overrideLogHeader(const std::string& change) {
|
||||||
|
_header = change;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSink::addLogFileHeader() {
|
||||||
|
filestream() << header(_header);
|
||||||
|
}
|
||||||
|
} // namespace g3
|
||||||
127
PeakMenu/g3log/filesinkhelper.ipp
Normal file
127
PeakMenu/g3log/filesinkhelper.ipp
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
/** ==========================================================================
|
||||||
|
* 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
||||||
|
* with no warranties. This code is yours to share, use and modify with no
|
||||||
|
* strings attached and no restrictions or obligations.
|
||||||
|
*
|
||||||
|
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
||||||
|
* ============================================================================*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
|
||||||
|
namespace g3 {
|
||||||
|
namespace internal {
|
||||||
|
static const std::string file_name_time_formatted = "%Y%m%d-%H%M%S";
|
||||||
|
|
||||||
|
// check for filename validity - filename should not be part of PATH
|
||||||
|
bool isValidFilename(const std::string &prefix_filename) {
|
||||||
|
std::string illegal_characters("/,|<>:#$%{}[]\'\"^!?+* ");
|
||||||
|
size_t pos = prefix_filename.find_first_of(illegal_characters, 0);
|
||||||
|
if (pos != std::string::npos) {
|
||||||
|
std::cerr << "Illegal character [" << prefix_filename.at(pos) << "] in logname prefix: " << "[" << prefix_filename << "]" << std::endl;
|
||||||
|
return false;
|
||||||
|
} else if (prefix_filename.empty()) {
|
||||||
|
std::cerr << "Empty filename prefix is not allowed" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string prefixSanityFix(std::string prefix) {
|
||||||
|
prefix.erase(std::remove_if(prefix.begin(), prefix.end(), ::isspace), prefix.end());
|
||||||
|
prefix.erase(std::remove(prefix.begin(), prefix.end(), '/'), prefix.end());
|
||||||
|
prefix.erase(std::remove(prefix.begin(), prefix.end(), '\\'), prefix.end());
|
||||||
|
prefix.erase(std::remove(prefix.begin(), prefix.end(), '.'), prefix.end());
|
||||||
|
prefix.erase(std::remove(prefix.begin(), prefix.end(), ':'), prefix.end());
|
||||||
|
if (!isValidFilename(prefix)) {
|
||||||
|
return
|
||||||
|
{
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string pathSanityFix(std::string path, const std::string &file_name) {
|
||||||
|
// Unify the delimeters,. maybe sketchy solution but it seems to work
|
||||||
|
// on at least win7 + ubuntu. All bets are off for older windows
|
||||||
|
std::replace(path.begin(), path.end(), '\\', '/');
|
||||||
|
|
||||||
|
// clean up in case of multiples
|
||||||
|
auto contains_end = [&](std::string & in) -> bool {
|
||||||
|
size_t size = in.size();
|
||||||
|
if (!size) return false;
|
||||||
|
char end = in[size - 1];
|
||||||
|
return (end == '/' || end == ' ');
|
||||||
|
};
|
||||||
|
|
||||||
|
while (contains_end(path)) {
|
||||||
|
path.erase(path.size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!path.empty()) {
|
||||||
|
path.insert(path.end(), '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
path.insert(path.size(), file_name);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string header(const std::string& headerFormat) {
|
||||||
|
std::ostringstream ss_entry;
|
||||||
|
// Day Month Date Time Year: is written as "%a %b %d %H:%M:%S %Y" and formatted output as : Wed Sep 19 08:28:16 2012
|
||||||
|
auto now = std::chrono::system_clock::now();
|
||||||
|
ss_entry << "\t\tg3log created log at: " << g3::localtime_formatted(now, "%a %b %d %H:%M:%S %Y") << "\n";
|
||||||
|
ss_entry << headerFormat;
|
||||||
|
return ss_entry.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string createLogFileName(const std::string &verified_prefix, const std::string &logger_id) {
|
||||||
|
std::stringstream oss_name;
|
||||||
|
oss_name << verified_prefix << ".";
|
||||||
|
if( !logger_id.empty() ) {
|
||||||
|
oss_name << logger_id << ".";
|
||||||
|
}
|
||||||
|
auto now = std::chrono::system_clock::now();
|
||||||
|
oss_name << g3::localtime_formatted(now, file_name_time_formatted);
|
||||||
|
oss_name << ".log";
|
||||||
|
return oss_name.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool openLogFile(const std::string &complete_file_with_path, std::ofstream &outstream) {
|
||||||
|
std::ios_base::openmode mode = std::ios_base::out; // for clarity: it's really overkill since it's an ofstream
|
||||||
|
mode |= std::ios_base::trunc;
|
||||||
|
outstream.open(complete_file_with_path, mode);
|
||||||
|
if (!outstream.is_open()) {
|
||||||
|
std::ostringstream ss_error;
|
||||||
|
ss_error << "FILE ERROR: could not open log file:[" << complete_file_with_path << "]";
|
||||||
|
ss_error << "\n\t\t std::ios_base state = " << outstream.rdstate();
|
||||||
|
std::cerr << ss_error.str().c_str() << std::endl;
|
||||||
|
outstream.close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<std::ofstream> createLogFile(const std::string &file_with_full_path) {
|
||||||
|
std::unique_ptr<std::ofstream> out(new std::ofstream);
|
||||||
|
std::ofstream &stream(*(out.get()));
|
||||||
|
bool success_with_open_file = openLogFile(file_with_full_path, stream);
|
||||||
|
if (false == success_with_open_file) {
|
||||||
|
out.reset();
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
22
PeakMenu/g3log/g2log.hpp
Normal file
22
PeakMenu/g3log/g2log.hpp
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
/** ==========================================================================
|
||||||
|
* 2015 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
||||||
|
* with no warranties. This code is yours to share, use and modify with no
|
||||||
|
* strings attached and no restrictions or obligations.
|
||||||
|
*
|
||||||
|
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
||||||
|
* ============================================================================*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// For convenience: If you don't want to do a recursive search and replace in your source code
|
||||||
|
// for replacing g2log.hpp for g3log/g3log.hpp then you can choose to add this header file to your
|
||||||
|
// code. It will get the necessary includes
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Btw: replacing g2log for g3log include is easy on Linux
|
||||||
|
// find . -name "*.cpp*" -print | xargs sed -i -e 's/\g2log\.hpp/\g3log\/g3log\.hpp/g'
|
||||||
|
|
||||||
|
#include <g3log/filesink.hpp>
|
||||||
|
#include <g3log/g3log.hpp>
|
||||||
|
#include <g3log/loglevels.hpp>
|
||||||
|
#include <g3log/logworker.hpp>
|
||||||
242
PeakMenu/g3log/g3log.cpp
Normal file
242
PeakMenu/g3log/g3log.cpp
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
/** ==========================================================================
|
||||||
|
* 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
||||||
|
* with no warranties. This code is yours to share, use and modify with no
|
||||||
|
* strings attached and no restrictions or obligations.
|
||||||
|
*
|
||||||
|
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
||||||
|
* ============================================================================
|
||||||
|
*
|
||||||
|
* Filename:g3log.cpp Framework for Logging and Design By Contract
|
||||||
|
* Created: 2011 by Kjell Hedström
|
||||||
|
*
|
||||||
|
* PUBLIC DOMAIN and Not copyrighted since it was built on public-domain software and at least in "spirit" influenced
|
||||||
|
* from the following sources
|
||||||
|
* 1. kjellkod.cc ;)
|
||||||
|
* 2. Dr.Dobbs, Petru Marginean: http://drdobbs.com/article/printableArticle.jhtml?articleId=201804215&dept_url=/cpp/
|
||||||
|
* 3. Dr.Dobbs, Michael Schulze: http://drdobbs.com/article/printableArticle.jhtml?articleId=225700666&dept_url=/cpp/
|
||||||
|
* 4. Google 'glog': http://google-glog.googlecode.com/svn/trunk/doc/glog.html
|
||||||
|
* 5. Various Q&A at StackOverflow
|
||||||
|
* ********************************************* */
|
||||||
|
|
||||||
|
#include "g3log/g3log.hpp"
|
||||||
|
#include "g3log/crashhandler.hpp"
|
||||||
|
#include "g3log/loglevels.hpp"
|
||||||
|
#include "g3log/logmessage.hpp"
|
||||||
|
#include "g3log/logworker.hpp"
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <sstream>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
std::once_flag g_initialize_flag;
|
||||||
|
g3::LogWorker* g_logger_instance = nullptr; // instantiated and OWNED somewhere else (main)
|
||||||
|
std::mutex g_logging_init_mutex;
|
||||||
|
|
||||||
|
std::unique_ptr<g3::LogMessage> g_first_uninitialized_msg = {nullptr};
|
||||||
|
std::once_flag g_set_first_uninitialized_flag;
|
||||||
|
std::once_flag g_save_first_uninitialized_flag;
|
||||||
|
const std::function<void(void)> g_pre_fatal_hook_that_does_nothing = [] { /*does nothing */ };
|
||||||
|
std::function<void(void)> g_fatal_pre_logging_hook;
|
||||||
|
|
||||||
|
std::atomic<size_t> g_fatal_hook_recursive_counter = {0};
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace g3 {
|
||||||
|
// signalhandler and internal clock is only needed to install once
|
||||||
|
// for unit testing purposes the initializeLogging might be called
|
||||||
|
// several times...
|
||||||
|
// for all other practical use, it shouldn't!
|
||||||
|
|
||||||
|
void initializeLogging(LogWorker* bgworker) {
|
||||||
|
std::call_once(g_initialize_flag, [] {
|
||||||
|
installCrashHandler();
|
||||||
|
});
|
||||||
|
std::lock_guard<std::mutex> lock(g_logging_init_mutex);
|
||||||
|
if (internal::isLoggingInitialized() || nullptr == bgworker) {
|
||||||
|
std::ostringstream exitMsg;
|
||||||
|
exitMsg << __FILE__ "->" << __FUNCTION__ << ":" << __LINE__ << std::endl;
|
||||||
|
exitMsg << "\tFatal exit due to illegal initialization of g3::LogWorker\n";
|
||||||
|
exitMsg << "\t(due to multiple initializations? : " << std::boolalpha << internal::isLoggingInitialized();
|
||||||
|
exitMsg << ", due to nullptr == bgworker? : " << std::boolalpha << (nullptr == bgworker) << ")";
|
||||||
|
std::cerr << exitMsg.str() << std::endl;
|
||||||
|
std::exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the first uninitialized message, if any
|
||||||
|
std::call_once(g_save_first_uninitialized_flag, [&bgworker] {
|
||||||
|
if (g_first_uninitialized_msg) {
|
||||||
|
bgworker->save(LogMessagePtr{std::move(g_first_uninitialized_msg)});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
g_logger_instance = bgworker;
|
||||||
|
// by default the pre fatal logging hook does nothing
|
||||||
|
// if it WOULD do something it would happen in
|
||||||
|
setFatalPreLoggingHook(g_pre_fatal_hook_that_does_nothing);
|
||||||
|
// recursive crash counter re-set to zero
|
||||||
|
g_fatal_hook_recursive_counter.store(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* default does nothing, @ref ::g_pre_fatal_hook_that_does_nothing
|
||||||
|
* It will be called just before sending the fatal message, @ref pushFatalmessageToLogger
|
||||||
|
* It will be reset to do nothing in ::initializeLogging(...)
|
||||||
|
* so please call this function, if you ever need to, after initializeLogging(...)
|
||||||
|
*/
|
||||||
|
void setFatalPreLoggingHook(std::function<void(void)> pre_fatal_hook) {
|
||||||
|
static std::mutex m;
|
||||||
|
std::lock_guard<std::mutex> lock(m);
|
||||||
|
g_fatal_pre_logging_hook = pre_fatal_hook;
|
||||||
|
}
|
||||||
|
|
||||||
|
// By default this function pointer goes to \ref pushFatalMessageToLogger;
|
||||||
|
std::function<void(FatalMessagePtr)> g_fatal_to_g3logworker_function_ptr = internal::pushFatalMessageToLogger;
|
||||||
|
|
||||||
|
/** REPLACE fatalCallToLogger for fatalCallForUnitTest
|
||||||
|
* This function switches the function pointer so that only
|
||||||
|
* 'unitTest' mock-fatal calls are made.
|
||||||
|
* */
|
||||||
|
void setFatalExitHandler(std::function<void(FatalMessagePtr)> fatal_call) {
|
||||||
|
g_fatal_to_g3logworker_function_ptr = fatal_call;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
bool isLoggingInitialized() {
|
||||||
|
return g_logger_instance != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shutdown the logging by making the pointer to the background logger to nullptr. The object is not deleted
|
||||||
|
* that is the responsibility of its owner. *
|
||||||
|
*/
|
||||||
|
void shutDownLogging() {
|
||||||
|
std::lock_guard<std::mutex> lock(g_logging_init_mutex);
|
||||||
|
g_logger_instance = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Same as the Shutdown above but called by the destructor of the LogWorker, thus ensuring that no further
|
||||||
|
* LOG(...) calls can happen to a non-existing LogWorker.
|
||||||
|
* @param active MUST BE the LogWorker initialized for logging. If it is not then this call is just ignored
|
||||||
|
* and the logging continues to be active.
|
||||||
|
* @return true if the correct worker was given,. and shutDownLogging was called
|
||||||
|
*/
|
||||||
|
bool shutDownLoggingForActiveOnly(LogWorker* active) {
|
||||||
|
if (isLoggingInitialized() && nullptr != active && (active != g_logger_instance)) {
|
||||||
|
LOG(WARNING) << "\n\t\tAttempted to shut down logging, but the ID of the Logger is not the one that is active."
|
||||||
|
<< "\n\t\tHaving multiple instances of the g3::LogWorker is likely a BUG"
|
||||||
|
<< "\n\t\tEither way, this call to shutDownLogging was ignored"
|
||||||
|
<< "\n\t\tTry g3::internal::shutDownLogging() instead";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
shutDownLogging();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** explicitly copy of all input. This is makes it possibly to use g3log across dynamically loaded libraries
|
||||||
|
* i.e. (dlopen + dlsym) */
|
||||||
|
void saveMessage(const char* entry, const char* file, int line, const char* function, const LEVELS& level,
|
||||||
|
const char* boolean_expression, int fatal_signal, const char* stack_trace) {
|
||||||
|
LEVELS msgLevel{level};
|
||||||
|
LogMessagePtr message{std::make_unique<LogMessage>(file, line, function, msgLevel)};
|
||||||
|
message.get()->write().append(entry);
|
||||||
|
message.get()->setExpression(boolean_expression);
|
||||||
|
|
||||||
|
if (internal::wasFatal(level)) {
|
||||||
|
saveFatalMessage(stack_trace, message, fatal_signal);
|
||||||
|
} else {
|
||||||
|
pushMessageToLogger(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void saveFatalMessage(const char* stack_trace, g3::LogMessagePtr& message, int& fatal_signal) {
|
||||||
|
auto fatalhook = g_fatal_pre_logging_hook;
|
||||||
|
// In case the fatal_pre logging actually will cause a crash in its turn
|
||||||
|
// let's not do recursive crashing!
|
||||||
|
setFatalPreLoggingHook(g_pre_fatal_hook_that_does_nothing);
|
||||||
|
++g_fatal_hook_recursive_counter; // thread safe counter
|
||||||
|
// "benign" race here. If two threads crashes, with recursive crashes
|
||||||
|
// then it's possible that the "other" fatal stack trace will be shown
|
||||||
|
// that's OK since it was anyhow the first crash detected
|
||||||
|
static const std::string first_stack_trace = stack_trace;
|
||||||
|
fatalhook();
|
||||||
|
message.get()->write().append(stack_trace);
|
||||||
|
|
||||||
|
if (g_fatal_hook_recursive_counter.load() > 1) {
|
||||||
|
message.get()->write().append(
|
||||||
|
"\n\n\nWARNING\n"
|
||||||
|
"A recursive crash detected. It is likely the hook set with 'setFatalPreLoggingHook(...)' is responsible\n\n")
|
||||||
|
.append("---First crash stacktrace: ")
|
||||||
|
.append(first_stack_trace)
|
||||||
|
.append("\n---End of first stacktrace\n");
|
||||||
|
}
|
||||||
|
FatalMessagePtr fatal_message{std::make_unique<FatalMessage>(*(message._move_only.get()), fatal_signal)};
|
||||||
|
// At destruction, flushes fatal message to g3LogWorker
|
||||||
|
// either we will stay here until the background worker has received the fatal
|
||||||
|
// message, flushed the crash message to the sinks and exits with the same fatal signal
|
||||||
|
//..... OR it's in unit-test mode then we throw a std::runtime_error (and never hit sleep)
|
||||||
|
fatalCall(fatal_message);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* save the message to the logger. In case of called before the logger is instantiated
|
||||||
|
* the first message will be saved. Any following subsequent uninitialized log calls
|
||||||
|
* will be ignored.
|
||||||
|
*
|
||||||
|
* The first initialized log entry will also save the first uninitialized log message, if any
|
||||||
|
* @param log_entry to save to logger
|
||||||
|
*/
|
||||||
|
void pushMessageToLogger(LogMessagePtr incoming) { // todo rename to Push SavedMessage To Worker
|
||||||
|
// Uninitialized messages are ignored but does not CHECK/crash the logger
|
||||||
|
if (!internal::isLoggingInitialized()) {
|
||||||
|
std::call_once(g_set_first_uninitialized_flag, [&] {
|
||||||
|
g_first_uninitialized_msg = incoming.release();
|
||||||
|
std::string err = {"LOGGER NOT INITIALIZED:\n\t\t"};
|
||||||
|
err.append(g_first_uninitialized_msg->message());
|
||||||
|
std::string& str = g_first_uninitialized_msg->write();
|
||||||
|
str.clear();
|
||||||
|
str.append(err); // replace content
|
||||||
|
std::cerr << str << std::endl; });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// logger is initialized
|
||||||
|
g_logger_instance->save(incoming);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Fatal call saved to logger. This will trigger SIGABRT or other fatal signal
|
||||||
|
* to exit the program. After saving the fatal message the calling thread
|
||||||
|
* will sleep forever (i.e. until the background thread catches up, saves the fatal
|
||||||
|
* message and kills the software with the fatal signal.
|
||||||
|
*/
|
||||||
|
void pushFatalMessageToLogger(FatalMessagePtr message) {
|
||||||
|
if (!isLoggingInitialized()) {
|
||||||
|
std::ostringstream error;
|
||||||
|
error << "FATAL CALL but logger is NOT initialized\n"
|
||||||
|
<< "CAUSE: " << message.get()->reason()
|
||||||
|
<< "\nMessage: \n"
|
||||||
|
<< message.get()->toString() << std::flush;
|
||||||
|
std::cerr << error.str() << std::flush;
|
||||||
|
internal::exitWithDefaultSignalHandler(message.get()->_level, message.get()->_signal_id);
|
||||||
|
}
|
||||||
|
g_logger_instance->fatal(message);
|
||||||
|
while (shouldBlockForFatalHandling()) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The default, initial, handling to send a 'fatal' event to g3logworker
|
||||||
|
* the caller will stay here, eternally, until the software is aborted
|
||||||
|
* ... in the case of unit testing it is the given "Mock" fatalCall that will
|
||||||
|
* define the behaviour.
|
||||||
|
*/
|
||||||
|
void fatalCall(FatalMessagePtr message) {
|
||||||
|
g_fatal_to_g3logworker_function_ptr(FatalMessagePtr{std::move(message)});
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace g3
|
||||||
67
PeakMenu/g3log/g3log/active.hpp
Normal file
67
PeakMenu/g3log/g3log/active.hpp
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
/** ==========================================================================
|
||||||
|
* 2010 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
||||||
|
* with no warranties. This code is yours to share, use and modify with no
|
||||||
|
* strings attached and no restrictions or obligations.
|
||||||
|
*
|
||||||
|
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
||||||
|
* ============================================================================
|
||||||
|
*
|
||||||
|
* Example of a Active Object, using C++11 std::thread mechanisms to make it
|
||||||
|
* safe for thread communication.
|
||||||
|
*
|
||||||
|
* This was originally published at http://sites.google.com/site/kjellhedstrom2/active-object-with-cpp0x
|
||||||
|
* and inspired from Herb Sutter's C++11 Active Object
|
||||||
|
* http://herbsutter.com/2010/07/12/effective-concurrency-prefer-using-active-objects-instead-of-naked-threads
|
||||||
|
*
|
||||||
|
* Last update 2013-12-19 by Kjell Hedstrom,
|
||||||
|
* e-mail: hedstrom at kjellkod dot cc
|
||||||
|
* linkedin: http://linkedin.com/se/kjellkod */
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <thread>
|
||||||
|
#include "g3log/shared_queue.hpp"
|
||||||
|
|
||||||
|
namespace kjellkod {
|
||||||
|
typedef std::function<void()> Callback;
|
||||||
|
|
||||||
|
class Active {
|
||||||
|
private:
|
||||||
|
Active() :
|
||||||
|
done_(false) {} // Construction ONLY through factory createActive();
|
||||||
|
Active(const Active&) = delete;
|
||||||
|
Active& operator=(const Active&) = delete;
|
||||||
|
|
||||||
|
void run() {
|
||||||
|
while (!done_) {
|
||||||
|
Callback func;
|
||||||
|
mq_.wait_and_pop(func);
|
||||||
|
func();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
shared_queue<Callback> mq_;
|
||||||
|
std::thread thd_;
|
||||||
|
bool done_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual ~Active() {
|
||||||
|
send([this]() noexcept { done_ = true; });
|
||||||
|
thd_.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
void send(Callback msg_) {
|
||||||
|
mq_.push(msg_);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Factory: safe construction of object before thread start
|
||||||
|
static std::unique_ptr<Active> createActive() {
|
||||||
|
std::unique_ptr<Active> aPtr(new Active());
|
||||||
|
aPtr->thd_ = std::thread(&Active::run, aPtr.get());
|
||||||
|
return aPtr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace kjellkod
|
||||||
47
PeakMenu/g3log/g3log/atomicbool.hpp
Normal file
47
PeakMenu/g3log/g3log/atomicbool.hpp
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
/** ==========================================================================
|
||||||
|
* 2015 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
||||||
|
* with no warranties. This code is yours to share, use and modify with no
|
||||||
|
* strings attached and no restrictions or obligations.
|
||||||
|
*
|
||||||
|
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
||||||
|
* ============================================================================*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
namespace g3 {
|
||||||
|
/// As suggested in: http://stackoverflow.com/questions/13193484/how-to-declare-a-vector-of-atomic-in-c
|
||||||
|
struct atomicbool {
|
||||||
|
private:
|
||||||
|
std::atomic<bool> value_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
atomicbool() :
|
||||||
|
value_{false} {}
|
||||||
|
atomicbool(bool value) :
|
||||||
|
value_{value} {}
|
||||||
|
atomicbool(const std::atomic<bool>& value) :
|
||||||
|
value_{value.load(std::memory_order_acquire)} {}
|
||||||
|
atomicbool(const atomicbool& other) :
|
||||||
|
value_{other.value_.load(std::memory_order_acquire)} {}
|
||||||
|
|
||||||
|
atomicbool& operator=(const atomicbool& other) {
|
||||||
|
value_.store(other.value_.load(std::memory_order_acquire), std::memory_order_release);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
atomicbool& operator=(const bool other) {
|
||||||
|
value_.store(other, std::memory_order_release);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const atomicbool& rhs) const {
|
||||||
|
return (value_.load(std::memory_order_acquire) == rhs.value_.load(std::memory_order_acquire));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool value() { return value_.load(std::memory_order_acquire); }
|
||||||
|
std::atomic<bool>& get() { return value_; }
|
||||||
|
};
|
||||||
|
} // namespace g3
|
||||||
|
// explicit whitespace/EOF for VS15
|
||||||
81
PeakMenu/g3log/g3log/crashhandler.hpp
Normal file
81
PeakMenu/g3log/g3log/crashhandler.hpp
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/** ==========================================================================
|
||||||
|
* 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
||||||
|
* with no warranties. This code is yours to share, use and modify with no
|
||||||
|
* strings attached and no restrictions or obligations.
|
||||||
|
*
|
||||||
|
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
||||||
|
* ============================================================================*/
|
||||||
|
#include <cstdio>
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
#include "g3log/loglevels.hpp"
|
||||||
|
|
||||||
|
// kjell. Separera på crashhandler.hpp och crashhanlder_internal.hpp
|
||||||
|
// implementationsfilen kan vara den samma
|
||||||
|
namespace g3 {
|
||||||
|
|
||||||
|
// PUBLIC API:
|
||||||
|
/** Install signal handler that catches FATAL C-runtime or OS signals
|
||||||
|
See the wikipedia site for details http://en.wikipedia.org/wiki/SIGFPE
|
||||||
|
See the this site for example usage: http://www.tutorialspoint.com/cplusplus/cpp_signal_handling
|
||||||
|
SIGABRT ABORT (ANSI), abnormal termination
|
||||||
|
SIGFPE Floating point exception (ANSI)
|
||||||
|
SIGILL ILlegal instruction (ANSI)
|
||||||
|
SIGSEGV Segmentation violation i.e. illegal memory reference
|
||||||
|
SIGTERM TERMINATION (ANSI) */
|
||||||
|
void installCrashHandler();
|
||||||
|
|
||||||
|
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
|
||||||
|
typedef unsigned long SignalType;
|
||||||
|
/// SIGFPE, SIGILL, and SIGSEGV handling must be installed per thread
|
||||||
|
/// on Windows. This is automatically done if you do at least one LOG(...) call
|
||||||
|
/// you can also use this function call, per thread so make sure these three
|
||||||
|
/// fatal signals are covered in your thread (even if you don't do a LOG(...) call
|
||||||
|
void installSignalHandlerForThread();
|
||||||
|
#else
|
||||||
|
typedef int SignalType;
|
||||||
|
std::string signalToStr(int signal_number);
|
||||||
|
|
||||||
|
// restore to whatever signal handler was used before signal handler installation
|
||||||
|
void restoreSignalHandler(int signal_number);
|
||||||
|
|
||||||
|
/// Overrides the existing signal handling for custom signals
|
||||||
|
/// For example: usage of zcmq relies on its own signal handler for SIGTERM
|
||||||
|
/// so users of g3log with zcmq should then use the @ref overrideSetupSignals
|
||||||
|
/// , likely with the original set of signals but with SIGTERM removed
|
||||||
|
///
|
||||||
|
/// call example:
|
||||||
|
/// g3::overrideSetupSignals({ {SIGABRT, "SIGABRT"}, {SIGFPE, "SIGFPE"},{SIGILL, "SIGILL"},
|
||||||
|
// {SIGSEGV, "SIGSEGV"},});
|
||||||
|
void overrideSetupSignals(const std::map<int, std::string> overrideSignals);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
/// Resets the fatal signal/exception handling back to default
|
||||||
|
/// which might be needed in case it was previously overridden
|
||||||
|
/// The default signals are: SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGTERM
|
||||||
|
void restoreFatalHandlingToDefault();
|
||||||
|
|
||||||
|
/** return whether or any fatal handling is still ongoing
|
||||||
|
* this is used by g3log::fatalCallToLogger
|
||||||
|
* only in the case of Windows exceptions (not fatal signals)
|
||||||
|
* are we interested in changing this from false to true to
|
||||||
|
* help any other exceptions handler work with 'EXCEPTION_CONTINUE_SEARCH'*/
|
||||||
|
bool shouldBlockForFatalHandling();
|
||||||
|
|
||||||
|
/** \return signal_name Ref: signum.hpp and \ref installSignalHandler
|
||||||
|
* or for Windows exception name */
|
||||||
|
std::string exitReasonName(const LEVELS& level, g3::SignalType signal_number);
|
||||||
|
|
||||||
|
/** return calling thread's stackdump*/
|
||||||
|
std::string stackdump(const char* dump = nullptr);
|
||||||
|
|
||||||
|
/** Re-"throw" a fatal signal, previously caught. This will exit the application
|
||||||
|
* This is an internal only function. Do not use it elsewhere. It is triggered
|
||||||
|
* from g3log, g3LogWorker after flushing messages to file */
|
||||||
|
void exitWithDefaultSignalHandler(const LEVELS& level, g3::SignalType signal_number);
|
||||||
|
size_t writeErrorMessage(const char* message);
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace g3
|
||||||
46
PeakMenu/g3log/g3log/filesink.hpp
Normal file
46
PeakMenu/g3log/g3log/filesink.hpp
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
/** ==========================================================================
|
||||||
|
* 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
||||||
|
* with no warranties. This code is yours to share, use and modify with no
|
||||||
|
* strings attached and no restrictions or obligations.
|
||||||
|
*
|
||||||
|
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
||||||
|
* ============================================================================*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "g3log/logmessage.hpp"
|
||||||
|
namespace g3 {
|
||||||
|
|
||||||
|
class FileSink {
|
||||||
|
public:
|
||||||
|
FileSink(const std::string& log_prefix, const std::string& log_directory, const std::string& logger_id = "g3log", size_t write_to_log_every_x_message = 100);
|
||||||
|
virtual ~FileSink();
|
||||||
|
|
||||||
|
void fileWrite(LogMessageMover message);
|
||||||
|
std::string changeLogFile(const std::string& directory, const std::string& logger_id);
|
||||||
|
std::string fileName();
|
||||||
|
void overrideLogDetails(LogMessage::LogDetailsFunc func);
|
||||||
|
void overrideLogHeader(const std::string& change);
|
||||||
|
|
||||||
|
private:
|
||||||
|
LogMessage::LogDetailsFunc _log_details_func;
|
||||||
|
std::string _log_file_with_path;
|
||||||
|
std::string _log_prefix_backup; // needed in case of future log file changes of directory
|
||||||
|
std::unique_ptr<std::ofstream> _outptr;
|
||||||
|
std::string _header;
|
||||||
|
bool _firstEntry;
|
||||||
|
std::string _write_buffer;
|
||||||
|
size_t _write_counter;
|
||||||
|
size_t _write_to_log_every_x_message;
|
||||||
|
|
||||||
|
void addLogFileHeader();
|
||||||
|
std::ofstream& filestream() {
|
||||||
|
return *(_outptr.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
FileSink& operator=(const FileSink&) = delete;
|
||||||
|
FileSink(const FileSink& other) = delete;
|
||||||
|
};
|
||||||
|
} // namespace g3
|
||||||
60
PeakMenu/g3log/g3log/future.hpp
Normal file
60
PeakMenu/g3log/g3log/future.hpp
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
#pragma once
|
||||||
|
/** ==========================================================================
|
||||||
|
* 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
||||||
|
* with no warranties. This code is yours to share, use and modify with no
|
||||||
|
* strings attached and no restrictions or obligations.
|
||||||
|
*
|
||||||
|
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
||||||
|
* ============================================================================
|
||||||
|
* Filename:g3future.hpp
|
||||||
|
* Helper functionality to put packaged_tasks in standard container. This
|
||||||
|
* is especially helpful for background thread processing a la async but through
|
||||||
|
* an actor pattern (active object), thread pool or similar.
|
||||||
|
* Created: 2012 by Kjell Hedström
|
||||||
|
*
|
||||||
|
* COMMUNITY THANKS:
|
||||||
|
* The code below is in large thanks to exemplifying code snippets from StackOverflow
|
||||||
|
* question/answer: http://stackoverflow.com/questions/6230893/developing-c-concurrency-library-with-futures-or-similar-paradigm
|
||||||
|
* and a discussion between Lars Gullik Bjønnes and Jonathan Wakely's at: http://gcc.gnu.org/ml/gcc-help/2011-11/msg00052.html
|
||||||
|
*
|
||||||
|
* Both are highly recommended reads if you are interested in c++ concurrency library
|
||||||
|
* - Kjell, 2012
|
||||||
|
*
|
||||||
|
* PUBLIC DOMAIN and NOT under copywrite protection.
|
||||||
|
* ********************************************* */
|
||||||
|
|
||||||
|
#include <future>
|
||||||
|
#include "g3log/active.hpp"
|
||||||
|
#include "g3log/moveoncopy.hpp"
|
||||||
|
#include "g3log/stlpatch_future.hpp"
|
||||||
|
|
||||||
|
namespace g3 {
|
||||||
|
// Generic helper function to avoid repeating the steps for managing
|
||||||
|
// asynchronous task job (by active object) that returns a future results
|
||||||
|
// could of course be made even more generic if done more in the way of
|
||||||
|
// std::async, ref: http://en.cppreference.com/w/cpp/thread/async
|
||||||
|
//
|
||||||
|
// Example usage:
|
||||||
|
// std::unique_ptr<Active> bgWorker{Active::createActive()};
|
||||||
|
// ...
|
||||||
|
// auto msg_call=[=](){return ("Hello from the Background");};
|
||||||
|
// auto future_msg = g3::spawn_task(msg_lambda, bgWorker.get());
|
||||||
|
template <typename Func, class BgWorker>
|
||||||
|
std::future<std::invoke_result_t<Func>> spawn_task(Func func, BgWorker* worker) {
|
||||||
|
typedef std::invoke_result_t<Func> result_type;
|
||||||
|
typedef std::packaged_task<result_type()> task_type;
|
||||||
|
|
||||||
|
if (nullptr == worker) {
|
||||||
|
auto p = std::make_shared<std::promise<result_type>>();
|
||||||
|
std::future<result_type> future_result = p->get_future();
|
||||||
|
p->set_exception(std::make_exception_ptr(std::runtime_error("nullptr instantiated worker")));
|
||||||
|
return future_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
task_type task(std::move(func));
|
||||||
|
|
||||||
|
std::future<result_type> result = task.get_future();
|
||||||
|
worker->send(MoveOnCopy<task_type>(std::move(task)));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
} // end namespace g3
|
||||||
226
PeakMenu/g3log/g3log/g3log.hpp
Normal file
226
PeakMenu/g3log/g3log/g3log.hpp
Normal file
@@ -0,0 +1,226 @@
|
|||||||
|
/** ==========================================================================
|
||||||
|
* 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
||||||
|
* with no warranties. This code is yours to share, use and modify with no
|
||||||
|
* strings attached and no restrictions or obligations.
|
||||||
|
*
|
||||||
|
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
||||||
|
* ============================================================================
|
||||||
|
*
|
||||||
|
* Filename:g3log.hpp Framework for Logging and Design By Contract
|
||||||
|
* Created: 2011 by Kjell Hedström
|
||||||
|
*
|
||||||
|
* PUBLIC DOMAIN and Not copywrited since it was built on public-domain software and influenced
|
||||||
|
* at least in "spirit" from the following sources
|
||||||
|
* 1. kjellkod.cc ;)
|
||||||
|
* 2. Dr.Dobbs, Petru Marginean: http://drdobbs.com/article/printableArticle.jhtml?articleId=201804215&dept_url=/caddpp/
|
||||||
|
* 3. Dr.Dobbs, Michael Schulze: http://drdobbs.com/article/printableArticle.jhtml?articleId=225700666&dept_url=/cpp/
|
||||||
|
* 4. Google 'glog': http://google-glog.googlecode.com/svn/trunk/doc/glog.html
|
||||||
|
* 5. Various Q&A at StackOverflow
|
||||||
|
* ********************************************* */
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "g3log/logcapture.hpp"
|
||||||
|
#include "g3log/loglevels.hpp"
|
||||||
|
#include "g3log/logmessage.hpp"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && (defined(WINDOWS_FUNCSIG)) // Microsoft
|
||||||
|
#define G3LOG_PRETTY_FUNCTION __FUNCSIG__
|
||||||
|
#elif defined(__GNUC__) && defined(PRETTY_FUNCTION) // GCC compatible
|
||||||
|
#define G3LOG_PRETTY_FUNCTION __PRETTY_FUNCTION__
|
||||||
|
#else
|
||||||
|
#define G3LOG_PRETTY_FUNCTION __FUNCTION__
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// thread_local doesn't exist before VS2013
|
||||||
|
// it exists on VS2015
|
||||||
|
#if !(defined(thread_local)) && defined(_MSC_VER) && _MSC_VER < 1900
|
||||||
|
#define thread_local __declspec(thread)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** namespace for LOG() and CHECK() frameworks
|
||||||
|
* History lesson: Why the names 'g3' and 'g3log'?:
|
||||||
|
* The framework was made in my own free time as PUBLIC DOMAIN but the
|
||||||
|
* first commercial project to use it used 'g3' as an internal denominator for
|
||||||
|
* the current project. g3 as in 'generation 2'. I decided to keep the g3 and g3log names
|
||||||
|
* to give credit to the people in that project (you know who you are :) and I guess also
|
||||||
|
* for 'sentimental' reasons. That a big influence was Google's glog is just a happy
|
||||||
|
* coincidence or subconscious choice. Either way g3log became the name for this logger.
|
||||||
|
*
|
||||||
|
* --- Thanks for a great 2011 and good luck with 'g3' --- KjellKod
|
||||||
|
*/
|
||||||
|
namespace g3 {
|
||||||
|
class LogWorker;
|
||||||
|
struct LogMessage;
|
||||||
|
struct FatalMessage;
|
||||||
|
|
||||||
|
/** Should be called at very first startup of the software with \ref g3LogWorker
|
||||||
|
* pointer. Ownership of the \ref g3LogWorker is the responsibility of the caller */
|
||||||
|
void initializeLogging(LogWorker* logger);
|
||||||
|
|
||||||
|
/** setFatalPreLoggingHook() provides an optional extra step before the fatalExitHandler is called
|
||||||
|
*
|
||||||
|
* Set a function-hook before a fatal message will be sent to the logger
|
||||||
|
* i.e. this is a great place to put a break point, either in your debugger
|
||||||
|
* or programmatically to catch LOG(FATAL), CHECK(...) or an OS fatal event (exception or signal)
|
||||||
|
* This will be reset to default (does nothing) at initializeLogging(...);
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
* Windows: g3::setFatalPreLoggingHook([]{__debugbreak();}); // remember #include <intrin.h>
|
||||||
|
* WARNING: '__debugbreak()' when not running in Debug in your Visual Studio IDE will likely
|
||||||
|
* trigger a recursive crash if used here. It should only be used when debugging
|
||||||
|
* in your Visual Studio IDE. Recursive crashes are handled but are unnecessary.
|
||||||
|
*
|
||||||
|
* Linux: g3::setFatalPreLoggingHook([]{ raise(SIGTRAP); });
|
||||||
|
*/
|
||||||
|
void setFatalPreLoggingHook(std::function<void(void)> pre_fatal_hook);
|
||||||
|
|
||||||
|
/** If the @ref setFatalPreLoggingHook is not enough and full fatal exit handling is needed then
|
||||||
|
* use "setFatalExithandler". Please see g3log.cpp and crashhandler_windows.cpp or crashhandler_unix for
|
||||||
|
* example of restoring signal and exception handlers, flushing the log and shutting down.
|
||||||
|
*/
|
||||||
|
void setFatalExitHandler(std::function<void(FatalMessagePtr)> fatal_call);
|
||||||
|
|
||||||
|
#ifdef G3_DYNAMIC_MAX_MESSAGE_SIZE
|
||||||
|
// only_change_at_initialization namespace is for changes to be done only during initialization. More specifically
|
||||||
|
// items here would be called prior to calling other parts of g3log
|
||||||
|
namespace only_change_at_initialization {
|
||||||
|
// Sets the MaxMessageSize to be used when capturing log messages. Currently this value is set to 2KB. Messages
|
||||||
|
// Longer than this are bound to 2KB with the string "[...truncated...]" at the end. This function allows
|
||||||
|
// this limit to be changed.
|
||||||
|
void setMaxMessageSize(size_t max_size);
|
||||||
|
} // namespace only_change_at_initialization
|
||||||
|
#endif /* G3_DYNAMIC_MAX_MESSAGE_SIZE */
|
||||||
|
|
||||||
|
// internal namespace is for completely internal or semi-hidden from the g3 namespace due to that it is unlikely
|
||||||
|
// that you will use these
|
||||||
|
namespace internal {
|
||||||
|
/// @returns true if logger is initialized
|
||||||
|
bool isLoggingInitialized();
|
||||||
|
|
||||||
|
// Save the created LogMessage to any existing sinks
|
||||||
|
void saveMessage(const char* message, const char* file, int line, const char* function, const LEVELS& level,
|
||||||
|
const char* boolean_expression, int fatal_signal, const char* stack_trace);
|
||||||
|
|
||||||
|
void saveFatalMessage(const char* stack_trace, g3::LogMessagePtr& message, int& fatal_signal);
|
||||||
|
|
||||||
|
// forwards the message to all sinks
|
||||||
|
void pushMessageToLogger(LogMessagePtr log_entry);
|
||||||
|
|
||||||
|
// forwards a FATAL message to all sinks,. after which the g3logworker
|
||||||
|
// will trigger crashhandler / g3::internal::exitWithDefaultSignalHandler
|
||||||
|
//
|
||||||
|
// By default the "fatalCall" will forward a FatalMessageptr to this function
|
||||||
|
// this behavior can be changed if you set a different fatal handler through
|
||||||
|
// "setFatalExitHandler"
|
||||||
|
void pushFatalMessageToLogger(FatalMessagePtr message);
|
||||||
|
|
||||||
|
// Saves the created FatalMessage to any existing sinks and exits with
|
||||||
|
// the originating fatal signal,. or SIGABRT if it originated from a broken contract.
|
||||||
|
// By default forwards to: pushFatalMessageToLogger, see "setFatalExitHandler" to override
|
||||||
|
//
|
||||||
|
// If you override it then you probably want to call "pushFatalMessageToLogger" after your
|
||||||
|
// custom fatal handler is done. This will make sure that the fatal message the pushed
|
||||||
|
// to sinks as well as shutting down the process
|
||||||
|
void fatalCall(FatalMessagePtr message);
|
||||||
|
|
||||||
|
// Shuts down logging. No object cleanup but further LOG(...) calls will be ignored.
|
||||||
|
void shutDownLogging();
|
||||||
|
|
||||||
|
// Shutdown logging, but ONLY if the active logger corresponds to the one currently initialized
|
||||||
|
bool shutDownLoggingForActiveOnly(LogWorker* active);
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace g3
|
||||||
|
// clang-format off
|
||||||
|
#define INTERNAL_LOG_MESSAGE(level) LogCapture(__FILE__, __LINE__, static_cast<const char*>(G3LOG_PRETTY_FUNCTION), level)
|
||||||
|
|
||||||
|
#define INTERNAL_CONTRACT_MESSAGE(boolean_expression) \
|
||||||
|
LogCapture(__FILE__, __LINE__, G3LOG_PRETTY_FUNCTION, g3::internal::CONTRACT, boolean_expression)
|
||||||
|
|
||||||
|
|
||||||
|
// LOG(level) is the API for the stream log
|
||||||
|
#define LOG(level) if (!g3::logLevel(level)) {} else INTERNAL_LOG_MESSAGE(level).stream()
|
||||||
|
|
||||||
|
|
||||||
|
// 'Conditional' stream log
|
||||||
|
#define LOG_IF(level, boolean_expression) \
|
||||||
|
if (!g3::logLevel(level) || false == (boolean_expression)) {} else INTERNAL_LOG_MESSAGE(level).stream()
|
||||||
|
|
||||||
|
// 'Design By Contract' stream API. Broken Contracts will exit the application by using fatal signal SIGABRT
|
||||||
|
// For unit testing, you can override the fatal handling using setFatalExitHandler(...). See tes_io.cpp for examples
|
||||||
|
#define CHECK(boolean_expression) \
|
||||||
|
if (true == (boolean_expression)) {} else INTERNAL_CONTRACT_MESSAGE(#boolean_expression).stream()
|
||||||
|
|
||||||
|
|
||||||
|
/** For details please see this
|
||||||
|
* REFERENCE: http://www.cppreference.com/wiki/io/c/printf_format
|
||||||
|
* \verbatim
|
||||||
|
*
|
||||||
|
There are different %-codes for different variable types, as well as options to
|
||||||
|
limit the length of the variables and whatnot.
|
||||||
|
Code Format
|
||||||
|
%[flags][width][.precision][length]specifier
|
||||||
|
SPECIFIERS
|
||||||
|
----------
|
||||||
|
%c character
|
||||||
|
%d signed integers
|
||||||
|
%i signed integers
|
||||||
|
%e scientific notation, with a lowercase “e”
|
||||||
|
%E scientific notation, with a uppercase “E”
|
||||||
|
%f floating point
|
||||||
|
%g use %e or %f, whichever is shorter
|
||||||
|
%G use %E or %f, whichever is shorter
|
||||||
|
%o octal
|
||||||
|
%s a string of characters
|
||||||
|
%u unsigned integer
|
||||||
|
%x unsigned hexadecimal, with lowercase letters
|
||||||
|
%X unsigned hexadecimal, with uppercase letters
|
||||||
|
%p a pointer
|
||||||
|
%n the argument shall be a pointer to an integer into which is placed the number of characters written so far
|
||||||
|
|
||||||
|
For flags, width, precision etc please see the above references.
|
||||||
|
EXAMPLES:
|
||||||
|
{
|
||||||
|
LOGF(INFO, "Characters: %c %c \n", 'a', 65);
|
||||||
|
LOGF(INFO, "Decimals: %d %ld\n", 1977, 650000L); // printing long
|
||||||
|
LOGF(INFO, "Preceding with blanks: %10d \n", 1977);
|
||||||
|
LOGF(INFO, "Preceding with zeros: %010d \n", 1977);
|
||||||
|
LOGF(INFO, "Some different radixes: %d %x %o %#x %#o \n", 100, 100, 100, 100, 100);
|
||||||
|
LOGF(INFO, "floats: %4.2f %+.0e %E \n", 3.1416, 3.1416, 3.1416);
|
||||||
|
LOGF(INFO, "Width trick: %*d \n", 5, 10);
|
||||||
|
LOGF(INFO, "%s \n", "A string");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
And here is possible output
|
||||||
|
: Characters: a A
|
||||||
|
: Decimals: 1977 650000
|
||||||
|
: Preceding with blanks: 1977
|
||||||
|
: Preceding with zeros: 0000001977
|
||||||
|
: Some different radixes: 100 64 144 0x64 0144
|
||||||
|
: floats: 3.14 +3e+000 3.141600E+000
|
||||||
|
: Width trick: 10
|
||||||
|
: A string \endverbatim */
|
||||||
|
#define LOGF(level, printf_like_message, ...) \
|
||||||
|
if (!g3::logLevel(level)) {} else INTERNAL_LOG_MESSAGE(level).capturef(printf_like_message, ##__VA_ARGS__)
|
||||||
|
|
||||||
|
// Conditional log printf syntax
|
||||||
|
#define LOGF_IF(level,boolean_expression, printf_like_message, ...) \
|
||||||
|
if (!g3::logLevel(level) || false == (boolean_expression)) {} else INTERNAL_LOG_MESSAGE(level).capturef(printf_like_message, ##__VA_ARGS__)
|
||||||
|
|
||||||
|
// Design By Contract, printf-like API syntax with variadic input parameters.
|
||||||
|
// Calls the signal handler if the contract failed with the default exit for a failed contract. This is typically SIGABRT
|
||||||
|
// See g3log, setFatalExitHandler(...) which can be overriden for unit tests (ref test_io.cpp)
|
||||||
|
#define CHECKF(boolean_expression, printf_like_message, ...) \
|
||||||
|
if (true == (boolean_expression)) {} else INTERNAL_CONTRACT_MESSAGE(#boolean_expression).capturef(printf_like_message, ##__VA_ARGS__)
|
||||||
|
|
||||||
|
// Backwards compatible. The same as CHECKF.
|
||||||
|
// Design By Contract, printf-like API syntax with variadic input parameters.
|
||||||
|
// Calls the signal handler if the contract failed. See g3log, setFatalExitHandler(...) which can be overriden for unit tests
|
||||||
|
// (ref test_io.cpp)
|
||||||
|
#define CHECK_F(boolean_expression, printf_like_message, ...) \
|
||||||
|
if (true == (boolean_expression)) {} else INTERNAL_CONTRACT_MESSAGE(#boolean_expression).capturef(printf_like_message, ##__VA_ARGS__)
|
||||||
|
// clang-format on
|
||||||
75
PeakMenu/g3log/g3log/logcapture.hpp
Normal file
75
PeakMenu/g3log/g3log/logcapture.hpp
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
/** ==========================================================================
|
||||||
|
* 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
||||||
|
* with no warranties. This code is yours to share, use and modify with no
|
||||||
|
* strings attached and no restrictions or obligations.
|
||||||
|
*
|
||||||
|
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
||||||
|
* ============================================================================*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "g3log/crashhandler.hpp"
|
||||||
|
#include "g3log/loglevels.hpp"
|
||||||
|
|
||||||
|
#include <csignal>
|
||||||
|
#include <cstdarg>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#include <sal.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple struct for capturing log/fatal entries. At destruction the captured message is
|
||||||
|
* forwarded to background worker.
|
||||||
|
* As a safety precaution: No memory allocated here will be moved into the background
|
||||||
|
* worker in case of dynamic loaded library reasons
|
||||||
|
*/
|
||||||
|
struct LogCapture {
|
||||||
|
/// Called from crash handler when a fatal signal has occurred (SIGSEGV etc)
|
||||||
|
LogCapture(const LEVELS& level, g3::SignalType fatal_signal, const char* dump = nullptr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file, line, function are given in g3log.hpp from macros
|
||||||
|
* @level INFO/DEBUG/WARNING/FATAL
|
||||||
|
* @expression for CHECK calls
|
||||||
|
* @fatal_signal for failed CHECK:SIGABRT or fatal signal caught in the signal handler
|
||||||
|
*/
|
||||||
|
LogCapture(const char* file, const int line, const char* function, const LEVELS& level, const char* expression = "", g3::SignalType fatal_signal = SIGABRT, const char* dump = nullptr);
|
||||||
|
|
||||||
|
// At destruction the message will be forwarded to the g3log worker.
|
||||||
|
// In the case of dynamically (at runtime) loaded libraries, the important thing to know is that
|
||||||
|
// all strings are copied, so the original are not destroyed at the receiving end, only the copy
|
||||||
|
virtual ~LogCapture() noexcept(false);
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#if _MSC_VER >= 1400
|
||||||
|
#define G3LOG_FORMAT_STRING _Printf_format_string_
|
||||||
|
#else
|
||||||
|
#define G3LOG_FORMAT_STRING __format_string
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void capturef(G3LOG_FORMAT_STRING const char* printf_like_message, ...);
|
||||||
|
#else
|
||||||
|
#define G3LOG_FORMAT_STRING
|
||||||
|
|
||||||
|
// Use "-Wall" to generate warnings in case of illegal printf format.
|
||||||
|
// Ref: http://www.unixwiz.net/techtips/gnu-c-attributes.html
|
||||||
|
[[gnu::format(printf, 2, 3)]] void capturef(G3LOG_FORMAT_STRING const char* printf_like_message, ...); // 2,3 ref: http://www.codemaestro.com/reviews/18
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// prettifying API for this completely open struct
|
||||||
|
std::ostringstream& stream() {
|
||||||
|
return _stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostringstream _stream;
|
||||||
|
std::string _stack_trace;
|
||||||
|
const char* _file;
|
||||||
|
const int _line;
|
||||||
|
const char* _function;
|
||||||
|
const LEVELS& _level;
|
||||||
|
const char* _expression;
|
||||||
|
const g3::SignalType _fatal_signal;
|
||||||
|
};
|
||||||
|
//} // g3
|
||||||
194
PeakMenu/g3log/g3log/loglevels.hpp
Normal file
194
PeakMenu/g3log/g3log/loglevels.hpp
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
/** ==========================================================================
|
||||||
|
* 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
||||||
|
* with no warranties. This code is yours to share, use and modify with no
|
||||||
|
* strings attached and no restrictions or obligations.
|
||||||
|
*
|
||||||
|
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
||||||
|
* ============================================================================*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Users of Juce or other libraries might have a define DEBUG which clashes with
|
||||||
|
// the DEBUG logging level for G3log. In that case they can instead use the define
|
||||||
|
// "CHANGE_G3LOG_DEBUG_TO_DBUG" and G3log's logging level DEBUG is changed to be DBUG
|
||||||
|
#if (defined(CHANGE_G3LOG_DEBUG_TO_DBUG))
|
||||||
|
#if (defined(DBUG))
|
||||||
|
#error "DBUG is already defined elsewhere which clashes with G3Log's log level DBUG"
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#if (defined(DEBUG))
|
||||||
|
#error "DEBUG is already defined elsewhere which clashes with G3Log's log level DEBUG"
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <atomic>
|
||||||
|
#include <g3log/atomicbool.hpp>
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
// Levels for logging, made so that it would be easy to change, remove, add levels -- KjellKod
|
||||||
|
struct LEVELS {
|
||||||
|
// force internal copy of the const char*. This is a simple safeguard for when g3log is used in a
|
||||||
|
// "dynamic, runtime loading of shared libraries"
|
||||||
|
|
||||||
|
LEVELS(const LEVELS& other) :
|
||||||
|
value(other.value),
|
||||||
|
text(other.text.c_str()) {}
|
||||||
|
|
||||||
|
LEVELS(int id, const std::string& idtext) :
|
||||||
|
value(id),
|
||||||
|
text(idtext) {}
|
||||||
|
|
||||||
|
bool operator==(const LEVELS& rhs) const {
|
||||||
|
return (value == rhs.value && text == rhs.text);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const LEVELS& rhs) const {
|
||||||
|
return (value != rhs.value || text != rhs.text);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend void swap(LEVELS& first, LEVELS& second) {
|
||||||
|
using std::swap;
|
||||||
|
swap(first.value, second.value);
|
||||||
|
swap(first.text, second.text);
|
||||||
|
}
|
||||||
|
|
||||||
|
LEVELS& operator=(LEVELS other) {
|
||||||
|
swap(*this, other);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
int value;
|
||||||
|
std::string text;
|
||||||
|
};
|
||||||
|
|
||||||
|
// If you want to add any extra logging level then please add to your own source file the logging level you need
|
||||||
|
// 1. If the cmake option G3_DYNAMIC_LOGGING is enabled then you must use g3::only_change_at_initialization::addLogLevel(...).
|
||||||
|
// to give g3log a record of your logging level and if it is an enabled or disbled logging level.
|
||||||
|
//
|
||||||
|
// 2. If the cmake dynamic logging option is turned OFF
|
||||||
|
// then giving g3log a record of your logging level with 'addLogLevel(...) is NOT needed since no "disbled/enabled"
|
||||||
|
// check will happen - all logging levels will be considered enabled.
|
||||||
|
// 3. See also the [g3log/API.markdown](https://github.com/KjellKod/g3log/blob/master/API.markdown) for for information.
|
||||||
|
//
|
||||||
|
// example: MyLoggingLevel.h
|
||||||
|
// #pragma once
|
||||||
|
// const LEVELS MYINFO {WARNING.value +1, "MyInfoLevel"};
|
||||||
|
// const LEVELS MYFATAL {FATAL.value +1, "MyFatalLevel"};
|
||||||
|
//
|
||||||
|
// ... somewhere else when G3_DYNAMIC_LOGGING is enabled
|
||||||
|
// addLogLevel(MYINFO, true);
|
||||||
|
// LOG(MYINFO) << "some text";
|
||||||
|
//
|
||||||
|
// ... another example, when G3_DYNAMIC_LOGGING is enabled
|
||||||
|
// 'addLogLevel' is NOT required
|
||||||
|
// LOG(MYFATAL) << "this will just work, and it will be counted as a FATAL event";
|
||||||
|
namespace g3 {
|
||||||
|
static const int kDebugValue = 100;
|
||||||
|
static const int kInfoValue = 300;
|
||||||
|
static const int kWarningValue = 500;
|
||||||
|
static const int kFatalValue = 1000;
|
||||||
|
static const int kInternalFatalValue = 2000;
|
||||||
|
} // namespace g3
|
||||||
|
|
||||||
|
const LEVELS G3LOG_DEBUG{g3::kDebugValue, "DEBUG"},
|
||||||
|
INFO{g3::kInfoValue, "INFO"},
|
||||||
|
WARNING{g3::kWarningValue, "WARNING"},
|
||||||
|
FATAL{g3::kFatalValue, "FATAL"};
|
||||||
|
|
||||||
|
namespace g3 {
|
||||||
|
// Logging level and atomic status collection struct
|
||||||
|
struct LoggingLevel {
|
||||||
|
atomicbool status;
|
||||||
|
LEVELS level;
|
||||||
|
|
||||||
|
// default operator needed for std::map compliance
|
||||||
|
LoggingLevel() :
|
||||||
|
status(false),
|
||||||
|
level(INFO){};
|
||||||
|
LoggingLevel(const LoggingLevel& lvl) :
|
||||||
|
status(lvl.status),
|
||||||
|
level(lvl.level) {}
|
||||||
|
LoggingLevel(const LEVELS& lvl) :
|
||||||
|
status(true),
|
||||||
|
level(lvl){};
|
||||||
|
LoggingLevel(const LEVELS& lvl, bool enabled) :
|
||||||
|
status(enabled),
|
||||||
|
level(lvl){};
|
||||||
|
~LoggingLevel() = default;
|
||||||
|
|
||||||
|
LoggingLevel& operator=(const LoggingLevel& other) {
|
||||||
|
status = other.status;
|
||||||
|
level = other.level;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const LoggingLevel& rhs) const {
|
||||||
|
return (status == rhs.status && level == rhs.level);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace g3
|
||||||
|
|
||||||
|
namespace g3 {
|
||||||
|
namespace internal {
|
||||||
|
const LEVELS CONTRACT{g3::kInternalFatalValue, {"CONTRACT"}},
|
||||||
|
FATAL_SIGNAL{g3::kInternalFatalValue + 1, {"FATAL_SIGNAL"}},
|
||||||
|
FATAL_EXCEPTION{kInternalFatalValue + 2, {"FATAL_EXCEPTION"}};
|
||||||
|
|
||||||
|
/// helper function to tell the logger if a log message was fatal. If it is it will force
|
||||||
|
/// a shutdown after all log entries are saved to the sinks
|
||||||
|
bool wasFatal(const LEVELS& level);
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
#ifdef G3_DYNAMIC_LOGGING
|
||||||
|
// Only safe if done at initialization in a single-thread context
|
||||||
|
namespace only_change_at_initialization {
|
||||||
|
|
||||||
|
/// add a custom level - enabled or disabled
|
||||||
|
void addLogLevel(LEVELS level, bool enabled);
|
||||||
|
|
||||||
|
/// add a custom level - enabled
|
||||||
|
void addLogLevel(LEVELS level);
|
||||||
|
|
||||||
|
/// reset all default logging levels to enabled
|
||||||
|
/// remove any added logging levels so that the only ones left are
|
||||||
|
/// {DEBUG,INFO,WARNING,FATAL}
|
||||||
|
void reset();
|
||||||
|
} // namespace only_change_at_initialization
|
||||||
|
|
||||||
|
namespace log_levels {
|
||||||
|
/// Enable log level >= log_level.
|
||||||
|
/// log levels below will be disabled
|
||||||
|
/// log levels equal or higher will be enabled.
|
||||||
|
void setHighest(LEVELS level);
|
||||||
|
|
||||||
|
void set(LEVELS level, bool enabled);
|
||||||
|
void disable(LEVELS level);
|
||||||
|
void enable(LEVELS level);
|
||||||
|
|
||||||
|
/// WARNING: This will also disable FATAL events from being logged
|
||||||
|
void disableAll();
|
||||||
|
void enableAll();
|
||||||
|
|
||||||
|
/// print all levels with their disabled or enabled status
|
||||||
|
std::string to_string(std::map<int, g3::LoggingLevel> levelsToPrint);
|
||||||
|
|
||||||
|
/// print snapshot of system levels with their
|
||||||
|
/// disabled or enabled status
|
||||||
|
std::string to_string();
|
||||||
|
|
||||||
|
/// Snapshot view of the current logging levels' status
|
||||||
|
std::map<int, g3::LoggingLevel> getAll();
|
||||||
|
|
||||||
|
enum class status { Absent,
|
||||||
|
Enabled,
|
||||||
|
Disabled };
|
||||||
|
status getStatus(LEVELS level);
|
||||||
|
} // namespace log_levels
|
||||||
|
|
||||||
|
#endif
|
||||||
|
/// Enabled status for the given logging level
|
||||||
|
bool logLevel(const LEVELS& level);
|
||||||
|
|
||||||
|
} // namespace g3
|
||||||
148
PeakMenu/g3log/g3log/logmessage.hpp
Normal file
148
PeakMenu/g3log/g3log/logmessage.hpp
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
/** ==========================================================================
|
||||||
|
* 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
||||||
|
* with no warranties. This code is yours to share, use and modify with no
|
||||||
|
* strings attached and no restrictions or obligations.
|
||||||
|
*
|
||||||
|
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
||||||
|
* ============================================================================*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "g3log/crashhandler.hpp"
|
||||||
|
#include "g3log/loglevels.hpp"
|
||||||
|
#include "g3log/moveoncopy.hpp"
|
||||||
|
#include "g3log/time.hpp"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
namespace g3 {
|
||||||
|
|
||||||
|
/** LogMessage contains all the data collected from the LOG(...) call.
|
||||||
|
* If the sink receives a std::string it will be the std::string toString()... function
|
||||||
|
* that will format the data into a string
|
||||||
|
*
|
||||||
|
* For sinks that receive a LogMessage they can either use the toString() function, or use
|
||||||
|
* the helper functions or even the public raw data to format the saved log message any
|
||||||
|
* desired way.
|
||||||
|
*/
|
||||||
|
struct LogMessage {
|
||||||
|
std::string file_path() const {
|
||||||
|
return _file_path;
|
||||||
|
}
|
||||||
|
std::string file() const {
|
||||||
|
return _file;
|
||||||
|
}
|
||||||
|
std::string line() const {
|
||||||
|
return std::to_string(_line);
|
||||||
|
}
|
||||||
|
std::string function() const {
|
||||||
|
return _function;
|
||||||
|
}
|
||||||
|
std::string level() const {
|
||||||
|
return _level.text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// use a different format string to get a different look on the time.
|
||||||
|
// default look is Y/M/D H:M:S
|
||||||
|
std::string timestamp(const std::string& time_format = {internal::date_formatted + " " + internal::time_formatted}) const;
|
||||||
|
|
||||||
|
std::string message() const {
|
||||||
|
return _message;
|
||||||
|
}
|
||||||
|
std::string& write() const {
|
||||||
|
return _message;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string expression() const {
|
||||||
|
return _expression;
|
||||||
|
}
|
||||||
|
bool wasFatal() const {
|
||||||
|
return internal::wasFatal(_level);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string threadID() const;
|
||||||
|
|
||||||
|
void setExpression(std::string expression) {
|
||||||
|
_expression = std::move(expression);
|
||||||
|
}
|
||||||
|
|
||||||
|
LogMessage& operator=(LogMessage other);
|
||||||
|
|
||||||
|
LogMessage(std::string file, const int line, std::string function, const LEVELS level);
|
||||||
|
|
||||||
|
explicit LogMessage(const std::string& fatalOsSignalCrashMessage);
|
||||||
|
LogMessage(const LogMessage& other);
|
||||||
|
LogMessage(LogMessage&& other);
|
||||||
|
virtual ~LogMessage() {}
|
||||||
|
|
||||||
|
// helper log printing functions used by "toString()"
|
||||||
|
static std::string splitFileName(const std::string& str);
|
||||||
|
static std::string fatalSignalToString(const LogMessage& msg);
|
||||||
|
// windows only: fatalExceptionToString
|
||||||
|
static std::string fatalExceptionToString(const LogMessage& msg);
|
||||||
|
static std::string fatalLogToString(const LogMessage& msg);
|
||||||
|
static std::string fatalCheckToString(const LogMessage& msg);
|
||||||
|
static std::string normalToString(const LogMessage& msg);
|
||||||
|
|
||||||
|
// the default formatting option
|
||||||
|
static std::string DefaultLogDetailsToString(const LogMessage& msg);
|
||||||
|
|
||||||
|
// this function can be used by the logging sink to add thread ID
|
||||||
|
// see this concept and it is easy to make your own custom formatting
|
||||||
|
static std::string FullLogDetailsToString(const LogMessage& msg);
|
||||||
|
|
||||||
|
using LogDetailsFunc = std::string (*)(const LogMessage&);
|
||||||
|
std::string toString(LogDetailsFunc formattingFunc = DefaultLogDetailsToString) const;
|
||||||
|
|
||||||
|
void overrideLogDetailsFunc(LogDetailsFunc func) const;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Complete access to the raw data in case the helper functions above
|
||||||
|
// are not enough.
|
||||||
|
//
|
||||||
|
mutable LogDetailsFunc _logDetailsToStringFunc;
|
||||||
|
g3::high_resolution_time_point _timestamp;
|
||||||
|
std::thread::id _call_thread_id;
|
||||||
|
std::string _file;
|
||||||
|
std::string _file_path;
|
||||||
|
int _line;
|
||||||
|
std::string _function;
|
||||||
|
LEVELS _level;
|
||||||
|
std::string _expression; // only with content for CHECK(...) calls
|
||||||
|
mutable std::string _message;
|
||||||
|
|
||||||
|
friend void swap(LogMessage& first, LogMessage& second) {
|
||||||
|
using std::swap;
|
||||||
|
swap(first._timestamp, second._timestamp);
|
||||||
|
swap(first._call_thread_id, second._call_thread_id);
|
||||||
|
swap(first._file, second._file);
|
||||||
|
swap(first._line, second._line);
|
||||||
|
swap(first._function, second._function);
|
||||||
|
swap(first._level, second._level);
|
||||||
|
swap(first._expression, second._expression);
|
||||||
|
swap(first._message, second._message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Trigger for flushing the message queue and exiting the application
|
||||||
|
* A thread that causes a FatalMessage will sleep forever until the
|
||||||
|
* application has exited (after message flush) */
|
||||||
|
struct FatalMessage : public LogMessage {
|
||||||
|
FatalMessage(const LogMessage& details, g3::SignalType signal_id);
|
||||||
|
FatalMessage(const FatalMessage&);
|
||||||
|
FatalMessage& operator=(const FatalMessage&) = delete;
|
||||||
|
virtual ~FatalMessage() {}
|
||||||
|
|
||||||
|
LogMessage copyToLogMessage() const;
|
||||||
|
std::string reason() const;
|
||||||
|
|
||||||
|
const SignalType _signal_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef MoveOnCopy<std::unique_ptr<FatalMessage>> FatalMessagePtr;
|
||||||
|
typedef MoveOnCopy<std::unique_ptr<LogMessage>> LogMessagePtr;
|
||||||
|
typedef MoveOnCopy<LogMessage> LogMessageMover;
|
||||||
|
} // namespace g3
|
||||||
143
PeakMenu/g3log/g3log/logworker.hpp
Normal file
143
PeakMenu/g3log/g3log/logworker.hpp
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
#pragma once
|
||||||
|
/** ==========================================================================
|
||||||
|
* 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
||||||
|
* with no warranties. This code is yours to share, use and modify with no
|
||||||
|
* strings attached and no restrictions or obligations.
|
||||||
|
*
|
||||||
|
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
||||||
|
* ============================================================================
|
||||||
|
* Filename:g3logworker.h Framework for Logging and Design By Contract
|
||||||
|
* Created: 2011 by Kjell Hedström
|
||||||
|
*
|
||||||
|
* PUBLIC DOMAIN and Not copyrighted. First published at KjellKod.cc
|
||||||
|
* ********************************************* */
|
||||||
|
#include <memory>
|
||||||
|
#include "g3log/filesink.hpp"
|
||||||
|
#include "g3log/g3log.hpp"
|
||||||
|
#include "g3log/logmessage.hpp"
|
||||||
|
#include "g3log/sinkhandle.hpp"
|
||||||
|
#include "g3log/sinkwrapper.hpp"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace g3 {
|
||||||
|
class LogWorker;
|
||||||
|
struct LogWorkerImpl;
|
||||||
|
using FileSinkHandle = g3::SinkHandle<g3::FileSink>;
|
||||||
|
|
||||||
|
/// Background side of the LogWorker. Internal use only
|
||||||
|
struct LogWorkerImpl final {
|
||||||
|
typedef std::shared_ptr<g3::internal::SinkWrapper> SinkWrapperPtr;
|
||||||
|
std::vector<SinkWrapperPtr> _sinks;
|
||||||
|
std::unique_ptr<kjellkod::Active> _bg; // do not change declaration order. _bg must be destroyed before sinks
|
||||||
|
|
||||||
|
LogWorkerImpl();
|
||||||
|
~LogWorkerImpl() = default;
|
||||||
|
|
||||||
|
void bgSave(g3::LogMessagePtr msgPtr);
|
||||||
|
void bgFatal(FatalMessagePtr msgPtr);
|
||||||
|
|
||||||
|
LogWorkerImpl(const LogWorkerImpl&) = delete;
|
||||||
|
LogWorkerImpl& operator=(const LogWorkerImpl&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Front end of the LogWorker. API that is useful is
|
||||||
|
/// addSink( sink, default_call ) which returns a handle to the sink. See below and README for usage example
|
||||||
|
/// save( msg ) : internal use
|
||||||
|
/// fatal ( fatal_msg ) : internal use
|
||||||
|
class LogWorker final {
|
||||||
|
LogWorker() = default;
|
||||||
|
void addWrappedSink(std::shared_ptr<g3::internal::SinkWrapper> wrapper);
|
||||||
|
|
||||||
|
LogWorkerImpl _impl;
|
||||||
|
LogWorker(const LogWorker&) = delete;
|
||||||
|
LogWorker& operator=(const LogWorker&) = delete;
|
||||||
|
|
||||||
|
public:
|
||||||
|
~LogWorker();
|
||||||
|
|
||||||
|
/// Creates the LogWorker with no sinks. See example below on @ref addSink for how to use it
|
||||||
|
/// if you want to use the default file logger then see below for @ref addDefaultLogger
|
||||||
|
static std::unique_ptr<LogWorker> createLogWorker();
|
||||||
|
|
||||||
|
/**
|
||||||
|
A convenience function to add the default g3::FileSink to the log worker
|
||||||
|
@param log_prefix that you want
|
||||||
|
@param log_directory where the log is to be stored.
|
||||||
|
@return a handle for API access to the sink. See the README for example usage
|
||||||
|
|
||||||
|
@verbatim
|
||||||
|
Example:
|
||||||
|
using namespace g3;
|
||||||
|
std::unique_ptr<LogWorker> logworker {LogWorker::createLogWorker()};
|
||||||
|
auto handle = addDefaultLogger("my_test_log", "/tmp");
|
||||||
|
initializeLogging(logworker.get()); // ref. g3log.hpp
|
||||||
|
|
||||||
|
std::future<std::string> log_file_name = sinkHandle->call(&FileSink::fileName);
|
||||||
|
std::cout << "The filename is: " << log_file_name.get() << std::endl;
|
||||||
|
// something like: /tmp/my_test_log.g3log.20150819-100300.log
|
||||||
|
*/
|
||||||
|
std::unique_ptr<FileSinkHandle> addDefaultLogger(const std::string& log_prefix, const std::string& log_directory, const std::string& default_id = "g3log");
|
||||||
|
|
||||||
|
/// Adds a sink and returns the handle for access to the sink
|
||||||
|
/// @param real_sink unique_ptr ownership is passed to the log worker
|
||||||
|
/// @param call the default call that should receive either a std::string or a LogMessageMover message
|
||||||
|
/// @return handle to the sink for API access. See usage example below at @ref addDefaultLogger
|
||||||
|
template <typename T, typename DefaultLogCall>
|
||||||
|
std::unique_ptr<g3::SinkHandle<T>> addSink(std::unique_ptr<T> real_sink, DefaultLogCall call) {
|
||||||
|
using namespace g3;
|
||||||
|
using namespace g3::internal;
|
||||||
|
auto sink = std::make_shared<Sink<T>>(std::move(real_sink), call);
|
||||||
|
addWrappedSink(sink);
|
||||||
|
return std::make_unique<SinkHandle<T>>(sink);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes a sink. This is a synchronous call.
|
||||||
|
/// You are guaranteed that the sink is removed by the time the call returns
|
||||||
|
/// @param sink_handle the ownership of the sink handle is given
|
||||||
|
template <typename T>
|
||||||
|
void removeSink(std::unique_ptr<SinkHandle<T>> sink_handle) {
|
||||||
|
if (sink_handle) {
|
||||||
|
// sink_handle->sink().use_count() is 1 at this point
|
||||||
|
// i.e. this would be safe as long as no other weak_ptr to shared_ptr conversion
|
||||||
|
// was made by the client: assert(sink_handle->sink().use_count() == 0);
|
||||||
|
auto weak_ptr_sink = sink_handle->sink();
|
||||||
|
{
|
||||||
|
auto bg_removesink_call = [this, weak_ptr_sink] {
|
||||||
|
auto shared_sink = weak_ptr_sink.lock();
|
||||||
|
if (shared_sink) {
|
||||||
|
_impl._sinks.erase(std::remove(_impl._sinks.begin(), _impl._sinks.end(), shared_sink), _impl._sinks.end());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
auto token_done = g3::spawn_task(bg_removesink_call, _impl._bg.get());
|
||||||
|
token_done.wait();
|
||||||
|
}
|
||||||
|
// sink_handle->sink().use_count() is 1 at this point.
|
||||||
|
// i.e. this would be safe: assert(sink_handle->sink().use_count() == 0);
|
||||||
|
// as long as the client has not converted more instances from the weak_ptr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This will clear/remove all the sinks. If a sink shared_ptr was retrieved via the sink
|
||||||
|
/// handle then the sink will be removed internally but will live on in the client's instance
|
||||||
|
void removeAllSinks() {
|
||||||
|
auto bg_clear_sink_call = [this]() noexcept {
|
||||||
|
_impl._sinks.clear();
|
||||||
|
};
|
||||||
|
auto token_cleared = g3::spawn_task(bg_clear_sink_call, _impl._bg.get());
|
||||||
|
token_cleared.wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// internal:
|
||||||
|
/// pushes in background thread (asynchronously) input messages to log file
|
||||||
|
void save(LogMessagePtr entry);
|
||||||
|
|
||||||
|
/// internal:
|
||||||
|
// pushes a fatal message on the queue, this is the last message to be processed
|
||||||
|
/// this way it's ensured that all existing entries were flushed before 'fatal'
|
||||||
|
/// Will abort the application!
|
||||||
|
void fatal(FatalMessagePtr fatal_message);
|
||||||
|
};
|
||||||
|
} // namespace g3
|
||||||
51
PeakMenu/g3log/g3log/moveoncopy.hpp
Normal file
51
PeakMenu/g3log/g3log/moveoncopy.hpp
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
/** ==========================================================================
|
||||||
|
* 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
||||||
|
* with no warranties. This code is yours to share, use and modify with no
|
||||||
|
* strings attached and no restrictions or obligations.
|
||||||
|
*
|
||||||
|
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
||||||
|
* ============================================================================*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
namespace g3 {
|
||||||
|
|
||||||
|
// A straightforward technique to move around packaged_tasks.
|
||||||
|
// Instances of std::packaged_task are MoveConstructible and MoveAssignable, but
|
||||||
|
// not CopyConstructible or CopyAssignable. To put them in a std container they need
|
||||||
|
// to be wrapped and their internals "moved" when tried to be copied.
|
||||||
|
|
||||||
|
template <typename Moveable>
|
||||||
|
struct MoveOnCopy {
|
||||||
|
mutable Moveable _move_only;
|
||||||
|
|
||||||
|
explicit MoveOnCopy(Moveable&& m) :
|
||||||
|
_move_only(std::move(m)) {}
|
||||||
|
MoveOnCopy(MoveOnCopy const& t) :
|
||||||
|
_move_only(std::move(t._move_only)) {}
|
||||||
|
MoveOnCopy(MoveOnCopy&& t) :
|
||||||
|
_move_only(std::move(t._move_only)) {}
|
||||||
|
|
||||||
|
MoveOnCopy& operator=(MoveOnCopy const& other) {
|
||||||
|
_move_only = std::move(other._move_only);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
MoveOnCopy& operator=(MoveOnCopy&& other) {
|
||||||
|
_move_only = std::move(other._move_only);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()() {
|
||||||
|
_move_only();
|
||||||
|
}
|
||||||
|
|
||||||
|
Moveable& get() {
|
||||||
|
return _move_only;
|
||||||
|
}
|
||||||
|
|
||||||
|
Moveable release() {
|
||||||
|
return std::move(_move_only);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace g3
|
||||||
78
PeakMenu/g3log/g3log/shared_queue.hpp
Normal file
78
PeakMenu/g3log/g3log/shared_queue.hpp
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
/** ==========================================================================
|
||||||
|
* 2010 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
||||||
|
* with no warranties. This code is yours to share, use and modify with no
|
||||||
|
* strings attached and no restrictions or obligations.
|
||||||
|
*
|
||||||
|
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
||||||
|
* ============================================================================
|
||||||
|
*
|
||||||
|
* Example of a normal std::queue protected by a mutex for operations,
|
||||||
|
* making it safe for thread communication, using std::mutex from C++0x with
|
||||||
|
* the help from the std::thread library from JustSoftwareSolutions
|
||||||
|
* ref: http://www.stdthread.co.uk/doc/headers/mutex.html
|
||||||
|
*
|
||||||
|
* This example was totally inspired by Anthony Williams lock-based data structures in
|
||||||
|
* Ref: "C++ Concurrency In Action" http://www.manning.com/williams */
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <exception>
|
||||||
|
#include <mutex>
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
|
/** Multiple producer, multiple consumer thread safe queue
|
||||||
|
* Since 'return by reference' is used this queue won't throw */
|
||||||
|
template <typename T>
|
||||||
|
class shared_queue {
|
||||||
|
std::queue<T> queue_;
|
||||||
|
mutable std::mutex m_;
|
||||||
|
std::condition_variable data_cond_;
|
||||||
|
|
||||||
|
shared_queue& operator=(const shared_queue&) = delete;
|
||||||
|
shared_queue(const shared_queue& other) = delete;
|
||||||
|
|
||||||
|
public:
|
||||||
|
shared_queue() = default;
|
||||||
|
|
||||||
|
void push(T item) {
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m_);
|
||||||
|
queue_.push(std::move(item));
|
||||||
|
}
|
||||||
|
data_cond_.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \return immediately, with true if successful retrieval
|
||||||
|
bool try_and_pop(T& popped_item) {
|
||||||
|
std::lock_guard<std::mutex> lock(m_);
|
||||||
|
if (queue_.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
popped_item = std::move(queue_.front());
|
||||||
|
queue_.pop();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Try to retrieve, if no items, wait till an item is available and try again
|
||||||
|
void wait_and_pop(T& popped_item) {
|
||||||
|
std::unique_lock<std::mutex> lock(m_);
|
||||||
|
while (queue_.empty()) {
|
||||||
|
data_cond_.wait(lock);
|
||||||
|
// This 'while' loop is equal to
|
||||||
|
// data_cond_.wait(lock, [](bool result){return !queue_.empty();});
|
||||||
|
}
|
||||||
|
popped_item = std::move(queue_.front());
|
||||||
|
queue_.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty() const {
|
||||||
|
std::lock_guard<std::mutex> lock(m_);
|
||||||
|
return queue_.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned size() const {
|
||||||
|
std::lock_guard<std::mutex> lock(m_);
|
||||||
|
return queue_.size();
|
||||||
|
}
|
||||||
|
};
|
||||||
76
PeakMenu/g3log/g3log/sink.hpp
Normal file
76
PeakMenu/g3log/g3log/sink.hpp
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
/** ==========================================================================
|
||||||
|
* 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
||||||
|
* with no warranties. This code is yours to share, use and modify with no
|
||||||
|
* strings attached and no restrictions or obligations.
|
||||||
|
*
|
||||||
|
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
||||||
|
* ============================================================================*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "g3log/active.hpp"
|
||||||
|
#include "g3log/future.hpp"
|
||||||
|
#include "g3log/logmessage.hpp"
|
||||||
|
#include "g3log/sinkwrapper.hpp"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace g3 {
|
||||||
|
namespace internal {
|
||||||
|
typedef std::function<void(LogMessageMover)> AsyncMessageCall;
|
||||||
|
|
||||||
|
/// The asynchronous Sink has an active object, incoming requests for actions
|
||||||
|
// will be processed in the background by the specific object the Sink represents.
|
||||||
|
//
|
||||||
|
// The Sink will wrap either
|
||||||
|
// a Sink with Message object receiving call
|
||||||
|
// or a Sink with a LogEntry (string) receiving call
|
||||||
|
//
|
||||||
|
// The Sink can also be used through the SinkHandler to call Sink specific function calls
|
||||||
|
// Ref: send(Message) deals with incoming log entries (converted if necessary to string)
|
||||||
|
// Ref: send(Call call, Args... args) deals with calls
|
||||||
|
// to the real sink's API
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
struct Sink : public SinkWrapper {
|
||||||
|
std::unique_ptr<T> _real_sink;
|
||||||
|
std::unique_ptr<kjellkod::Active> _bg;
|
||||||
|
AsyncMessageCall _default_log_call;
|
||||||
|
|
||||||
|
template <typename DefaultLogCall>
|
||||||
|
Sink(std::unique_ptr<T> sink, DefaultLogCall call) :
|
||||||
|
SinkWrapper(),
|
||||||
|
_real_sink{std::move(sink)},
|
||||||
|
_bg(kjellkod::Active::createActive()),
|
||||||
|
_default_log_call(std::bind(call, _real_sink.get(), std::placeholders::_1)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
Sink(std::unique_ptr<T> sink, void (T::*Call)(std::string)) :
|
||||||
|
SinkWrapper(),
|
||||||
|
_real_sink{std::move(sink)},
|
||||||
|
_bg(kjellkod::Active::createActive()) {
|
||||||
|
std::function<void(std::string)> adapter = std::bind(Call, _real_sink.get(), std::placeholders::_1);
|
||||||
|
_default_log_call = [=](LogMessageMover m) {
|
||||||
|
adapter(m.get().toString());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~Sink() {
|
||||||
|
_bg.reset(); // TODO: to remove
|
||||||
|
}
|
||||||
|
|
||||||
|
void send(LogMessageMover msg) override {
|
||||||
|
_bg->send([this, msg] {
|
||||||
|
_default_log_call(msg);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Call, typename... Args>
|
||||||
|
auto async(Call call, Args&&... args) -> std::future<std::invoke_result_t<decltype(call), T, Args...>> {
|
||||||
|
return g3::spawn_task(std::bind(call, _real_sink.get(), std::forward<Args>(args)...), _bg.get());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace g3
|
||||||
59
PeakMenu/g3log/g3log/sinkhandle.hpp
Normal file
59
PeakMenu/g3log/g3log/sinkhandle.hpp
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
/** ==========================================================================
|
||||||
|
* 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
||||||
|
* with no warranties. This code is yours to share, use and modify with no
|
||||||
|
* strings attached and no restrictions or obligations.
|
||||||
|
*
|
||||||
|
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
||||||
|
* ============================================================================*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "g3log/sink.hpp"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace g3 {
|
||||||
|
|
||||||
|
// The Sinkhandle is the client's access point to the specific sink instance.
|
||||||
|
// Only through the Sinkhandle can, and should, the real sink's specific API
|
||||||
|
// be called.
|
||||||
|
//
|
||||||
|
// The real sink will be owned by g3log. If the real sink is deleted
|
||||||
|
// calls to sink's API through the SinkHandle will return an exception embedded
|
||||||
|
// in the resulting future. Ref: SinkHandle::call
|
||||||
|
template <class T>
|
||||||
|
class SinkHandle {
|
||||||
|
std::weak_ptr<internal::Sink<T>> _sink;
|
||||||
|
|
||||||
|
public:
|
||||||
|
SinkHandle(std::shared_ptr<internal::Sink<T>> sink) :
|
||||||
|
_sink(sink) {}
|
||||||
|
|
||||||
|
~SinkHandle() = default;
|
||||||
|
|
||||||
|
// Asynchronous call to the real sink. If the real sink is already deleted
|
||||||
|
// the returned future will contain a bad_weak_ptr exception instead of the
|
||||||
|
// call result.
|
||||||
|
template <typename AsyncCall, typename... Args>
|
||||||
|
auto call(AsyncCall func, Args&&... args) -> std::future<std::invoke_result_t<decltype(func), T, Args...>> {
|
||||||
|
try {
|
||||||
|
std::shared_ptr<internal::Sink<T>> sink(_sink);
|
||||||
|
return sink->async(func, std::forward<Args>(args)...);
|
||||||
|
} catch (const std::bad_weak_ptr& e) {
|
||||||
|
typedef std::invoke_result_t<decltype(func), T, Args...> PromiseType;
|
||||||
|
std::promise<PromiseType> promise;
|
||||||
|
promise.set_exception(std::make_exception_ptr(e));
|
||||||
|
return std::move(promise.get_future());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get weak_ptr access to the sink(). Make sure to check that the returned pointer is valid,
|
||||||
|
/// auto p = sink(); auto ptr = p.lock(); if (ptr) { .... }
|
||||||
|
/// ref: https://en.cppreference.com/w/cpp/memory/weak_ptr/lock
|
||||||
|
std::weak_ptr<internal::Sink<T>> sink() {
|
||||||
|
return _sink.lock();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace g3
|
||||||
21
PeakMenu/g3log/g3log/sinkwrapper.hpp
Normal file
21
PeakMenu/g3log/g3log/sinkwrapper.hpp
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
/** ==========================================================================
|
||||||
|
* 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
||||||
|
* with no warranties. This code is yours to share, use and modify with no
|
||||||
|
* strings attached and no restrictions or obligations.
|
||||||
|
*
|
||||||
|
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
||||||
|
* ============================================================================*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "g3log/logmessage.hpp"
|
||||||
|
|
||||||
|
namespace g3 {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
struct SinkWrapper {
|
||||||
|
virtual ~SinkWrapper() {}
|
||||||
|
virtual void send(LogMessageMover msg) = 0;
|
||||||
|
};
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace g3
|
||||||
41
PeakMenu/g3log/g3log/stacktrace_windows.hpp
Normal file
41
PeakMenu/g3log/g3log/stacktrace_windows.hpp
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
/** ==========================================================================
|
||||||
|
* 2014 by KjellKod.cc AND Robert Engeln.
|
||||||
|
* The stacktrace code was given as a public domain dedication by Robert Engeln
|
||||||
|
* It was originally published at: http://code-freeze.blogspot.com/2012/01/generating-stack-traces-from-c.html
|
||||||
|
* It was (here) modified for g3log purposes.
|
||||||
|
*
|
||||||
|
* This is PUBLIC DOMAIN to use at your own risk and comes
|
||||||
|
* with no warranties. This code is yours to share, use and modify with no
|
||||||
|
* strings attached and no restrictions or obligations.
|
||||||
|
*
|
||||||
|
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
||||||
|
* ============================================================================*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#if !(defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
|
||||||
|
#error "stacktrace_win.cpp used but not on a windows system"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "g3log/crashhandler.hpp"
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace stacktrace {
|
||||||
|
/// return the text description of a Windows exception code
|
||||||
|
std::string exceptionIdToText(g3::SignalType id);
|
||||||
|
|
||||||
|
/// return whether or not the exception is a known exception, i.e.
|
||||||
|
/// an exception that we should treat as a fatal event
|
||||||
|
bool isKnownException(g3::SignalType id);
|
||||||
|
|
||||||
|
/// helper function: retrieve stackdump from no excisting exception pointer
|
||||||
|
std::string stackdump();
|
||||||
|
|
||||||
|
/// helper function: retrieve stackdump, starting from an exception pointer
|
||||||
|
std::string stackdump(EXCEPTION_POINTERS* info);
|
||||||
|
|
||||||
|
/// main stackdump function. retrieve stackdump, from the given context
|
||||||
|
std::string stackdump(CONTEXT* context);
|
||||||
|
|
||||||
|
} // namespace stacktrace
|
||||||
78
PeakMenu/g3log/g3log/stlpatch_future.hpp
Normal file
78
PeakMenu/g3log/g3log/stlpatch_future.hpp
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
/** ==========================================================================
|
||||||
|
* 2013 This is PUBLIC DOMAIN to use at your own risk and comes
|
||||||
|
* with no warranties. This code is yours to share, use and modify with no
|
||||||
|
* strings attached and no restrictions or obligations.
|
||||||
|
*
|
||||||
|
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* 2013/12/28 Bugfix for Visual Studio 2013 which does not handle well
|
||||||
|
* std::packaged_task<void()>. Thanks to Michael Rasmussen (lap777)
|
||||||
|
* Ref: workarounds at http://connect.microsoft.com/VisualStudio/feedback/details/791185/std-packaged-task-t-where-t-is-void-or-a-reference-class-are-not-movable
|
||||||
|
* ============================================================================*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) && !defined(__MINGW32__) && (_MSC_VER <= 1800)
|
||||||
|
namespace std {
|
||||||
|
|
||||||
|
template <class... _ArgTypes>
|
||||||
|
class packaged_task<void(_ArgTypes...)> {
|
||||||
|
promise<void> _my_promise;
|
||||||
|
function<void(_ArgTypes...)> _my_func;
|
||||||
|
|
||||||
|
public:
|
||||||
|
packaged_task() {
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class _Fty2>
|
||||||
|
explicit packaged_task(_Fty2&& _Fnarg) :
|
||||||
|
_my_func(_Fnarg) {
|
||||||
|
}
|
||||||
|
|
||||||
|
packaged_task(packaged_task&& _Other) :
|
||||||
|
_my_promise(move(_Other._my_promise)),
|
||||||
|
_my_func(move(_Other._my_func)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
packaged_task& operator=(packaged_task&& _Other) {
|
||||||
|
_my_promise = move(_Other._my_promise);
|
||||||
|
_my_func = move(_Other._my_func);
|
||||||
|
return (*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
packaged_task(const packaged_task&) = delete;
|
||||||
|
packaged_task& operator=(const packaged_task&) = delete;
|
||||||
|
|
||||||
|
~packaged_task() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void swap(packaged_task& _Other) {
|
||||||
|
swap(_my_promise, _Other._my_promise);
|
||||||
|
swap(_my_func, _Other._my_func);
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit operator bool() const {
|
||||||
|
return _my_func != false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool valid() const {
|
||||||
|
return _my_func != false;
|
||||||
|
}
|
||||||
|
|
||||||
|
future<void> get_future() {
|
||||||
|
return _my_promise.get_future();
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(_ArgTypes... _Args) {
|
||||||
|
_my_func(forward<_ArgTypes>(_Args)...);
|
||||||
|
_my_promise.set_value();
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
_my_promise.swap(promise<void>());
|
||||||
|
_my_func.swap(function<void(_ArgTypes...)>());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}; // namespace std
|
||||||
|
#endif // defined(WIN32) ...
|
||||||
78
PeakMenu/g3log/g3log/time.hpp
Normal file
78
PeakMenu/g3log/g3log/time.hpp
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
#pragma once
|
||||||
|
/** ==========================================================================
|
||||||
|
* 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
||||||
|
* with no warranties. This code is yours to share, use and modify with no
|
||||||
|
* strings attached and no restrictions or obligations.
|
||||||
|
*
|
||||||
|
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
||||||
|
* ============================================================================
|
||||||
|
* Filename:g3time.h cross-platform, thread-safe replacement for C++11 non-thread-safe
|
||||||
|
* localtime (and similar)
|
||||||
|
* Created: 2012 by Kjell Hedström
|
||||||
|
*
|
||||||
|
* PUBLIC DOMAIN and Not under copywrite protection. First published for g3log at KjellKod.cc
|
||||||
|
* ********************************************* */
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <ctime>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
// FYI:
|
||||||
|
// namespace g3::internal ONLY in g3time.cpp
|
||||||
|
// std::string put_time(const struct tm* tmb, const char* c_time_format)
|
||||||
|
|
||||||
|
namespace g3 {
|
||||||
|
typedef std::chrono::time_point<std::chrono::system_clock> system_time_point;
|
||||||
|
typedef std::chrono::time_point<std::chrono::high_resolution_clock> high_resolution_time_point;
|
||||||
|
typedef std::chrono::milliseconds milliseconds;
|
||||||
|
typedef std::chrono::microseconds microseconds;
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
enum class Fractional { Millisecond,
|
||||||
|
Microsecond,
|
||||||
|
Nanosecond,
|
||||||
|
NanosecondDefault };
|
||||||
|
Fractional getFractional(const std::string& format_buffer, size_t pos);
|
||||||
|
std::string to_string(const g3::system_time_point& ts, Fractional fractional);
|
||||||
|
std::string localtime_formatted_fractions(const g3::system_time_point& ts, std::string format_buffer);
|
||||||
|
static const std::string date_formatted = "%Y/%m/%d";
|
||||||
|
// %f: fractions of seconds (%f is nanoseconds)
|
||||||
|
// %f3: milliseconds, 3 digits: 001
|
||||||
|
// %6: microseconds: 6 digits: 000001 --- default for the time_format
|
||||||
|
// %f9, %f: nanoseconds, 9 digits: 000000001
|
||||||
|
static const std::string time_formatted = "%H:%M:%S %f6";
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
// This mimics the original "std::put_time(const std::tm* tmb, const charT* fmt)"
|
||||||
|
// This is needed since latest version (at time of writing) of gcc4.7 does not implement this library function yet.
|
||||||
|
// return value is SIMPLIFIED to only return a std::string
|
||||||
|
std::string put_time(const struct tm* tmb, const char* c_time_format);
|
||||||
|
|
||||||
|
/** return time representing POD struct (ref ctime + wchar) that is normally
|
||||||
|
* retrieved with std::localtime. g3::localtime is threadsafe which std::localtime is not.
|
||||||
|
* g3::localtime is probably used together with @ref g3::systemtime_now */
|
||||||
|
tm localtime(std::time_t time);
|
||||||
|
|
||||||
|
/** format string must conform to std::put_time's demands.
|
||||||
|
* WARNING: At time of writing there is only so-so compiler support for
|
||||||
|
* std::put_time. A possible fix if your c++11 library is not updated is to
|
||||||
|
* modify this to use std::strftime instead */
|
||||||
|
std::string localtime_formatted(const system_time_point& ts, const std::string& time_format);
|
||||||
|
|
||||||
|
inline system_time_point to_system_time(const high_resolution_time_point& ts) {
|
||||||
|
// On some (windows) systems, the system_clock does not provide the highest possible time
|
||||||
|
// resolution. Thus g3log uses high_resolution_clock for message time stamps. However,
|
||||||
|
// unlike system_clock, high_resolution_clock cannot be converted to a time and date as
|
||||||
|
// it usually measures reflects the time since power-up.
|
||||||
|
// Thus, hrs_now and sys_now are recorded once when the program starts to be able to convert
|
||||||
|
// timestamps to dime and date using to_system_time(). The precision of the absolute time is
|
||||||
|
// of course that of system_clock() with some error added due to the non-simultaneous initialization
|
||||||
|
// of the two static variables but relative times within one log will be as precise as
|
||||||
|
// high_resolution_clock.
|
||||||
|
using namespace std::chrono;
|
||||||
|
static const auto hrs_now = high_resolution_clock::now();
|
||||||
|
static const auto sys_now = system_clock::now();
|
||||||
|
|
||||||
|
return time_point_cast<system_clock::duration>(sys_now + (ts - hrs_now));
|
||||||
|
}
|
||||||
|
} // namespace g3
|
||||||
113
PeakMenu/g3log/logcapture.cpp
Normal file
113
PeakMenu/g3log/logcapture.cpp
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
/** ==========================================================================
|
||||||
|
* 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
||||||
|
* with no warranties. This code is yours to share, use and modify with no
|
||||||
|
* strings attached and no restrictions or obligations.
|
||||||
|
*
|
||||||
|
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
||||||
|
* ============================================================================*/
|
||||||
|
|
||||||
|
#include "g3log/logcapture.hpp"
|
||||||
|
#include "g3log/crashhandler.hpp"
|
||||||
|
#include "g3log/g3log.hpp"
|
||||||
|
|
||||||
|
#ifdef G3_DYNAMIC_MAX_MESSAGE_SIZE
|
||||||
|
#include <vector>
|
||||||
|
#endif /* G3_DYNAMIC_MAX_MESSAGE_SIZE */
|
||||||
|
|
||||||
|
// For Windows we need force a thread_local install per thread of three
|
||||||
|
// signals that must have a signal handler installed per thread-basis
|
||||||
|
// It is really a royal pain. Seriously Microsoft? Seriously?
|
||||||
|
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
|
||||||
|
#define SIGNAL_HANDLER_VERIFY() g3::installSignalHandlerForThread()
|
||||||
|
#else
|
||||||
|
// Does nothing --- enforces that semicolon must be written
|
||||||
|
#define SIGNAL_HANDLER_VERIFY() \
|
||||||
|
do { \
|
||||||
|
} while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef G3_DYNAMIC_MAX_MESSAGE_SIZE
|
||||||
|
// MaxMessageSize is message limit used with vsnprintf/vsnprintf_s
|
||||||
|
static int MaxMessageSize = 2048;
|
||||||
|
|
||||||
|
void g3::only_change_at_initialization::setMaxMessageSize(size_t max_size) {
|
||||||
|
MaxMessageSize = max_size;
|
||||||
|
}
|
||||||
|
#endif /* G3_DYNAMIC_MAX_MESSAGE_SIZE */
|
||||||
|
|
||||||
|
/** logCapture is a simple struct for capturing log/fatal entries. At destruction the
|
||||||
|
* captured message is forwarded to background worker.
|
||||||
|
* As a safety precaution: No memory allocated here will be moved into the background
|
||||||
|
* worker in case of dynamic loaded library reasons instead the arguments are copied
|
||||||
|
* inside of g3log.cpp::saveMessage*/
|
||||||
|
LogCapture::~LogCapture() noexcept(false) {
|
||||||
|
using namespace g3::internal;
|
||||||
|
SIGNAL_HANDLER_VERIFY();
|
||||||
|
saveMessage(_stream.str().c_str(), _file, _line, _function, _level, _expression, _fatal_signal, _stack_trace.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called from crash handler when a fatal signal has occurred (SIGSEGV etc)
|
||||||
|
LogCapture::LogCapture(const LEVELS& level, g3::SignalType fatal_signal, const char* dump) :
|
||||||
|
LogCapture("", 0, "", level, "", fatal_signal, dump) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file, line, function are given in g3log.hpp from macros
|
||||||
|
* @level INFO/DEBUG/WARNING/FATAL
|
||||||
|
* @expression for CHECK calls
|
||||||
|
* @fatal_signal for failed CHECK:SIGABRT or fatal signal caught in the signal handler
|
||||||
|
*/
|
||||||
|
LogCapture::LogCapture(const char* file, const int line, const char* function, const LEVELS& level,
|
||||||
|
const char* expression, g3::SignalType fatal_signal, const char* dump) :
|
||||||
|
_file(file),
|
||||||
|
_line(line),
|
||||||
|
_function(function),
|
||||||
|
_level(level),
|
||||||
|
_expression(expression),
|
||||||
|
_fatal_signal(fatal_signal) {
|
||||||
|
|
||||||
|
if (g3::internal::wasFatal(level)) {
|
||||||
|
_stack_trace = std::string{"\n*******\tSTACKDUMP *******\n"};
|
||||||
|
_stack_trace.append(g3::internal::stackdump(dump));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* capturef, used for "printf" like API in CHECKF, LOGF, LOGF_IF
|
||||||
|
* See also for the attribute formatting ref: http://www.codemaestro.com/reviews/18
|
||||||
|
*/
|
||||||
|
void LogCapture::capturef(const char* printf_like_message, ...) {
|
||||||
|
static const std::string kTruncatedWarningText = "[...truncated...]";
|
||||||
|
#ifdef G3_DYNAMIC_MAX_MESSAGE_SIZE
|
||||||
|
std::vector<char> finished_message_backing(MaxMessageSize);
|
||||||
|
char* finished_message = finished_message_backing.data();
|
||||||
|
auto finished_message_len = MaxMessageSize;
|
||||||
|
#else
|
||||||
|
static const int kMaxMessageSize = 2048;
|
||||||
|
char finished_message[kMaxMessageSize];
|
||||||
|
#if ((defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) && !defined(__GNUC__))
|
||||||
|
auto finished_message_len = _countof(finished_message);
|
||||||
|
#else
|
||||||
|
int finished_message_len = sizeof(finished_message);
|
||||||
|
#endif
|
||||||
|
#endif /* G3_DYNAMIC_MAX_MESSAGE_SIZE*/
|
||||||
|
|
||||||
|
va_list arglist;
|
||||||
|
va_start(arglist, printf_like_message);
|
||||||
|
|
||||||
|
#if ((defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) && !defined(__GNUC__))
|
||||||
|
const int nbrcharacters = vsnprintf_s(finished_message, finished_message_len, _TRUNCATE, printf_like_message, arglist);
|
||||||
|
#else
|
||||||
|
const int nbrcharacters = vsnprintf(finished_message, finished_message_len, printf_like_message, arglist);
|
||||||
|
#endif
|
||||||
|
va_end(arglist);
|
||||||
|
|
||||||
|
if (nbrcharacters < 0) {
|
||||||
|
stream() << "\n\tERROR LOG MSG NOTIFICATION: Failure to successfully parse the message";
|
||||||
|
stream() << '"' << printf_like_message << '"' << std::endl;
|
||||||
|
} else if (nbrcharacters > finished_message_len) {
|
||||||
|
stream() << finished_message << kTruncatedWarningText;
|
||||||
|
} else {
|
||||||
|
stream() << finished_message;
|
||||||
|
}
|
||||||
|
}
|
||||||
128
PeakMenu/g3log/loglevels.cpp
Normal file
128
PeakMenu/g3log/loglevels.cpp
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
/** ==========================================================================
|
||||||
|
* 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
||||||
|
* with no warranties. This code is yours to share, use and modify with no
|
||||||
|
* strings attached and no restrictions or obligations.
|
||||||
|
*
|
||||||
|
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
||||||
|
* ============================================================================*/
|
||||||
|
|
||||||
|
#include "g3log/loglevels.hpp"
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace g3 {
|
||||||
|
namespace internal {
|
||||||
|
bool wasFatal(const LEVELS& level) {
|
||||||
|
return level.value >= FATAL.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef G3_DYNAMIC_LOGGING
|
||||||
|
const std::map<int, LoggingLevel> g_log_level_defaults = {
|
||||||
|
{G3LOG_DEBUG.value, {G3LOG_DEBUG}},
|
||||||
|
{INFO.value, {INFO}},
|
||||||
|
{WARNING.value, {WARNING}},
|
||||||
|
{FATAL.value, {FATAL}}};
|
||||||
|
|
||||||
|
std::map<int, g3::LoggingLevel> g_log_levels = g_log_level_defaults;
|
||||||
|
#endif
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
#ifdef G3_DYNAMIC_LOGGING
|
||||||
|
namespace only_change_at_initialization {
|
||||||
|
|
||||||
|
void addLogLevel(LEVELS lvl, bool enabled) {
|
||||||
|
int value = lvl.value;
|
||||||
|
internal::g_log_levels[value] = {lvl, enabled};
|
||||||
|
}
|
||||||
|
|
||||||
|
void addLogLevel(LEVELS level) {
|
||||||
|
addLogLevel(level, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
g3::internal::g_log_levels = g3::internal::g_log_level_defaults;
|
||||||
|
}
|
||||||
|
} // namespace only_change_at_initialization
|
||||||
|
|
||||||
|
namespace log_levels {
|
||||||
|
|
||||||
|
void setHighest(LEVELS enabledFrom) {
|
||||||
|
auto it = internal::g_log_levels.find(enabledFrom.value);
|
||||||
|
if (it != internal::g_log_levels.end()) {
|
||||||
|
for (auto& v : internal::g_log_levels) {
|
||||||
|
if (v.first < enabledFrom.value) {
|
||||||
|
disable(v.second.level);
|
||||||
|
} else {
|
||||||
|
enable(v.second.level);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void set(LEVELS level, bool enabled) {
|
||||||
|
auto it = internal::g_log_levels.find(level.value);
|
||||||
|
if (it != internal::g_log_levels.end()) {
|
||||||
|
internal::g_log_levels[level.value] = {level, enabled};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void disable(LEVELS level) {
|
||||||
|
set(level, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void enable(LEVELS level) {
|
||||||
|
set(level, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void disableAll() {
|
||||||
|
for (auto& v : internal::g_log_levels) {
|
||||||
|
v.second.status = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void enableAll() {
|
||||||
|
for (auto& v : internal::g_log_levels) {
|
||||||
|
v.second.status = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string to_string(std::map<int, g3::LoggingLevel> levelsToPrint) {
|
||||||
|
std::string levels;
|
||||||
|
for (auto& v : levelsToPrint) {
|
||||||
|
levels += "name: " + v.second.level.text + " level: " + std::to_string(v.first) + " status: " + std::to_string(v.second.status.value()) + "\n";
|
||||||
|
}
|
||||||
|
return levels;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string to_string() {
|
||||||
|
return to_string(internal::g_log_levels);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<int, g3::LoggingLevel> getAll() {
|
||||||
|
return internal::g_log_levels;
|
||||||
|
}
|
||||||
|
|
||||||
|
// status : {Absent, Enabled, Disabled};
|
||||||
|
status getStatus(LEVELS level) {
|
||||||
|
const auto it = internal::g_log_levels.find(level.value);
|
||||||
|
if (internal::g_log_levels.end() == it) {
|
||||||
|
return status::Absent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (it->second.status.get().load() ? status::Enabled : status::Disabled);
|
||||||
|
}
|
||||||
|
} // namespace log_levels
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool logLevel(const LEVELS& log_level) {
|
||||||
|
#ifdef G3_DYNAMIC_LOGGING
|
||||||
|
int level = log_level.value;
|
||||||
|
bool status = internal::g_log_levels[level].status.value();
|
||||||
|
return status;
|
||||||
|
#else
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
} // namespace g3
|
||||||
193
PeakMenu/g3log/logmessage.cpp
Normal file
193
PeakMenu/g3log/logmessage.cpp
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
/** ==========================================================================
|
||||||
|
* 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
||||||
|
* with no warranties. This code is yours to share, use and modify with no
|
||||||
|
* strings attached and no restrictions or obligations.
|
||||||
|
*
|
||||||
|
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
||||||
|
* ============================================================================*/
|
||||||
|
|
||||||
|
#include "g3log/logmessage.hpp"
|
||||||
|
#include <mutex>
|
||||||
|
#include "g3log/crashhandler.hpp"
|
||||||
|
#include "g3log/time.hpp"
|
||||||
|
|
||||||
|
namespace g3 {
|
||||||
|
|
||||||
|
std::string LogMessage::splitFileName(const std::string& str) {
|
||||||
|
size_t found;
|
||||||
|
found = str.find_last_of("(/\\");
|
||||||
|
return str.substr(found + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// helper for fatal signal
|
||||||
|
std::string LogMessage::fatalSignalToString(const LogMessage& msg) {
|
||||||
|
std::string out; // clear any previous text and formatting
|
||||||
|
out.append(msg.timestamp() + "\n\n***** FATAL SIGNAL RECEIVED ******* \n" + msg.message() + '\n');
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// helper for fatal exception (windows only)
|
||||||
|
std::string LogMessage::fatalExceptionToString(const LogMessage& msg) {
|
||||||
|
std::string out; // clear any previous text and formatting
|
||||||
|
out.append(msg.timestamp() + "\n\n***** FATAL EXCEPTION RECEIVED ******* \n" + msg.message() + '\n');
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// helper for fatal LOG
|
||||||
|
std::string LogMessage::fatalLogToString(const LogMessage& msg) {
|
||||||
|
auto out = msg._logDetailsToStringFunc(msg);
|
||||||
|
static const std::string fatalExitReason = {"EXIT trigger caused by LOG(FATAL) entry: "};
|
||||||
|
out.append("\n\t*******\t " + fatalExitReason + "\n\t" + '"' + msg.message() + '"');
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// helper for fatal CHECK
|
||||||
|
std::string LogMessage::fatalCheckToString(const LogMessage& msg) {
|
||||||
|
auto out = msg._logDetailsToStringFunc(msg);
|
||||||
|
static const std::string contractExitReason = {"EXIT trigger caused by broken Contract:"};
|
||||||
|
out.append("\n\t*******\t " + contractExitReason + " CHECK(" + msg.expression() + ")\n\t" + '"' + msg.message() + '"');
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// helper for setting the normal log details in an entry
|
||||||
|
std::string LogMessage::DefaultLogDetailsToString(const LogMessage& msg) {
|
||||||
|
std::string out;
|
||||||
|
out.append(msg.timestamp() + "\t" + msg.level() + " [" + msg.file() + "->" + msg.function() + ":" + msg.line() + "]\t");
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string LogMessage::FullLogDetailsToString(const LogMessage& msg) {
|
||||||
|
std::string out;
|
||||||
|
out.append(msg.timestamp() + "\t" + msg.level() + " [" + msg.threadID() + " " + msg.file() + "->" + msg.function() + ":" + msg.line() + "]\t");
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// helper for normal
|
||||||
|
std::string LogMessage::normalToString(const LogMessage& msg) {
|
||||||
|
auto out = msg._logDetailsToStringFunc(msg);
|
||||||
|
out.append(msg.message() + '\n');
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// end static functions section
|
||||||
|
|
||||||
|
void LogMessage::overrideLogDetailsFunc(LogDetailsFunc func) const {
|
||||||
|
_logDetailsToStringFunc = func;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format the log message according to it's type
|
||||||
|
std::string LogMessage::toString(LogDetailsFunc formattingFunc) const {
|
||||||
|
overrideLogDetailsFunc(formattingFunc);
|
||||||
|
|
||||||
|
if (false == wasFatal()) {
|
||||||
|
return LogMessage::normalToString(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto level_value = _level.value;
|
||||||
|
if (internal::FATAL_SIGNAL.value == _level.value) {
|
||||||
|
return LogMessage::fatalSignalToString(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (internal::FATAL_EXCEPTION.value == _level.value) {
|
||||||
|
return LogMessage::fatalExceptionToString(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FATAL.value == _level.value) {
|
||||||
|
return LogMessage::fatalLogToString(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (internal::CONTRACT.value == level_value) {
|
||||||
|
return LogMessage::fatalCheckToString(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// What? Did we hit a custom made level?
|
||||||
|
auto out = _logDetailsToStringFunc(*this);
|
||||||
|
static const std::string errorUnknown = {"UNKNOWN or Custom made Log Message Type"};
|
||||||
|
out.append("\t*******" + errorUnknown + "\n\t" + message() + '\n');
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string LogMessage::timestamp(const std::string& time_look) const {
|
||||||
|
return g3::localtime_formatted(to_system_time(_timestamp), time_look);
|
||||||
|
}
|
||||||
|
|
||||||
|
// By copy, not by reference. See this explanation for details:
|
||||||
|
// http://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom
|
||||||
|
LogMessage& LogMessage::operator=(LogMessage other) {
|
||||||
|
swap(*this, other);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
LogMessage::LogMessage(std::string file, const int line,
|
||||||
|
std::string function, const LEVELS level) :
|
||||||
|
_logDetailsToStringFunc(LogMessage::DefaultLogDetailsToString),
|
||||||
|
_timestamp(std::chrono::high_resolution_clock::now()),
|
||||||
|
_call_thread_id(std::this_thread::get_id())
|
||||||
|
#if defined(G3_LOG_FULL_FILENAME)
|
||||||
|
,
|
||||||
|
_file(file)
|
||||||
|
#else
|
||||||
|
,
|
||||||
|
_file(LogMessage::splitFileName(file))
|
||||||
|
#endif
|
||||||
|
,
|
||||||
|
_file_path(file),
|
||||||
|
_line(line),
|
||||||
|
_function(std::move(function)),
|
||||||
|
_level(level) {
|
||||||
|
}
|
||||||
|
|
||||||
|
LogMessage::LogMessage(const std::string& fatalOsSignalCrashMessage) :
|
||||||
|
LogMessage({""}, 0, {""}, internal::FATAL_SIGNAL) {
|
||||||
|
_message.append(fatalOsSignalCrashMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
LogMessage::LogMessage(const LogMessage& other) :
|
||||||
|
_logDetailsToStringFunc(other._logDetailsToStringFunc),
|
||||||
|
_timestamp(other._timestamp),
|
||||||
|
_call_thread_id(other._call_thread_id),
|
||||||
|
_file(other._file),
|
||||||
|
_file_path(other._file_path),
|
||||||
|
_line(other._line),
|
||||||
|
_function(other._function),
|
||||||
|
_level(other._level),
|
||||||
|
_expression(other._expression),
|
||||||
|
_message(other._message) {
|
||||||
|
}
|
||||||
|
|
||||||
|
LogMessage::LogMessage(LogMessage&& other) :
|
||||||
|
_logDetailsToStringFunc(other._logDetailsToStringFunc),
|
||||||
|
_timestamp(other._timestamp),
|
||||||
|
_call_thread_id(other._call_thread_id),
|
||||||
|
_file(std::move(other._file)),
|
||||||
|
_file_path(std::move(other._file_path)),
|
||||||
|
_line(other._line),
|
||||||
|
_function(std::move(other._function)),
|
||||||
|
_level(other._level),
|
||||||
|
_expression(std::move(other._expression)),
|
||||||
|
_message(std::move(other._message)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string LogMessage::threadID() const {
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << _call_thread_id;
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
FatalMessage::FatalMessage(const LogMessage& details, g3::SignalType signal_id) :
|
||||||
|
LogMessage(details),
|
||||||
|
_signal_id(signal_id) {}
|
||||||
|
|
||||||
|
FatalMessage::FatalMessage(const FatalMessage& other) :
|
||||||
|
LogMessage(other),
|
||||||
|
_signal_id(other._signal_id) {}
|
||||||
|
|
||||||
|
LogMessage FatalMessage::copyToLogMessage() const {
|
||||||
|
return LogMessage(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string FatalMessage::reason() const {
|
||||||
|
return internal::exitReasonName(_level, _signal_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace g3
|
||||||
125
PeakMenu/g3log/logworker.cpp
Normal file
125
PeakMenu/g3log/logworker.cpp
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
/** ==========================================================================
|
||||||
|
* 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
||||||
|
* with no warranties. This code is yours to share, use and modify with no
|
||||||
|
* strings attached and no restrictions or obligations.
|
||||||
|
*
|
||||||
|
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
||||||
|
* ============================================================================*/
|
||||||
|
|
||||||
|
#include "g3log/logworker.hpp"
|
||||||
|
#include "g3log/active.hpp"
|
||||||
|
#include "g3log/crashhandler.hpp"
|
||||||
|
#include "g3log/future.hpp"
|
||||||
|
#include "g3log/g3log.hpp"
|
||||||
|
#include "g3log/logmessage.hpp"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace g3 {
|
||||||
|
|
||||||
|
LogWorkerImpl::LogWorkerImpl() :
|
||||||
|
_bg(kjellkod::Active::createActive()) {}
|
||||||
|
|
||||||
|
void LogWorkerImpl::bgSave(g3::LogMessagePtr msgPtr) {
|
||||||
|
std::unique_ptr<LogMessage> uniqueMsg(std::move(msgPtr.get()));
|
||||||
|
|
||||||
|
for (auto& sink : _sinks) {
|
||||||
|
LogMessage msg(*(uniqueMsg));
|
||||||
|
sink->send(LogMessageMover(std::move(msg)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_sinks.empty()) {
|
||||||
|
std::string err_msg{"g3logworker has no sinks. Message: ["};
|
||||||
|
err_msg.append(uniqueMsg.get()->toString()).append("]\n");
|
||||||
|
std::cerr << err_msg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogWorkerImpl::bgFatal(FatalMessagePtr msgPtr) {
|
||||||
|
// this will be the last message. Only the active logworker can receive a FATAL call so it's
|
||||||
|
// safe to shutdown logging now
|
||||||
|
g3::internal::shutDownLogging();
|
||||||
|
|
||||||
|
std::string reason = msgPtr.get()->reason();
|
||||||
|
const auto level = msgPtr.get()->_level;
|
||||||
|
const auto fatal_id = msgPtr.get()->_signal_id;
|
||||||
|
|
||||||
|
std::unique_ptr<LogMessage> uniqueMsg(std::move(msgPtr.get()));
|
||||||
|
uniqueMsg->write().append("\nExiting after fatal event (").append(uniqueMsg->level());
|
||||||
|
|
||||||
|
// Change output in case of a fatal signal (or windows exception)
|
||||||
|
std::string exiting = {"Fatal type: "};
|
||||||
|
|
||||||
|
uniqueMsg->write().append("). ").append(exiting).append(" ").append(reason).append("\nLog content flushed successfully to sink\n\n");
|
||||||
|
|
||||||
|
std::cerr << uniqueMsg->toString() << std::flush;
|
||||||
|
for (auto& sink : _sinks) {
|
||||||
|
LogMessage msg(*(uniqueMsg));
|
||||||
|
sink->send(LogMessageMover(std::move(msg)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// This clear is absolutely necessary
|
||||||
|
// All sinks are forced to receive the fatal message above before we continue
|
||||||
|
_sinks.clear(); // flush all queues
|
||||||
|
internal::exitWithDefaultSignalHandler(level, fatal_id);
|
||||||
|
|
||||||
|
// should never reach this point
|
||||||
|
perror("g3log exited after receiving FATAL trigger. Flush message status: ");
|
||||||
|
}
|
||||||
|
|
||||||
|
LogWorker::~LogWorker() {
|
||||||
|
g3::internal::shutDownLoggingForActiveOnly(this);
|
||||||
|
|
||||||
|
// The sinks WILL automatically be cleared at exit of this destructor
|
||||||
|
// The waiting inside removeAllSinks ensures that all messages until this point are
|
||||||
|
// taken care of before any internals/LogWorkerImpl of LogWorker starts to be destroyed.
|
||||||
|
// i.e. this avoids a race with another thread slipping through the "shutdownLogging" and
|
||||||
|
// calling ::save or ::fatal through LOG/CHECK with lambda messages and "partly
|
||||||
|
// deconstructed LogWorkerImpl"
|
||||||
|
//
|
||||||
|
// Any messages put into the queue will be OK due to:
|
||||||
|
// *) If it is before the wait below then they will be executed
|
||||||
|
// *) If it is AFTER the wait below then they will be ignored and NEVER executed
|
||||||
|
removeAllSinks();
|
||||||
|
|
||||||
|
// The background worker WILL be automatically cleared at the exit of the destructor
|
||||||
|
// However, the explicitly clearing of the background worker (below) makes sure that there can
|
||||||
|
// be no thread that manages to add another sink after the call to clear the sinks above.
|
||||||
|
// i.e. this manages the extremely unlikely case of another thread calling
|
||||||
|
// addWrappedSink after the sink clear above. Normally adding of sinks should be done in main.cpp
|
||||||
|
// and be closely coupled with the existence of the LogWorker. Sharing this adding of sinks to
|
||||||
|
// other threads that do not know the state of LogWorker is considered a bug but it is dealt with
|
||||||
|
// nonetheless below.
|
||||||
|
//
|
||||||
|
// If sinks would already have been added after the sink clear above then this reset will deal with it
|
||||||
|
// without risking lambda execution with a partially deconstructed LogWorkerImpl
|
||||||
|
// Calling g3::spawn_task on a nullptr Active object will not crash but return
|
||||||
|
// a future containing an appropriate exception.
|
||||||
|
_impl._bg.reset(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogWorker::save(LogMessagePtr msg) {
|
||||||
|
_impl._bg->send([this, msg] { _impl.bgSave(msg); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogWorker::fatal(FatalMessagePtr fatal_message) {
|
||||||
|
_impl._bg->send([this, fatal_message] { _impl.bgFatal(fatal_message); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogWorker::addWrappedSink(std::shared_ptr<g3::internal::SinkWrapper> sink) {
|
||||||
|
auto bg_addsink_call = [this, sink] {
|
||||||
|
_impl._sinks.push_back(sink);
|
||||||
|
};
|
||||||
|
auto token_done = g3::spawn_task(bg_addsink_call, _impl._bg.get());
|
||||||
|
token_done.wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<LogWorker> LogWorker::createLogWorker() {
|
||||||
|
return std::unique_ptr<LogWorker>(new LogWorker);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<FileSinkHandle> LogWorker::addDefaultLogger(const std::string& log_prefix, const std::string& log_directory, const std::string& default_id) {
|
||||||
|
return addSink(std::make_unique<g3::FileSink>(log_prefix, log_directory, default_id), &FileSink::fileWrite);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace g3
|
||||||
196
PeakMenu/g3log/stacktrace_windows.cpp
Normal file
196
PeakMenu/g3log/stacktrace_windows.cpp
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
/** ==========================================================================
|
||||||
|
* Original code made by Robert Engeln. Given as a PUBLIC DOMAIN dedication for
|
||||||
|
* the benefit of g3log. It was originally published at:
|
||||||
|
* http://code-freeze.blogspot.com/2012/01/generating-stack-traces-from-c.html
|
||||||
|
|
||||||
|
* 2014-2015: adapted for g3log by Kjell Hedstrom (KjellKod).
|
||||||
|
*
|
||||||
|
* This is PUBLIC DOMAIN to use at your own risk and comes
|
||||||
|
* with no warranties. This code is yours to share, use and modify with no
|
||||||
|
* strings attached and no restrictions or obligations.
|
||||||
|
*
|
||||||
|
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
||||||
|
* ============================================================================*/
|
||||||
|
|
||||||
|
#include "g3log/stacktrace_windows.hpp"
|
||||||
|
|
||||||
|
#include <dbghelp.h>
|
||||||
|
#include <windows.h>
|
||||||
|
#include <cassert>
|
||||||
|
#include <g3log/g3log.hpp>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#pragma comment(lib, "dbghelp.lib")
|
||||||
|
|
||||||
|
#if !(defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
|
||||||
|
#error "stacktrace_win.cpp used but not on a windows system"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define g3_MAP_PAIR_STRINGIFY(x) \
|
||||||
|
{ x, #x }
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
thread_local size_t g_thread_local_recursive_crash_check = 0;
|
||||||
|
|
||||||
|
const std::map<g3::SignalType, std::string> kExceptionsAsText = {
|
||||||
|
g3_MAP_PAIR_STRINGIFY(EXCEPTION_ACCESS_VIOLATION), g3_MAP_PAIR_STRINGIFY(EXCEPTION_ARRAY_BOUNDS_EXCEEDED), g3_MAP_PAIR_STRINGIFY(EXCEPTION_DATATYPE_MISALIGNMENT), g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_DENORMAL_OPERAND), g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_DIVIDE_BY_ZERO), g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_INEXACT_RESULT), g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_INEXACT_RESULT), g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_INVALID_OPERATION), g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_OVERFLOW), g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_STACK_CHECK), g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_UNDERFLOW), g3_MAP_PAIR_STRINGIFY(EXCEPTION_ILLEGAL_INSTRUCTION), g3_MAP_PAIR_STRINGIFY(EXCEPTION_IN_PAGE_ERROR), g3_MAP_PAIR_STRINGIFY(EXCEPTION_INT_DIVIDE_BY_ZERO), g3_MAP_PAIR_STRINGIFY(EXCEPTION_INT_OVERFLOW), g3_MAP_PAIR_STRINGIFY(EXCEPTION_INVALID_DISPOSITION), g3_MAP_PAIR_STRINGIFY(EXCEPTION_NONCONTINUABLE_EXCEPTION), g3_MAP_PAIR_STRINGIFY(EXCEPTION_PRIV_INSTRUCTION), g3_MAP_PAIR_STRINGIFY(EXCEPTION_STACK_OVERFLOW), g3_MAP_PAIR_STRINGIFY(EXCEPTION_BREAKPOINT), g3_MAP_PAIR_STRINGIFY(EXCEPTION_SINGLE_STEP)
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// Using the given context, fill in all the stack frames.
|
||||||
|
// Which then later can be interpreted to human readable text
|
||||||
|
void captureStackTrace(CONTEXT* context, std::vector<uint64_t>& frame_pointers) {
|
||||||
|
DWORD machine_type = 0;
|
||||||
|
STACKFRAME64 frame = {}; // force zeroing
|
||||||
|
frame.AddrPC.Mode = AddrModeFlat;
|
||||||
|
frame.AddrFrame.Mode = AddrModeFlat;
|
||||||
|
frame.AddrStack.Mode = AddrModeFlat;
|
||||||
|
#if defined(_M_ARM64)
|
||||||
|
frame.AddrPC.Offset = context->Pc;
|
||||||
|
frame.AddrFrame.Offset = context->Fp;
|
||||||
|
frame.AddrStack.Offset = context->Sp;
|
||||||
|
machine_type = IMAGE_FILE_MACHINE_ARM64;
|
||||||
|
#elif defined(_M_ARM)
|
||||||
|
frame.AddrPC.Offset = context->Pc;
|
||||||
|
frame.AddrFrame.Offset = context->R11;
|
||||||
|
frame.AddrStack.Offset = context->Sp;
|
||||||
|
machine_type = IMAGE_FILE_MACHINE_ARM;
|
||||||
|
#elif defined(_M_X64)
|
||||||
|
frame.AddrPC.Offset = context->Rip;
|
||||||
|
frame.AddrFrame.Offset = context->Rbp;
|
||||||
|
frame.AddrStack.Offset = context->Rsp;
|
||||||
|
machine_type = IMAGE_FILE_MACHINE_AMD64;
|
||||||
|
#else
|
||||||
|
frame.AddrPC.Offset = context->Eip;
|
||||||
|
frame.AddrPC.Offset = context->Ebp;
|
||||||
|
frame.AddrPC.Offset = context->Esp;
|
||||||
|
machine_type = IMAGE_FILE_MACHINE_I386;
|
||||||
|
#endif
|
||||||
|
for (size_t index = 0; index < frame_pointers.size(); ++index) {
|
||||||
|
if (StackWalk64(machine_type,
|
||||||
|
GetCurrentProcess(),
|
||||||
|
GetCurrentThread(),
|
||||||
|
&frame,
|
||||||
|
context,
|
||||||
|
NULL,
|
||||||
|
SymFunctionTableAccess64,
|
||||||
|
SymGetModuleBase64,
|
||||||
|
NULL)) {
|
||||||
|
frame_pointers[index] = frame.AddrPC.Offset;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// extract readable text from a given stack frame. All thanks to
|
||||||
|
// using SymFromAddr and SymGetLineFromAddr64 with the stack pointer
|
||||||
|
std::string getSymbolInformation(const size_t index, const std::vector<uint64_t>& frame_pointers) {
|
||||||
|
auto addr = frame_pointers[index];
|
||||||
|
std::string frame_dump = "stack dump [" + std::to_string(index) + "]\t";
|
||||||
|
|
||||||
|
DWORD64 displacement64;
|
||||||
|
DWORD displacement;
|
||||||
|
alignas(SYMBOL_INFO) char symbol_buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME];
|
||||||
|
SYMBOL_INFO* symbol = reinterpret_cast<SYMBOL_INFO*>(symbol_buffer);
|
||||||
|
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||||
|
symbol->MaxNameLen = MAX_SYM_NAME;
|
||||||
|
|
||||||
|
IMAGEHLP_LINE64 line;
|
||||||
|
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
|
||||||
|
std::string lineInformation;
|
||||||
|
std::string callInformation;
|
||||||
|
if (SymFromAddr(GetCurrentProcess(), addr, &displacement64, symbol)) {
|
||||||
|
callInformation.append(" ").append(std::string(symbol->Name, symbol->NameLen));
|
||||||
|
if (SymGetLineFromAddr64(GetCurrentProcess(), addr, &displacement, &line)) {
|
||||||
|
lineInformation.append("\t").append(line.FileName).append(" L: ");
|
||||||
|
lineInformation.append(std::to_string(line.LineNumber));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
frame_dump.append(lineInformation).append(callInformation);
|
||||||
|
return frame_dump;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieves all the symbols for the stack frames, fills them within a text representation and returns it
|
||||||
|
std::string convertFramesToText(std::vector<uint64_t>& frame_pointers) {
|
||||||
|
std::string dump; // slightly more efficient than ostringstream
|
||||||
|
const size_t kSize = frame_pointers.size();
|
||||||
|
for (size_t index = 0; index < kSize && frame_pointers[index]; ++index) {
|
||||||
|
dump += getSymbolInformation(index, frame_pointers);
|
||||||
|
dump += "\n";
|
||||||
|
}
|
||||||
|
return dump;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace stacktrace {
|
||||||
|
const std::string kUnknown = {"UNKNOWN EXCEPTION"};
|
||||||
|
/// return the text description of a Windows exception code
|
||||||
|
/// From MSDN GetExceptionCode http://msdn.microsoft.com/en-us/library/windows/desktop/ms679356(v=vs.85).aspx
|
||||||
|
std::string exceptionIdToText(g3::SignalType id) {
|
||||||
|
const auto iter = kExceptionsAsText.find(id);
|
||||||
|
if (iter == kExceptionsAsText.end()) {
|
||||||
|
std::string unknown = {kUnknown + ":" + std::to_string(id)};
|
||||||
|
return unknown;
|
||||||
|
}
|
||||||
|
return iter->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Yes a double lookup: first for isKnownException and then exceptionIdToText
|
||||||
|
/// for vectored exceptions we only deal with known exceptions so this tiny
|
||||||
|
/// overhead we can live with
|
||||||
|
bool isKnownException(g3::SignalType id) {
|
||||||
|
return (kExceptionsAsText.end() != kExceptionsAsText.find(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// helper function: retrieve stackdump from no excisting exception pointer
|
||||||
|
std::string stackdump() {
|
||||||
|
CONTEXT current_context;
|
||||||
|
memset(¤t_context, 0, sizeof(CONTEXT));
|
||||||
|
RtlCaptureContext(¤t_context);
|
||||||
|
return stackdump(¤t_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// helper function: retrieve stackdump, starting from an exception pointer
|
||||||
|
std::string stackdump(EXCEPTION_POINTERS* info) {
|
||||||
|
auto context = info->ContextRecord;
|
||||||
|
return stackdump(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// main stackdump function. retrieve stackdump, from the given context
|
||||||
|
std::string stackdump(CONTEXT* context) {
|
||||||
|
|
||||||
|
if (g_thread_local_recursive_crash_check >= 2) { // In Debug scenarios we allow one extra pass
|
||||||
|
std::string recursive_crash = {"\n\n\n***** Recursive crash detected"};
|
||||||
|
recursive_crash.append(", cannot continue stackdump traversal. *****\n\n\n");
|
||||||
|
return recursive_crash;
|
||||||
|
}
|
||||||
|
++g_thread_local_recursive_crash_check;
|
||||||
|
|
||||||
|
static std::mutex m;
|
||||||
|
std::lock_guard<std::mutex> lock(m);
|
||||||
|
{
|
||||||
|
const BOOL kLoadSymModules = TRUE;
|
||||||
|
const auto initialized = SymInitialize(GetCurrentProcess(), nullptr, kLoadSymModules);
|
||||||
|
if (TRUE != initialized) {
|
||||||
|
return {"Error: Cannot call SymInitialize(...) for retrieving symbols in stack"};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<void> RaiiSymCleaner(nullptr, [&](void*) {
|
||||||
|
SymCleanup(GetCurrentProcess());
|
||||||
|
}); // Raii sym cleanup
|
||||||
|
|
||||||
|
constexpr size_t kmax_frame_dump_size = 64;
|
||||||
|
std::vector<uint64_t> frame_pointers(kmax_frame_dump_size);
|
||||||
|
// C++11: size set and values are zeroed
|
||||||
|
|
||||||
|
assert(frame_pointers.size() == kmax_frame_dump_size);
|
||||||
|
captureStackTrace(context, frame_pointers);
|
||||||
|
return convertFramesToText(frame_pointers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace stacktrace
|
||||||
151
PeakMenu/g3log/time.cpp
Normal file
151
PeakMenu/g3log/time.cpp
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
/** ==========================================================================
|
||||||
|
* 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
||||||
|
* with no warranties. This code is yours to share, use and modify with no
|
||||||
|
* strings attached and no restrictions or obligations.
|
||||||
|
*
|
||||||
|
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
|
||||||
|
* ============================================================================*/
|
||||||
|
|
||||||
|
#include "g3log/time.hpp"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <chrono>
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstring>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
#ifdef __MACH__
|
||||||
|
#include <sys/time.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace g3 {
|
||||||
|
namespace internal {
|
||||||
|
const std::string kFractionalIdentier = "%f";
|
||||||
|
const size_t kFractionalIdentierSize = 2;
|
||||||
|
|
||||||
|
Fractional getFractional(const std::string& format_buffer, size_t pos) {
|
||||||
|
char ch = (format_buffer.size() > pos + kFractionalIdentierSize ? format_buffer.at(pos + kFractionalIdentierSize) : '\0');
|
||||||
|
Fractional type = Fractional::NanosecondDefault;
|
||||||
|
switch (ch) {
|
||||||
|
case '3':
|
||||||
|
type = Fractional::Millisecond;
|
||||||
|
break;
|
||||||
|
case '6':
|
||||||
|
type = Fractional::Microsecond;
|
||||||
|
break;
|
||||||
|
case '9':
|
||||||
|
type = Fractional::Nanosecond;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
type = Fractional::NanosecondDefault;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the fractional as a string with padded zeroes
|
||||||
|
// 1 ms --> 001
|
||||||
|
// 1 us --> 000001
|
||||||
|
// 1 ns --> 000000001
|
||||||
|
std::string to_string(const g3::system_time_point& ts, Fractional fractional) {
|
||||||
|
auto duration = ts.time_since_epoch();
|
||||||
|
auto sec_duration = std::chrono::duration_cast<std::chrono::seconds>(duration);
|
||||||
|
duration -= sec_duration;
|
||||||
|
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count();
|
||||||
|
|
||||||
|
auto zeroes = 9; // default ns
|
||||||
|
auto digitsToCut = 1; // default ns, divide by 1 makes no change
|
||||||
|
switch (fractional) {
|
||||||
|
case Fractional::Millisecond: {
|
||||||
|
zeroes = 3;
|
||||||
|
digitsToCut = 1000000;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Fractional::Microsecond: {
|
||||||
|
zeroes = 6;
|
||||||
|
digitsToCut = 1000;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Fractional::Nanosecond:
|
||||||
|
case Fractional::NanosecondDefault:
|
||||||
|
default:
|
||||||
|
zeroes = 9;
|
||||||
|
digitsToCut = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ns /= digitsToCut;
|
||||||
|
auto value = std::string(std::to_string(ns));
|
||||||
|
return std::string(zeroes - value.size(), '0') + value;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string localtime_formatted_fractions(const g3::system_time_point& ts, std::string format_buffer) {
|
||||||
|
// iterating through every "%f" instance in the format string
|
||||||
|
auto identifierExtraSize = 0;
|
||||||
|
for (size_t pos = 0;
|
||||||
|
(pos = format_buffer.find(g3::internal::kFractionalIdentier, pos)) != std::string::npos;
|
||||||
|
pos += g3::internal::kFractionalIdentierSize + identifierExtraSize) {
|
||||||
|
// figuring out whether this is nano, micro or milli identifier
|
||||||
|
auto type = g3::internal::getFractional(format_buffer, pos);
|
||||||
|
auto value = g3::internal::to_string(ts, type);
|
||||||
|
auto padding = 0;
|
||||||
|
if (type != g3::internal::Fractional::NanosecondDefault) {
|
||||||
|
padding = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// replacing "%f[3|6|9]" with sec fractional part value
|
||||||
|
format_buffer.replace(pos, g3::internal::kFractionalIdentier.size() + padding, value);
|
||||||
|
}
|
||||||
|
return format_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace g3
|
||||||
|
|
||||||
|
namespace g3 {
|
||||||
|
// This mimics the original "std::put_time(const std::tm* tmb, const charT* fmt)"
|
||||||
|
// This is needed since latest version (at time of writing) of gcc4.7 does not implement this library function yet.
|
||||||
|
// return value is SIMPLIFIED to only return a std::string
|
||||||
|
std::string put_time(const struct tm* tmb, const char* c_time_format) {
|
||||||
|
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) && !defined(__MINGW32__)
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss.fill('0');
|
||||||
|
// BOGUS hack done for VS2012: C++11 non-conformant since it SHOULD take a "const struct tm* "
|
||||||
|
oss << std::put_time(const_cast<struct tm*>(tmb), c_time_format);
|
||||||
|
return oss.str();
|
||||||
|
#else // LINUX
|
||||||
|
const size_t size = 1024;
|
||||||
|
char buffer[size]; // IMPORTANT: check now and then for when gcc will implement std::put_time.
|
||||||
|
// ... also ... This is way more buffer space then we need
|
||||||
|
|
||||||
|
auto success = std::strftime(buffer, size, c_time_format, tmb);
|
||||||
|
// In DEBUG the assert will trigger a process exit. Once inside the if-statement
|
||||||
|
// the 'always true' expression will be displayed as reason for the exit
|
||||||
|
//
|
||||||
|
// In Production mode
|
||||||
|
// the assert will do nothing but the format string will instead be returned
|
||||||
|
if (0 == success) {
|
||||||
|
assert((0 != success) && "strftime fails with illegal formatting");
|
||||||
|
return c_time_format;
|
||||||
|
}
|
||||||
|
return buffer;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
tm localtime(std::time_t ts) {
|
||||||
|
struct tm tm_snapshot;
|
||||||
|
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
|
||||||
|
localtime_s(&tm_snapshot, &ts); // windsows
|
||||||
|
#else
|
||||||
|
localtime_r(&ts, &tm_snapshot); // POSIX
|
||||||
|
#endif
|
||||||
|
return tm_snapshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string localtime_formatted(const g3::system_time_point& ts, const std::string& time_format) {
|
||||||
|
auto format_buffer = internal::localtime_formatted_fractions(ts, time_format);
|
||||||
|
auto time_point = std::chrono::system_clock::to_time_t(ts);
|
||||||
|
std::tm t = localtime(time_point);
|
||||||
|
return g3::put_time(&t, format_buffer.c_str()); // format example: //"%Y/%m/%d %H:%M:%S");
|
||||||
|
}
|
||||||
|
} // namespace g3
|
||||||
134
PeakMenu/logger.hpp
Normal file
134
PeakMenu/logger.hpp
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common.hpp"
|
||||||
|
#include "g3log/g3log.hpp"
|
||||||
|
#include "g3log/logworker.hpp"
|
||||||
|
|
||||||
|
namespace peak
|
||||||
|
{
|
||||||
|
const LEVELS SUCCESS{200, {"SUCCESS"}};
|
||||||
|
const LEVELS ERR{800, "ERROR"};
|
||||||
|
|
||||||
|
class Logger;
|
||||||
|
inline Logger* logger{};
|
||||||
|
|
||||||
|
class Logger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit Logger() :
|
||||||
|
worker(g3::LogWorker::createLogWorker())
|
||||||
|
{
|
||||||
|
AllocConsole();
|
||||||
|
consoleHandle = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||||
|
SetConsoleTitleA("PeakMenu");
|
||||||
|
SetConsoleOutputCP(CP_UTF8);
|
||||||
|
RemoveMenu(GetSystemMenu(GetConsoleWindow(), FALSE), SC_CLOSE, MF_DISABLED);
|
||||||
|
SetConsoleCtrlHandler([](DWORD) -> BOOL
|
||||||
|
{
|
||||||
|
isRunning = false;
|
||||||
|
return TRUE;
|
||||||
|
}, TRUE);
|
||||||
|
consoleOut.open("CONOUT$", std::ios_base::out | std::ios_base::app);
|
||||||
|
size_t size = MAX_PATH;
|
||||||
|
char* buffer;
|
||||||
|
_dupenv_s(&buffer, &size, "APPDATA");
|
||||||
|
filePath = (std::filesystem::path)buffer / "PeakMenu";
|
||||||
|
std::filesystem::path logPath = filePath / "logs";
|
||||||
|
if (!std::filesystem::exists(filePath))
|
||||||
|
{
|
||||||
|
std::filesystem::create_directory(filePath);
|
||||||
|
}
|
||||||
|
else if (!std::filesystem::is_directory(filePath))
|
||||||
|
{
|
||||||
|
std::filesystem::remove(filePath);
|
||||||
|
std::filesystem::create_directory(filePath);
|
||||||
|
}
|
||||||
|
if (!std::filesystem::exists(logPath))
|
||||||
|
{
|
||||||
|
std::filesystem::create_directory(logPath);
|
||||||
|
}
|
||||||
|
else if (!std::filesystem::is_directory(logPath))
|
||||||
|
{
|
||||||
|
std::filesystem::remove(logPath);
|
||||||
|
std::filesystem::create_directory(logPath);
|
||||||
|
}
|
||||||
|
filePath /= "PeakMenu.log";
|
||||||
|
if (std::filesystem::exists(filePath))
|
||||||
|
{
|
||||||
|
tm localTime;
|
||||||
|
time_t creationDate = std::chrono::system_clock::to_time_t(std::chrono::time_point_cast<std::chrono::system_clock::duration>(std::filesystem::last_write_time(filePath) - std::filesystem::file_time_type::clock::now() + std::chrono::system_clock::now()));
|
||||||
|
localtime_s(&localTime, &creationDate);
|
||||||
|
std::ostringstream day, month, year, hour, minute, second;
|
||||||
|
day << std::setfill('0') << std::setw(2) << localTime.tm_mday;
|
||||||
|
month << "-" << std::setfill('0') << std::setw(2) << localTime.tm_mon + 1;
|
||||||
|
year << "-" << std::setfill('0') << std::setw(4) << localTime.tm_year + 1900;
|
||||||
|
hour << " " << std::setfill('0') << std::setw(2) << localTime.tm_hour;
|
||||||
|
minute << "-" << std::setfill('0') << std::setw(2) << localTime.tm_min;
|
||||||
|
second << "-" << std::setfill('0') << std::setw(2) << localTime.tm_sec;
|
||||||
|
std::filesystem::copy_file(filePath, logPath / (day.str() + month.str() + year.str() + hour.str() + minute.str() + second.str()));
|
||||||
|
}
|
||||||
|
fileOut.open(filePath, std::ios_base::out | std::ios_base::trunc);
|
||||||
|
worker->addSink(std::make_unique<Sink>(), &Sink::callback);
|
||||||
|
g3::initializeLogging(worker.get());
|
||||||
|
g3::setFatalExitHandler(&Sink::fatalCallback);
|
||||||
|
logger = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
~Logger()
|
||||||
|
{
|
||||||
|
worker.reset();
|
||||||
|
FreeConsole();
|
||||||
|
logger = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Sink
|
||||||
|
{
|
||||||
|
std::map<std::string, WORD> colors = {{SUCCESS.text, FOREGROUND_GREEN | FOREGROUND_INTENSITY}, {INFO.text, FOREGROUND_BLUE | FOREGROUND_INTENSITY}, {WARNING.text, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY}, {ERR.text, FOREGROUND_RED | FOREGROUND_INTENSITY}, {FATAL.text, FOREGROUND_RED | FOREGROUND_INTENSITY}};
|
||||||
|
|
||||||
|
void callback(g3::LogMessageMover log)
|
||||||
|
{
|
||||||
|
g3::LogMessage msg = log.get();
|
||||||
|
SetConsoleTextAttribute(logger->consoleHandle, colors.find(msg.level())->second);
|
||||||
|
std::stringstream message(msg.toString([](const g3::LogMessage&)->std::string {return ""; }));
|
||||||
|
std::string line;
|
||||||
|
std::ostringstream fmt, fileFmt;
|
||||||
|
fmt << "[" << msg.timestamp("%d/%m/%Y %H:%M:%S") << "] ";
|
||||||
|
fileFmt << "[" << msg.timestamp("%d/%m/%Y %H:%M:%S") << "] [" << msg.level() << "] ";
|
||||||
|
while (std::getline(message, line, '\n'))
|
||||||
|
{
|
||||||
|
logger->consoleOut << fmt.str() << line << std::endl << std::flush;
|
||||||
|
if (std::filesystem::exists(logger->filePath) && !std::filesystem::is_directory(logger->filePath))
|
||||||
|
{
|
||||||
|
logger->fileOut << fileFmt.str() << line << std::endl << std::flush;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fatalCallback(g3::FatalMessagePtr log)
|
||||||
|
{
|
||||||
|
SetConsoleTextAttribute(logger->consoleHandle, FOREGROUND_RED | FOREGROUND_INTENSITY);
|
||||||
|
std::stringstream message(log.get()->toString([](const g3::LogMessage&)->std::string {return ""; }));
|
||||||
|
std::string line;
|
||||||
|
std::ostringstream fmt, fileFmt;
|
||||||
|
fmt << "[" << log.get()->timestamp("%d/%m/%Y %H:%M:%S") << "] ";
|
||||||
|
fileFmt << "[" << log.get()->timestamp("%d/%m/%Y %H:%M:%S") << "] [FATAL] ";
|
||||||
|
while (std::getline(message, line, '\n'))
|
||||||
|
{
|
||||||
|
logger->consoleOut << fmt.str() << line << std::endl << std::flush;
|
||||||
|
if (std::filesystem::exists(logger->filePath) && !std::filesystem::is_directory(logger->filePath))
|
||||||
|
{
|
||||||
|
logger->fileOut << fileFmt.str() << line << std::endl << std::flush;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
HANDLE consoleHandle{};
|
||||||
|
std::ofstream consoleOut;
|
||||||
|
std::filesystem::path filePath;
|
||||||
|
std::ofstream fileOut;
|
||||||
|
std::unique_ptr<g3::LogWorker> worker;
|
||||||
|
};
|
||||||
|
}
|
||||||
37
PeakMenu/main.cpp
Normal file
37
PeakMenu/main.cpp
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
#include "common.hpp"
|
||||||
|
#include "logger.hpp"
|
||||||
|
#include "pointers.hpp"
|
||||||
|
|
||||||
|
BOOL WINAPI DllMain(HMODULE hmod, DWORD reason, PVOID)
|
||||||
|
{
|
||||||
|
using namespace peak;
|
||||||
|
if (reason == DLL_PROCESS_ATTACH)
|
||||||
|
{
|
||||||
|
hModule = hmod;
|
||||||
|
mainThread = CreateThread(nullptr, 0, [](PVOID) -> DWORD {
|
||||||
|
auto loggerInstance = std::make_unique<Logger>();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto pointerInstance = std::make_unique<Pointers>();
|
||||||
|
LOG(SUCCESS) << "Patterns found.";
|
||||||
|
while (isRunning)
|
||||||
|
{
|
||||||
|
pointers->character->infiniteStamina = true;
|
||||||
|
std::this_thread::sleep_for(100ms);
|
||||||
|
}
|
||||||
|
pointers->character->infiniteStamina = false;
|
||||||
|
pointerInstance.reset();
|
||||||
|
LOG(SUCCESS) << "Removed patterns";
|
||||||
|
}
|
||||||
|
catch (std::exception& exc)
|
||||||
|
{
|
||||||
|
LOG(FATAL) << exc.what();
|
||||||
|
}
|
||||||
|
std::this_thread::sleep_for(1s);
|
||||||
|
loggerInstance.reset();
|
||||||
|
CloseHandle(mainThread);
|
||||||
|
FreeLibraryAndExitThread(hModule, 0);
|
||||||
|
}, nullptr, 0, &mainThreadId);
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
24
PeakMenu/memory.hpp
Normal file
24
PeakMenu/memory.hpp
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common.hpp"
|
||||||
|
#include "logger.hpp"
|
||||||
|
|
||||||
|
namespace peak
|
||||||
|
{
|
||||||
|
class Memory
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
template <typename T>
|
||||||
|
static T pointerToObject(uintptr_t pointer, std::vector<unsigned int> offsets)
|
||||||
|
{
|
||||||
|
HANDLE process = GetCurrentProcess();
|
||||||
|
uintptr_t address = (uintptr_t)GetModuleHandle(L"UnityPlayer.dll") + pointer;
|
||||||
|
for (int i = 0; i < offsets.size(); i++)
|
||||||
|
{
|
||||||
|
ReadProcessMemory(process, (BYTE*)address, &address, sizeof(address), 0);
|
||||||
|
address += offsets[i];
|
||||||
|
}
|
||||||
|
return (T)address;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
13
PeakMenu/offsets.hpp
Normal file
13
PeakMenu/offsets.hpp
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
struct CharacterData
|
||||||
|
{
|
||||||
|
char _pad0[0x208];
|
||||||
|
float extraStamina;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Character
|
||||||
|
{
|
||||||
|
char _pad0[0xCB];
|
||||||
|
bool infiniteStamina;
|
||||||
|
};
|
||||||
15
PeakMenu/pointers.cpp
Normal file
15
PeakMenu/pointers.cpp
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#include "pointers.hpp"
|
||||||
|
|
||||||
|
namespace peak
|
||||||
|
{
|
||||||
|
Pointers::Pointers()
|
||||||
|
{
|
||||||
|
character = Memory::pointerToObject<Character*>(0x01EB35D0, {0x430, 0x9B0, 0x10, 0xC8, 0x8, 0x110, 0x0});
|
||||||
|
pointers = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Pointers::~Pointers()
|
||||||
|
{
|
||||||
|
pointers = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
15
PeakMenu/pointers.hpp
Normal file
15
PeakMenu/pointers.hpp
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#include "memory.hpp"
|
||||||
|
#include "offsets.hpp"
|
||||||
|
|
||||||
|
namespace peak
|
||||||
|
{
|
||||||
|
class Pointers
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Pointers();
|
||||||
|
~Pointers();
|
||||||
|
Character* character{};
|
||||||
|
};
|
||||||
|
|
||||||
|
inline Pointers* pointers{};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user