|
| 1 | +#include <chrono> |
| 2 | +#include <functional> |
| 3 | +#include <thread> |
| 4 | + |
| 5 | +#include <esp_core_dump.h> |
| 6 | + |
| 7 | +#include "binary-log.hpp" |
| 8 | +#include "file_system.hpp" |
| 9 | +#include "logger.hpp" |
| 10 | + |
| 11 | +using namespace std::chrono_literals; |
| 12 | + |
| 13 | +extern "C" void app_main(void) { |
| 14 | + espp::Logger logger({.tag = "blog_example", .level = espp::Logger::Verbosity::INFO}); |
| 15 | + |
| 16 | + logger.info("Starting binary log example"); |
| 17 | + // initialize the file system |
| 18 | + std::error_code ec; |
| 19 | + auto &fs = espp::FileSystem::get(); |
| 20 | + auto root = fs.get_root_path(); |
| 21 | + const std::filesystem::path logfile = root / std::filesystem::path{"log.out"}; |
| 22 | + |
| 23 | + auto print_files = [&fs, &logger, &root, &logfile]() { |
| 24 | + logger.info("Printing log file"); |
| 25 | + std::error_code ec; |
| 26 | + |
| 27 | + size_t file_size = std::filesystem::file_size(logfile, ec); |
| 28 | + std::ifstream ifs(logfile, std::ios::in | std::ios::binary); |
| 29 | + // read bytes |
| 30 | + std::vector<char> file_bytes(file_size); |
| 31 | + ifs.read(file_bytes.data(), file_size); |
| 32 | + ifs.close(); |
| 33 | + logger.info("Read {} bytes from log file", file_size); |
| 34 | + logger.info("File contents:\n{::#02x}", file_bytes); |
| 35 | + |
| 36 | + // now print the contents of the other two associated files |
| 37 | + logger.info("Printing index file"); |
| 38 | + std::filesystem::path indexfile = logfile; |
| 39 | + indexfile.replace_extension(logfile.extension().string() + ".index"); |
| 40 | + file_size = std::filesystem::file_size(indexfile, ec); |
| 41 | + ifs.open(indexfile, std::ios::in | std::ios::binary); |
| 42 | + // read bytes |
| 43 | + file_bytes.resize(file_size); |
| 44 | + ifs.read(file_bytes.data(), file_size); |
| 45 | + ifs.close(); |
| 46 | + logger.info("Read {} bytes from index file", file_size); |
| 47 | + logger.info("File contents:\n{::#02x}", file_bytes); |
| 48 | + |
| 49 | + // print the contents of the runlength file |
| 50 | + logger.info("Printing runlength file"); |
| 51 | + std::filesystem::path runlengthfile = logfile; |
| 52 | + runlengthfile.replace_extension(logfile.extension().string() + ".runlength"); |
| 53 | + file_size = std::filesystem::file_size(runlengthfile, ec); |
| 54 | + ifs.open(runlengthfile, std::ios::in | std::ios::binary); |
| 55 | + // read bytes |
| 56 | + file_bytes.resize(file_size); |
| 57 | + ifs.read(file_bytes.data(), file_size); |
| 58 | + ifs.close(); |
| 59 | + logger.info("Read {} bytes from runlength file", file_size); |
| 60 | + logger.info("File contents:\n{::#02x}", file_bytes); |
| 61 | + |
| 62 | + // print the contents of the file system |
| 63 | + logger.info("Directory contents:\n{}", fs.list_directory(root, {})); |
| 64 | + }; |
| 65 | + |
| 66 | + // first see if the file exists |
| 67 | + if (std::filesystem::exists(logfile, ec)) { |
| 68 | + logger.info("Removing existing log file"); |
| 69 | + fs.remove(logfile, ec); |
| 70 | + } |
| 71 | + |
| 72 | + { |
| 73 | + //! [Binary Log example] |
| 74 | + float num_seconds_to_run = 3.0f; |
| 75 | + // create standard logger. This will buffer the logs in memory until the buffer is |
| 76 | + // full or the logger is destroyed |
| 77 | + logger.info("Creating binary log file: {}", logfile.c_str()); |
| 78 | + static constexpr size_t log_buffer_size = 1024; |
| 79 | + static constexpr size_t index_buffer_size = 1024; |
| 80 | + static constexpr size_t runlength_buffer_size = 1024; |
| 81 | + using Packer = binary_log::packer<log_buffer_size, index_buffer_size, runlength_buffer_size>; |
| 82 | + binary_log::binary_log<Packer> log(logfile); |
| 83 | + |
| 84 | + logger.info("Starting binary logging for {}s", num_seconds_to_run); |
| 85 | + auto start = std::chrono::high_resolution_clock::now(); |
| 86 | + auto now = std::chrono::high_resolution_clock::now(); |
| 87 | + float elapsed = std::chrono::duration<float>(now - start).count(); |
| 88 | + while (elapsed < num_seconds_to_run) { |
| 89 | + now = std::chrono::high_resolution_clock::now(); |
| 90 | + elapsed = std::chrono::duration<float>(now - start).count(); |
| 91 | + auto remaining = num_seconds_to_run - elapsed; |
| 92 | + |
| 93 | + // NOTE: use of a single log within the majority of this loop allows for |
| 94 | + // run-length encoding of the log messages to be used to reduce the size |
| 95 | + BINARY_LOG(log, "elapsed: {}s\nremaining: {}s", elapsed, remaining); |
| 96 | + |
| 97 | + // print a log each second. to track it, just get the remainder |
| 98 | + // milliseconds and see if they're less than 15 |
| 99 | + if (std::chrono::duration_cast<std::chrono::milliseconds>(now - start).count() % 1000 < 15) { |
| 100 | + BINARY_LOG(log, "This is a log message at {}s", elapsed) |
| 101 | + } |
| 102 | + |
| 103 | + if (remaining < 0) { |
| 104 | + BINARY_LOG(log, "You overstayed your welcome by {}s!", -remaining); |
| 105 | + } |
| 106 | + std::this_thread::sleep_for(10ms); |
| 107 | + } |
| 108 | + //! [Logger example] |
| 109 | + } |
| 110 | + |
| 111 | + // now print out the logger files |
| 112 | + print_files(); |
| 113 | + |
| 114 | + // now erase the file system |
| 115 | + logger.info("Erase file system"); |
| 116 | + fs.remove_contents(root, ec); |
| 117 | + |
| 118 | + { |
| 119 | + //! [Binary Log example] |
| 120 | + float num_seconds_to_run = 3.0f; |
| 121 | + // create ringbuffer logger. This will not write to disk, but instead will |
| 122 | + // only store log data in memory. The log buffer will be a ring buffer, |
| 123 | + // discarding the oldest data, while the index and runlength buffers will be |
| 124 | + // simple arrays which will abort if they run out of space. |
| 125 | + logger.info("Creating ringbuffer binary log"); |
| 126 | + static constexpr size_t log_buffer_size = 1024; |
| 127 | + static constexpr size_t index_buffer_size = 1024; |
| 128 | + static constexpr size_t runlength_buffer_size = 1024; |
| 129 | + using Packer = |
| 130 | + binary_log::ringbuffer_packer<log_buffer_size, index_buffer_size, runlength_buffer_size>; |
| 131 | + binary_log::binary_log<Packer> log(logfile); |
| 132 | + |
| 133 | + logger.info("Starting binary logging for {}s", num_seconds_to_run); |
| 134 | + auto start = std::chrono::high_resolution_clock::now(); |
| 135 | + auto now = std::chrono::high_resolution_clock::now(); |
| 136 | + float elapsed = std::chrono::duration<float>(now - start).count(); |
| 137 | + while (elapsed < num_seconds_to_run) { |
| 138 | + now = std::chrono::high_resolution_clock::now(); |
| 139 | + elapsed = std::chrono::duration<float>(now - start).count(); |
| 140 | + auto remaining = num_seconds_to_run - elapsed; |
| 141 | + |
| 142 | + BINARY_LOG(log, "elapsed: {}s", elapsed); |
| 143 | + BINARY_LOG(log, "remaining: {}s", remaining); |
| 144 | + |
| 145 | + // print a log each second. to track it, just get the remainder |
| 146 | + // milliseconds and see if they're less than 15 |
| 147 | + if (std::chrono::duration_cast<std::chrono::milliseconds>(now - start).count() % 1000 < 15) { |
| 148 | + BINARY_LOG(log, "This is a log message at {}s", elapsed) |
| 149 | + } |
| 150 | + |
| 151 | + if (remaining < 0) { |
| 152 | + BINARY_LOG(log, "You overstayed your welcome by {}s!", -remaining); |
| 153 | + } |
| 154 | + std::this_thread::sleep_for(10ms); |
| 155 | + } |
| 156 | + // Since this is a memory-based logger, we must flush and read out the data |
| 157 | + // manually |
| 158 | + log.flush(); |
| 159 | + |
| 160 | + const Packer &packer = log.get_packer(); |
| 161 | + auto log_buffer = packer.get_log_buffer(); |
| 162 | + auto index_buffer_sv = packer.get_index_buffer(); |
| 163 | + // convert the string view into a vector for easy printing with libfmt |
| 164 | + std::vector<uint8_t> index_buffer(index_buffer_sv.begin(), index_buffer_sv.end()); |
| 165 | + auto runlength_buffer_sv = packer.get_runlength_buffer(); |
| 166 | + // convert the string view into a vector for easy printing with libfmt |
| 167 | + std::vector<uint8_t> runlength_buffer(runlength_buffer_sv.begin(), runlength_buffer_sv.end()); |
| 168 | + |
| 169 | + logger.info("Log file size: {} bytes", log_buffer.size()); |
| 170 | + logger.info("Index file size: {} bytes", index_buffer.size()); |
| 171 | + logger.info("Runlength file size: {} bytes", runlength_buffer.size()); |
| 172 | + logger.info("--------------------------------"); |
| 173 | + logger.info("Total file size: {} bytes", |
| 174 | + log_buffer.size() + index_buffer.size() + runlength_buffer.size()); |
| 175 | + |
| 176 | + logger.info("Log data:\n{::#02x}", log_buffer); |
| 177 | + logger.info("Index data:\n{::#02x}", index_buffer); |
| 178 | + logger.info("Runlength data:\n{::#02x}", runlength_buffer); |
| 179 | + //! [Logger example] |
| 180 | + } |
| 181 | + |
| 182 | + logger.info("Finished binary log example"); |
| 183 | + |
| 184 | + // now loop forever |
| 185 | + while (true) { |
| 186 | + std::this_thread::sleep_for(1s); |
| 187 | + } |
| 188 | +} |
0 commit comments