Skip to content

Commit

Permalink
Added chowdsp_logging module (#494)
Browse files Browse the repository at this point in the history
* Starting on logging module

* Fixing some things on Windows

* Refactoring

* Adding log file start message

* Update README and CHANGELOG

* Apply clang-format

* Update for CI

* Apply clang-format

* More CI fixing

* More CI fixing

* Fixing logging test

* Add maybe_unused

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
  • Loading branch information
jatinchowdhury18 and github-actions[bot] authored Feb 18, 2024
1 parent 5f52b05 commit ecc530e
Show file tree
Hide file tree
Showing 197 changed files with 32,164 additions and 191 deletions.
13 changes: 11 additions & 2 deletions .github/workflows/build-arm-mac.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
fail-fast: false # show all errors for each platform (vs. cancel jobs on error)
matrix:
tests: ["chowdsp_buffers_test chowdsp_dsp_data_structures_test chowdsp_dsp_juce_test chowdsp_dsp_utils_test chowdsp_sources_test chowdsp_math_test chowdsp_simd_test chowdsp_filters_test chowdsp_waveshapers_test chowdsp_modal_dsp_test chowdsp_compressor_test"]
os: [macos-latest]
os: [macos-14]

steps:
- name: Get latest CMake
Expand All @@ -41,11 +41,20 @@ jobs:
- name: Configure
working-directory: ${{env.WORK_DIR}}
shell: bash
run: cmake -Bbuild -DCHOWDSP_ENABLE_TESTING=ON -GXcode -DCMAKE_OSX_ARCHITECTURES=arm64
run: cmake -Bbuild -DCHOWDSP_ENABLE_TESTING=ON -DCMAKE_OSX_ARCHITECTURES=arm64

- name: Build
working-directory: ${{env.WORK_DIR}}/build
shell: bash
env:
TEST_TARGETS: ${{ matrix.tests }}
run: cmake --build . --config Release --parallel 4 --target $TEST_TARGETS | xcpretty

- name: "Run Tests: ${{ matrix.tests }}"
working-directory: ${{env.WORK_DIR}}
shell: bash
env:
TEST_TARGETS: ${{ matrix.tests }}
run: |
ctest --test-dir build -C Release -R ${TEST_TARGETS// /|} --show-only
ctest --test-dir build -C Release -R ${TEST_TARGETS// /|} -j 4 --no-tests=error --output-on-failure
4 changes: 2 additions & 2 deletions .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
fail-fast: false # show all errors for each platform (vs. cancel jobs on error)
matrix:
tests: [
"chowdsp_core_test chowdsp_data_structures_test chowdsp_json_test chowdsp_serialization_test chowdsp_units_test chowdsp_buffers_test chowdsp_dsp_juce_test", # common_tests_lib + dsp_juce_tests_lib
"chowdsp_core_test chowdsp_data_structures_test chowdsp_json_test chowdsp_serialization_test chowdsp_logging_test chowdsp_units_test chowdsp_buffers_test chowdsp_dsp_juce_test", # common_tests_lib + dsp_juce_tests_lib
"chowdsp_dsp_data_structures_test chowdsp_dsp_utils_test chowdsp_filters_test chowdsp_math_test chowdsp_modal_dsp_test chowdsp_simd_test chowdsp_sources_test chowdsp_waveshapers_test chowdsp_compressor_test", # dsp_tests_lib
"chowdsp_parameters_test chowdsp_plugin_state_test chowdsp_plugin_base_test chowdsp_plugin_utils_test chowdsp_presets_v2_test chowdsp_version_test", # plugin_tests_lib
"chowdsp_gui_test chowdsp_visualizers_test", # gui_tests_lib
Expand Down Expand Up @@ -119,7 +119,7 @@ jobs:
BUILD_TYPE: ${{ matrix.build_type }}
run: |
ctest --test-dir build -C $BUILD_TYPE -R ${TEST_TARGETS// /|} --show-only
ctest --test-dir build -C $BUILD_TYPE -R ${TEST_TARGETS// /|} -j 4 --output-on-failure
ctest --test-dir build -C $BUILD_TYPE -R ${TEST_TARGETS// /|} -j 4 --no-tests=error --output-on-failure
- name: "Run Live GUI Test"
if: matrix.live_gui
Expand Down
24 changes: 22 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,34 @@ All notable changes to this project will be documented in this file.
- Refactored `chowdsp_buffers` module out of `chowdsp_dsp_data_structures`.
- Refactored `chowdsp_data_structures` module out of `chowdsp_core`, and added new data structures.
- Added `chowdsp_compressor` module.
- Added `chowdsp::buffer_iters::samples`.
- Added `chowdsp_logging` module.
- Added `chowdsp::zip_multi`.
- Added `chowdsp::buffer_iters::samples` and `chowdsp::buffer_iters::zip_channels`.
- Added `chowdsp::ArenaAllocator` and `chowdsp::ChainedArenaAllocator`.
- Added `chowdsp::UIToAudioPipeline`.
- Added `chowdsp::CrossoverFilter`.
- Added `chowdsp::NoiseSynth`.
- Added math approximations: `chowdsp::LogApprox`, `chowdsp::PowApprox`, and `chowdsp::DecibelsApprox`.
- Added `chowdsp::OvershootLimiter`.
- Added `chowdsp::make_array`.
- Added `chowdsp::EndOfScopeAction` and `chowdsp::runAtEndOfScope`.
- Added `chowdsp::RandomFloat`.
- Added `chowdsp::FIRPolyphaseInterpolator` and `chowdsp::FIRPolyphaseDecimator`.
- Added `chowdsp::SmallMap`.
- Added `chowdsp::WidthPanner`.
- `chowdsp_visualizers`: Added `WaveshaperPlot`.
- `chowdsp_buffers`: Improved constructor and type compatibility.
- `chowdsp_dsp_utils`: Optimized `chowdsp::Upsampler` and `chowdsp::Downsampler`.
- `chowdsp_serialization`: Added specializations for serializing enums and json.
- `chowdsp_clap_extensions`: Added helpers for preset discovery.
- `chowds::DelayLine`: Added `free()` and `processBlock()` methods.
- `chowdsp::SynthBase`: No longer clears the audio buffer before passing it into `processSnyth()`.
- `chowdsp_presets_v2`: Refactored preset save/load logic into `chowdsp::PresetSaverLoader`.
- `chowdsp::CoefficientCalculationMode::Decramped`: Improved stability for decramped filters with low Q.
- `chowdsp::StringLiteral`: Improved construction.
- `chowdsp::SineWave`: Added `processSampleQuadrature()`.
- `chowdsp::Polynomials::estrin`: Fixed address sanitizer error.
- Improved memory alignment handling for structures that deal with low-level memory.
- Improved compatibility for targets without SIMD support.

## [2.1.0] 2023-03-04
- Added `chowdsp_visualizers` module.
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ sure to abide by the license of each module, as well as whichever libraries are
`chowdsp_listeners` (BSD)
- A thin wrapper around [`rocket`](https://github.com/tripleslash/rocket) (Public Domain, included internally).

`chowdsp_logging` (BSD)
- A thin wrapper around [`spdlog`](https://github.com/gabime/spdlog) (MIT License, included internally).

`chowdsp_reflection` (BSD)
- Includes the following internal dependencies:
- [`boost::pfr`](https://github.com/boostorg/pfr) (Boost license).
Expand Down
1 change: 1 addition & 0 deletions cmake/AddJUCEModules.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ juce_add_modules(
${PROJECT_SOURCE_DIR}/modules/common/chowdsp_listeners
${PROJECT_SOURCE_DIR}/modules/common/chowdsp_reflection
${PROJECT_SOURCE_DIR}/modules/common/chowdsp_serialization
${PROJECT_SOURCE_DIR}/modules/common/chowdsp_logging
${PROJECT_SOURCE_DIR}/modules/common/chowdsp_units

${PROJECT_SOURCE_DIR}/modules/dsp/chowdsp_buffers
Expand Down
1 change: 1 addition & 0 deletions cmake/test/SetupCodeQuality.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ target_link_libraries(chowdsp_utils_codeql PRIVATE
chowdsp::chowdsp_listeners
chowdsp::chowdsp_reflection
chowdsp::chowdsp_serialization
chowdsp::chowdsp_logging
chowdsp::chowdsp_units
chowdsp::chowdsp_buffers
chowdsp::chowdsp_compressor
Expand Down
1 change: 1 addition & 0 deletions cmake/test/SetupStaticTests.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ target_link_libraries(static_test_dummy_executable PRIVATE
chowdsp_listeners
chowdsp_reflection
chowdsp_serialization
chowdsp_logging
chowdsp_units
chowdsp_buffers
chowdsp_compressor
Expand Down
25 changes: 25 additions & 0 deletions modules/common/chowdsp_logging/Loggers/chowdsp_BaseLogger.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#pragma once

namespace chowdsp
{
struct BaseLogger : juce::Logger
{
spdlog::logger internal_logger { "chowdsp_log" };
spdlog::sink_ptr console_sink {};

BaseLogger()
{
console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
internal_logger.sinks().push_back (console_sink);

#if JUCE_DEBUG && JUCE_WINDOWS
internal_logger.sinks().push_back (std::make_shared<spdlog::sinks::msvc_sink_mt>());
#endif
}

void logMessage (const juce::String& message) override
{
internal_logger.info (message.toStdString());
}
};
} // namespace chowdsp
67 changes: 67 additions & 0 deletions modules/common/chowdsp_logging/Loggers/chowdsp_CrashLogHelpers.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#include "chowdsp_CrashLogHelpers.h"

namespace chowdsp::CrashLogHelpers
{
void defaultCrashLogAnalyzer (const juce::File& logFile)
{
#if JUCE_MODULE_AVAILABLE_juce_gui_basics
auto alertOptions =
juce::MessageBoxOptions()
.withTitle ("Crash detected!")
.withMessage (
"A previous instance of this plugin has crashed! Would you like to view the logs?")
#if JUCE_IOS
.withButton ("Copy Logs")
#else
.withButton ("Show Log File")
#endif
.withButton ("Cancel");

juce::AlertWindow::showAsync (
alertOptions,
[logFile] (int result)
{
if (result == 1)
{
#if JUCE_IOS
juce::SystemClipboard::copyTextToClipboard (logFile.loadFileAsString());
#else
logFile.startAsProcess();
#endif
}
});
#else
jassertfalse; // Implement your own!
#endif
}

constexpr std::string_view crashString = "Plugin crashing!!!";
constexpr std::string_view crashExaminedString = "The crash in this log file is now being examined!";

void checkLogFilesForCrashes (const LogFileHelpers::FileArray& logFiles,
const CrashLogAnalysisCallback& callback)
{
for (auto& logFile : logFiles)
{
const auto& logString = logFile.loadFileAsString();

if (! logString.contains (toString (crashString)))
continue;

if (logString.contains (toString (crashExaminedString)))
continue;

callback (logFile);
logFile.appendText (toString (crashExaminedString));
}
}

void signalHandler (void*) // NOSONAR (void* is needed here)
{
juce::Logger::writeToLog ("Interrupt signal received!");
juce::Logger::writeToLog ("Stack Trace:");
juce::Logger::writeToLog (juce::SystemStats::getStackBacktrace());

LogFileHelpers::shutdownLogger (1);
}
} // namespace chowdsp::CrashLogHelpers
18 changes: 18 additions & 0 deletions modules/common/chowdsp_logging/Loggers/chowdsp_CrashLogHelpers.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#pragma once

namespace chowdsp
{
#ifndef DOXYGEN
namespace CrashLogHelpers
{
using CrashLogAnalysisCallback = std::function<void (const juce::File&)>;

void defaultCrashLogAnalyzer (const juce::File& logFile);

void checkLogFilesForCrashes (const LogFileHelpers::FileArray& logFiles,
const CrashLogAnalysisCallback& callback);

void signalHandler (void*); // NOSONAR (void* is needed here)
} // namespace CrashLogHelpers
#endif
} // namespace chowdsp
69 changes: 69 additions & 0 deletions modules/common/chowdsp_logging/Loggers/chowdsp_LogFileHelpers.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#include "chowdsp_LogFileHelpers.h"

#if JUCE_MODULE_AVAILABLE_juce_gui_basics
#include <juce_gui_basics/juce_gui_basics.h>
#endif

namespace chowdsp
{
namespace LogFileHelpers
{
struct LogFileComparator
{
static int compareElements (juce::File first, juce::File second) // NOLINT(performance-unnecessary-value-param): JUCE sort needs copies for some reason
{
const auto firstTime = first.getLastModificationTime().toMilliseconds();
const auto secondTime = second.getLastModificationTime().toMilliseconds();

return firstTime < secondTime;
}
};

FileArray getLogFilesSorted (const LogFileParams& params)
{
const juce::String logFileWildcard = "*" + params.logFileExtension;
FileArray logFiles;

auto logFilesDir = juce::FileLogger::getSystemLogFileFolder().getChildFile (params.logFileSubDir);
if (! logFilesDir.isDirectory())
return logFiles;

const auto numLogFiles = logFilesDir.getNumberOfChildFiles (juce::File::findFiles, logFileWildcard);
logFiles.reserve (numLogFiles);

for (const auto& entry : juce::RangedDirectoryIterator (logFilesDir, false, logFileWildcard))
{
VectorHelpers::insert_sorted (logFiles,
entry.getFile(),
[] (const auto& lhs, const auto& rhs)
{
const auto lhsTime = lhs.getLastModificationTime().toMilliseconds();
const auto rhsTime = rhs.getLastModificationTime().toMilliseconds();
return lhsTime < rhsTime;
});
}

return logFiles;
}

// delete old log files to keep the total number of log files below the max!
void pruneOldLogFiles (FileArray& logFiles, const LogFileParams& params)
{
if (logFiles.size() <= params.maxNumLogFiles)
return;

while (logFiles.size() > params.maxNumLogFiles)
{
const auto& lastFile = logFiles.back();
lastFile.deleteFile();
logFiles.erase (logFiles.end() - 1);
}
}

void shutdownLogger (int signal)
{
juce::Logger::writeToLog (toString (signal == 0 ? exitString : crashString));
juce::Logger::setCurrentLogger (nullptr);
}
} // namespace LogFileHelpers
} // namespace chowdsp
33 changes: 33 additions & 0 deletions modules/common/chowdsp_logging/Loggers/chowdsp_LogFileHelpers.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#pragma once

#include <utility>

namespace chowdsp
{
/** Parameters for log files */
struct LogFileParams
{
juce::String logFileSubDir {};
juce::String logFileNameRoot {};
juce::String logFileExtension = ".log";
size_t maxNumLogFiles = 50;
};

#ifndef DOXYGEN
namespace LogFileHelpers
{
constexpr std::string_view openString = "This log file is currently being written to...";
constexpr std::string_view exitString = "Exiting gracefully...";
constexpr std::string_view crashString = "Plugin crashing!!!";
constexpr std::string_view crashExaminedString = "The crash in this log file is now being examined!";

using FileArray = std::vector<juce::File>;
FileArray getLogFilesSorted (const LogFileParams& params);

// delete old log files to keep the total number of log files below the max!
void pruneOldLogFiles (FileArray& logFiles, const LogFileParams& params);

void shutdownLogger (int signal = 0);
} // namespace LogFileHelpers
#endif // DOXYGEN
} // namespace chowdsp
59 changes: 59 additions & 0 deletions modules/common/chowdsp_logging/Loggers/chowdsp_Logger.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#include "chowdsp_Logger.h"

namespace chowdsp
{
Logger::Logger (const juce::String& logFileSubDir, const juce::String& logFileNameRoot)
: Logger (LogFileParams { logFileSubDir, logFileNameRoot })
{
}

static juce::File getSystemLogFileFolder()
{
#if JUCE_MAC
return juce::File ("~/Library/Logs");
#else
return juce::File::getSpecialLocation (juce::File::userApplicationDataDirectory);
#endif
}

Logger::Logger (const LogFileParams& loggerParams) : params (loggerParams)
{
using namespace LogFileHelpers;
using namespace CrashLogHelpers;

auto&& pastLogFiles = getLogFilesSorted (params);
pruneOldLogFiles (pastLogFiles, params);
checkLogFilesForCrashes (pastLogFiles, crashLogAnalysisCallback);

log_file = getSystemLogFileFolder()
.getChildFile (params.logFileSubDir)
.getChildFile (params.logFileNameRoot
+ juce::Time::getCurrentTime()
.formatted ("%Y-%m-%d_%H-%M-%S"))
.withFileExtension (params.logFileExtension)
.getNonexistentSibling();
log_file.create();

file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt> (log_file.getFullPathName().toStdString(),
false);
logger.internal_logger.sinks().push_back (file_sink);
logger.internal_logger.info ("Starting log file: " + log_file.getFullPathName().toStdString());

juce::Logger::setCurrentLogger (&logger);

juce::SystemStats::setApplicationCrashHandler (signalHandler);
}

Logger::~Logger()
{
try
{
logger.internal_logger.flush();
}
catch ([[maybe_unused]] const spdlog::spdlog_ex& ex)
{
jassertfalse;
}
LogFileHelpers::shutdownLogger();
}
} // namespace chowdsp
Loading

0 comments on commit ecc530e

Please sign in to comment.