Merge pull request #1638 from majestrate/log-refactor-source-location-2021-05-11

refactor logging to use std::source_location
pull/1760/head
Jeff 3 years ago committed by GitHub
commit 12bca7031b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -2,7 +2,6 @@ function(add_log_tag target)
get_target_property(TARGET_SRCS ${target} SOURCES)
foreach(F ${TARGET_SRCS})
get_filename_component(fpath "${F}" ABSOLUTE)
string(REPLACE "${PROJECT_SOURCE_DIR}/" "" logtag "${fpath}")
set_property(SOURCE ${F} APPEND PROPERTY COMPILE_DEFINITIONS LOG_TAG=\"${logtag}\")
set_property(SOURCE ${F} APPEND PROPERTY COMPILE_DEFINITIONS SOURCE_ROOT=\"${PROJECT_SOURCE_DIR}\")
endforeach()
endfunction()

@ -496,7 +496,7 @@ lokinet_main(int argc, char* argv[])
}
catch (std::exception& ex)
{
LogError("cannot generate config at ", *configFile, ": ", ex.what());
llarp::LogError("cannot generate config at ", *configFile, ": ", ex.what());
return 1;
}
}
@ -512,7 +512,7 @@ lokinet_main(int argc, char* argv[])
}
catch (std::exception& ex)
{
LogError("cannot check if ", *configFile, " exists: ", ex.what());
llarp::LogError("cannot check if ", *configFile, " exists: ", ex.what());
return 1;
}
}
@ -574,7 +574,7 @@ lokinet_main(int argc, char* argv[])
"file a bug report now or be cursed with this "
"annoying image in your syslog for all time."})
{
LogError(wtf);
llarp::LogError{wtf};
llarp::LogContext::Instance().ImmediateFlush();
}
#ifdef _WIN32

@ -10,7 +10,6 @@ add_library(lokinet-util
util/logging/android_logger.cpp
util/logging/buffer.cpp
util/logging/file_logger.cpp
util/logging/json_logger.cpp
util/logging/logger.cpp
util/logging/logger_internal.cpp
util/logging/loglevel.cpp

@ -6,7 +6,7 @@ namespace llarp::apple
NSLogStream::PreLog(
std::stringstream& ss,
LogLevel lvl,
const char* fname,
std::string_view fname,
int lineno,
const std::string& nodename) const
{
@ -17,7 +17,7 @@ namespace llarp::apple
}
void
NSLogStream::Print(LogLevel, const char*, const std::string& msg)
NSLogStream::Print(LogLevel, std::string_view, const std::string& msg)
{
ns_logger(msg.c_str());
}

@ -16,15 +16,15 @@ namespace llarp::apple
PreLog(
std::stringstream& s,
LogLevel lvl,
const char* fname,
std::string_view fname,
int lineno,
const std::string& nodename) const override;
void
Print(LogLevel lvl, const char* tag, const std::string& msg) override;
Print(LogLevel lvl, std::string_view tag, const std::string& msg) override;
void
PostLog(std::stringstream& ss) const override
PostLog(std::stringstream&) const override
{}
void

@ -105,7 +105,7 @@ llarp_apple_init(llarp_apple_config* appleconf)
}
catch (const std::exception& e)
{
LogError("Failed to initialize lokinet from config: ", e.what());
llarp::LogError("Failed to initialize lokinet from config: ", e.what());
}
return nullptr;
}
@ -149,7 +149,7 @@ llarp_apple_start(void* lokinet, void* callback_context)
}
catch (const std::exception& e)
{
LogError("Failed to initialize lokinet: ", e.what());
llarp::LogError("Failed to initialize lokinet: ", e.what());
return -1;
}
@ -178,7 +178,7 @@ llarp_apple_incoming(void* lokinet, const void* bytes, size_t size)
if (iface->OfferReadPacket(buf))
return 0;
LogError("invalid IP packet: ", llarp::buffer_printer(buf));
llarp::LogError("invalid IP packet: ", llarp::buffer_printer(buf));
return -1;
}

@ -1048,7 +1048,6 @@ namespace llarp
Comment{
"Log type (format). Valid options are:",
" file - plaintext formatting",
" json - json-formatted log statements",
" syslog - logs directed to syslog",
});

@ -117,16 +117,16 @@ namespace llarp
{
if (!qd.Decode(buf))
{
llarp::LogError("failed to decode question");
LogError("failed to decode question");
return false;
}
llarp::LogDebug(qd);
LogDebug("dns question: ", qd);
}
for (auto& an : answers)
{
if (not an.Decode(buf))
{
llarp::LogDebug("failed to decode answer");
LogDebug("failed to decode answer");
return false;
}
}

@ -337,7 +337,7 @@ namespace llarp::quic
va_start(ap, fmt);
if (char* msg; vasprintf(&msg, fmt, ap) >= 0)
{
LogTraceExplicit("external/ngtcp2/*.c", 0, msg);
LogTrace{msg};
std::free(msg);
}
va_end(ap);

@ -15,7 +15,6 @@
#include <stdexcept>
#include <llarp/util/buffer.hpp>
#include <llarp/util/logging/file_logger.hpp>
#include <llarp/util/logging/json_logger.hpp>
#include <llarp/util/logging/logger_syslog.hpp>
#include <llarp/util/logging/logger.hpp>
#include <llarp/util/meta/memfn.hpp>

@ -75,7 +75,7 @@ namespace llarp
{
if (!item.BDecode(buf))
{
llarp::LogWarnTag("llarp/bencode.hpp", "failed to decode key ", k, " for entry in dict");
llarp::LogWarn("failed to decode key ", k, " for entry in dict");
return false;
}
@ -94,7 +94,7 @@ namespace llarp
uint64_t read_i;
if (!bencode_read_integer(buf, &read_i))
{
llarp::LogWarnTag("llarp/BEncode.hpp", "failed to decode key ", k, " for integer in dict");
llarp::LogWarn("failed to decode key ", k, " for integer in dict");
return false;
}
@ -210,8 +210,7 @@ namespace llarp
return true;
if (sink.DecodeKey(*key, buffer))
return true;
llarp::LogWarnTag(
"llarp/bencode.hpp", "undefined key '", *key->cur, "' for entry in dict");
llarp::LogWarn("undefined key '", *key->cur, "' for entry in dict");
return false;
},

@ -8,7 +8,8 @@ namespace llarp
{
void
AndroidLogStream::PreLog(
std::stringstream& ss, LogLevel lvl, const char* fname, int lineno, const std::string&) const
std::stringstream& ss, LogLevel lvl, std::string_view fname, int lineno, const std::string&)
const
{
switch (lvl)
{
@ -43,7 +44,7 @@ namespace llarp
{}
void
AndroidLogStream::Print(LogLevel lvl, const char* tag, const std::string& msg)
AndroidLogStream::Print(LogLevel lvl, std::string_view tag, const std::string& msg)
{
std::string str("lokinet|");
str += tag;
@ -62,6 +63,8 @@ namespace llarp
case eLogError:
__android_log_write(ANDROID_LOG_ERROR, str.c_str(), msg.c_str());
return;
default:
return;
}
} // namespace llarp

@ -12,12 +12,12 @@ namespace llarp
PreLog(
std::stringstream& s,
LogLevel lvl,
const char* fname,
std::string_view filename,
int lineno,
const std::string& nodename) const override;
void
Print(LogLevel lvl, const char* filename, const std::string& msg) override;
Print(LogLevel lvl, std::string_view filename, const std::string& msg) override;
void
PostLog(std::stringstream&) const override;

@ -65,18 +65,18 @@ namespace llarp
FileLogStream::PreLog(
std::stringstream& ss,
LogLevel lvl,
const char* fname,
std::string_view filename,
int lineno,
const std::string& nodename) const
{
ss << "[" << LogLevelToString(lvl) << "] ";
ss << "[" << nodename << "]"
<< "(" << thread_id_string() << ") " << log_timestamp() << " " << fname << ":" << lineno
<< "(" << thread_id_string() << ") " << log_timestamp() << " " << filename << ":" << lineno
<< "\t";
}
void
FileLogStream::Print(LogLevel, const char*, const std::string& msg)
FileLogStream::Print(LogLevel, std::string_view, const std::string& msg)
{
m_Lines.pushBack(msg);
}
@ -84,12 +84,12 @@ namespace llarp
void
FileLogStream::AppendLog(
LogLevel lvl,
const char* fname,
std::string_view filename,
int lineno,
const std::string& nodename,
const std::string msg)
{
ILogStream::AppendLog(lvl, fname, lineno, nodename, msg);
ILogStream::AppendLog(lvl, filename, lineno, nodename, msg);
Tick(llarp::time_now_ms());
}

@ -23,12 +23,12 @@ namespace llarp
PreLog(
std::stringstream& out,
LogLevel lvl,
const char* fname,
std::string_view filename,
int lineno,
const std::string& nodename) const override;
void
Print(LogLevel, const char*, const std::string& msg) override;
Print(LogLevel, std::string_view filename, const std::string& msg) override;
void
Tick(llarp_time_t now) override;
@ -39,7 +39,7 @@ namespace llarp
void
AppendLog(
LogLevel lvl,
const char* fname,
std::string_view filename,
int lineno,
const std::string& nodename,
const std::string msg) override;

@ -1,24 +0,0 @@
#include "json_logger.hpp"
#include <llarp/util/json.hpp>
namespace llarp
{
void
JSONLogStream::AppendLog(
LogLevel lvl,
const char* fname,
int lineno,
const std::string& nodename,
const std::string msg)
{
json::Object obj;
obj["time"] = to_json(llarp::time_now_ms());
obj["nickname"] = nodename;
obj["file"] = std::string(fname);
obj["line"] = lineno;
obj["level"] = LogLevelToString(lvl);
obj["message"] = msg;
m_Lines.pushBack(obj.dump());
}
} // namespace llarp

@ -1,28 +0,0 @@
#ifndef LLARP_UTIL_JSON_LOGGER
#define LLARP_UTIL_JSON_LOGGER
#include "file_logger.hpp"
namespace llarp
{
struct JSONLogStream : public FileLogStream
{
JSONLogStream(
std::function<void(FileLogStream::Work_t)> disk,
FILE* f,
llarp_time_t flushInterval,
bool closeFile)
: FileLogStream(std::move(disk), f, flushInterval, closeFile)
{}
void
AppendLog(
LogLevel lvl,
const char* fname,
int lineno,
const std::string& nodename,
const std::string msg) override;
};
} // namespace llarp
#endif

@ -2,7 +2,6 @@
#include "ostream_logger.hpp"
#include "logger_syslog.hpp"
#include "file_logger.hpp"
#include "json_logger.hpp"
#if defined(_WIN32)
#include "win32_logger.hpp"
#endif
@ -36,8 +35,6 @@ namespace llarp
return LogType::Unknown;
else if (str == "file")
return LogType::File;
else if (str == "json")
return LogType::Json;
else if (str == "syslog")
return LogType::Syslog;
@ -111,7 +108,7 @@ namespace llarp
{
logfile = stdout;
}
else
else if (type != LogType::Syslog)
{
logfile = ::fopen(file.c_str(), "a");
if (not logfile)
@ -141,26 +138,10 @@ namespace llarp
}
break;
case LogType::Json:
LogInfo("Switching logger to JSON with file: ", file);
std::cout << std::flush;
LogContext::Instance().logStream =
std::make_unique<JSONLogStream>(io, logfile, 100ms, logfile != stdout);
break;
case LogType::Syslog:
if (logfile)
{
// TODO: this logic should be handled in Config
// TODO: this won't even work because of default value for 'file' (== "stdout")
::fclose(logfile);
throw std::invalid_argument("Cannot mix log type=syslog and file=*");
}
#if defined(_WIN32)
throw std::runtime_error("syslog not supported on win32");
#else
LogInfo("Switching logger to syslog");
std::cout << std::flush;
LogContext::Instance().logStream = std::make_unique<SysLogStream>();
#endif
break;

@ -1,9 +1,11 @@
#pragma once
#include <memory>
#include <llarp/util/str.hpp>
#include <llarp/util/time.hpp>
#include "logstream.hpp"
#include "logger_internal.hpp"
#include "source_location.hpp"
namespace llarp
{
@ -11,7 +13,6 @@ namespace llarp
{
Unknown = 0,
File,
Json,
Syslog,
};
LogType
@ -77,52 +78,91 @@ namespace llarp
LogLevel
GetLogLevel();
/** internal */
template <typename... TArgs>
inline static void
_log(LogLevel lvl, const char* fname, int lineno, TArgs&&... args) noexcept
namespace
{
auto& log = LogContext::Instance();
if (log.curLevel > lvl || log.logStream == nullptr)
return;
std::ostringstream ss;
if constexpr (sizeof...(args) > 0)
LogAppend(ss, std::forward<TArgs>(args)...);
log.logStream->AppendLog(lvl, fname, lineno, log.nodeName, ss.str());
}
inline void
_log_noop() noexcept
{}
/** internal */
template <typename... TArgs>
inline static void
_log(LogLevel lvl, const slns::source_location& location, TArgs&&... args) noexcept
{
auto& log = LogContext::Instance();
if (log.curLevel > lvl || log.logStream == nullptr)
return;
std::ostringstream ss;
if constexpr (sizeof...(args) > 0)
LogAppend(ss, std::forward<TArgs>(args)...);
log.logStream->AppendLog(
lvl,
strip_prefix(location.file_name(), SOURCE_ROOT),
location.line(),
log.nodeName,
ss.str());
}
} // namespace
template <typename... T>
struct LogTrace
{
LogTrace(T... args, const slns::source_location& location = slns::source_location::current())
{
#ifdef NDEBUG
((void)args, ...);
(void)location;
#else
_log(eLogTrace, location, std::forward<T>(args)...);
#endif
}
};
} // namespace llarp
template <typename... T>
LogTrace(T&&...) -> LogTrace<T...>;
#define LogDebug(...) _log(llarp::eLogDebug, LOG_TAG, __LINE__, __VA_ARGS__)
#define LogInfo(...) _log(llarp::eLogInfo, LOG_TAG, __LINE__, __VA_ARGS__)
#define LogWarn(...) _log(llarp::eLogWarn, LOG_TAG, __LINE__, __VA_ARGS__)
#define LogError(...) _log(llarp::eLogError, LOG_TAG, __LINE__, __VA_ARGS__)
template <typename... T>
struct LogDebug
{
LogDebug(T... args, const slns::source_location& location = slns::source_location::current())
{
_log(eLogDebug, location, std::forward<T>(args)...);
}
};
#define LogDebugTag(tag, ...) _log(llarp::eLogDebug, tag, __LINE__, __VA_ARGS__)
#define LogInfoTag(tag, ...) _log(llarp::eLogInfo, tag, __LINE__, __VA_ARGS__)
#define LogWarnTag(tag, ...) _log(llarp::eLogWarn, tag, __LINE__, __VA_ARGS__)
#define LogErrorTag(tag, ...) _log(llarp::eLogError, tag, __LINE__, __VA_ARGS__)
template <typename... T>
LogDebug(T&&...) -> LogDebug<T...>;
#define LogDebugExplicit(tag, line, ...) _log(llarp::eLogDebug, tag, line, __VA_ARGS__)
#define LogInfoExplicit(tag, line, ...) _log(llarp::eLogInfo, tag, line __VA_ARGS__)
#define LogWarnExplicit(tag, line, ...) _log(llarp::eLogWarn, tag, line, __VA_ARGS__)
#define LogErrorExplicit(tag, line, ...) _log(llarp::eLogError, tag, line, __VA_ARGS__)
template <typename... T>
struct LogInfo
{
LogInfo(T... args, const slns::source_location& location = slns::source_location::current())
{
_log(eLogInfo, location, std::forward<T>(args)...);
}
};
// null-op Trace logging if this is a release build
#ifdef NDEBUG
#define LogTrace(...) _log_noop()
#define LogTraceTag(tag, ...) _log_noop()
#define LogTraceExplicit(tag, line, ...) _log_noop()
#else
#define LogTrace(...) _log(llarp::eLogTrace, LOG_TAG, __LINE__, __VA_ARGS__)
#define LogTraceTag(tag, ...) _log(llarp::eLogTrace, tag, __LINE__, __VA_ARGS__)
#define LogTraceExplicit(tag, line, ...) _log(llarp::eLogTrace, tag, line, __VA_ARGS__)
#endif
template <typename... T>
LogInfo(T&&...) -> LogInfo<T...>;
#ifndef LOG_TAG
#define LOG_TAG "default"
#endif
template <typename... T>
struct LogWarn
{
LogWarn(T... args, const slns::source_location& location = slns::source_location::current())
{
_log(eLogWarn, location, std::forward<T>(args)...);
}
};
template <typename... T>
LogWarn(T&&...) -> LogWarn<T...>;
template <typename... T>
struct LogError
{
LogError(T... args, const slns::source_location& location = slns::source_location::current())
{
_log(eLogError, location, std::forward<T>(args)...);
}
};
template <typename... T>
LogError(T&&...) -> LogError<T...>;
} // namespace llarp

@ -11,12 +11,12 @@ namespace llarp
PreLog(
std::stringstream& s,
LogLevel lvl,
const char* fname,
std::string_view filename,
int lineno,
const std::string& nodename) const override;
void
Print(LogLevel lvl, const char* tag, const std::string& msg) override;
Print(LogLevel lvl, std::string_view tag, const std::string& msg) override;
void
PostLog(std::stringstream& ss) const override;

@ -6,6 +6,7 @@
#include <memory>
#include <string>
#include <sstream>
#include <string_view>
namespace llarp
{
@ -18,12 +19,12 @@ namespace llarp
PreLog(
std::stringstream& out,
LogLevel lvl,
const char* fname,
std::string_view filename,
int lineno,
const std::string& nodename) const = 0;
virtual void
Print(LogLevel lvl, const char* filename, const std::string& msg) = 0;
Print(LogLevel lvl, std::string_view filename, const std::string& msg) = 0;
virtual void
PostLog(std::stringstream& out) const = 0;
@ -31,16 +32,16 @@ namespace llarp
virtual void
AppendLog(
LogLevel lvl,
const char* fname,
std::string_view filename,
int lineno,
const std::string& nodename,
const std::string msg)
{
std::stringstream ss;
PreLog(ss, lvl, fname, lineno, nodename);
PreLog(ss, lvl, filename, lineno, nodename);
ss << msg;
PostLog(ss);
Print(lvl, fname, ss.str());
Print(lvl, filename, ss.str());
}
/// A blocking call to flush to disk. Should only be called in rare circumstances.

@ -11,7 +11,7 @@ namespace llarp
OStreamLogStream::PreLog(
std::stringstream& ss,
LogLevel lvl,
const char* fname,
std::string_view filename,
int lineno,
const std::string& nodename) const
{
@ -38,7 +38,7 @@ namespace llarp
}
ss << "[" << LogLevelToString(lvl) << "] ";
ss << "[" << nodename << "]"
<< "(" << thread_id_string() << ") " << log_timestamp() << " " << fname << ":" << lineno
<< "(" << thread_id_string() << ") " << log_timestamp() << " " << filename << ":" << lineno
<< "\t";
}
@ -51,7 +51,7 @@ namespace llarp
}
void
OStreamLogStream::Print(LogLevel, const char*, const std::string& msg)
OStreamLogStream::Print(LogLevel, std::string_view, const std::string& msg)
{
m_Out << msg << std::flush;
}

@ -15,12 +15,12 @@ namespace llarp
PreLog(
std::stringstream& s,
LogLevel lvl,
const char* fname,
std::string_view filename,
int lineno,
const std::string& nodename) const override;
virtual void
Print(LogLevel lvl, const char* tag, const std::string& msg) override;
Print(LogLevel lvl, std::string_view tag, const std::string& msg) override;
void
PostLog(std::stringstream& ss) const override;

@ -0,0 +1,53 @@
#pragma once
#ifdef __cpp_lib_source_location
#include <source_location>
namespace slns = std;
#else
namespace slns
{
struct source_location
{
public:
static constexpr source_location
current(
const char* fileName = __builtin_FILE(),
const char* functionName = __builtin_FUNCTION(),
const uint_least32_t lineNumber = __builtin_LINE()) noexcept
{
return source_location{fileName, functionName, lineNumber};
}
source_location(const source_location&) = default;
source_location(source_location&&) = default;
constexpr const char*
file_name() const noexcept
{
return fileName;
}
constexpr const char*
function_name() const noexcept
{
return functionName;
}
constexpr uint_least32_t
line() const noexcept
{
return lineNumber;
}
private:
constexpr explicit source_location(
const char* fileName, const char* functionName, const uint_least32_t lineNumber) noexcept
: fileName(fileName), functionName(functionName), lineNumber(lineNumber)
{}
const char* const fileName;
const char* const functionName;
const uint_least32_t lineNumber;
};
} // namespace slns
#endif

@ -10,18 +10,18 @@ namespace llarp
SysLogStream::PreLog(
std::stringstream& ss,
LogLevel lvl,
const char* fname,
std::string_view filename,
int lineno,
const std::string& nodename) const
{
ss << "[" << LogLevelToString(lvl) << "] ";
ss << "[" << nodename << "]"
<< "(" << thread_id_string() << ") " << log_timestamp() << " " << fname << ":" << lineno
<< "(" << thread_id_string() << ") " << log_timestamp() << " " << filename << ":" << lineno
<< "\t";
}
void
SysLogStream::Print(LogLevel lvl, const char*, const std::string& msg)
SysLogStream::Print(LogLevel lvl, std::string_view, const std::string& msg)
{
switch (lvl)
{

@ -27,7 +27,7 @@ namespace llarp
Win32LogStream::PreLog(
std::stringstream& ss,
LogLevel lvl,
const char* fname,
std::string_view filename,
int lineno,
const std::string& nodename) const
{
@ -54,11 +54,11 @@ namespace llarp
break;
}
ss << "[" << nodename << "]"
<< "(" << thread_id_string() << ") " << log_timestamp() << " " << fname << ":" << lineno
<< "(" << thread_id_string() << ") " << log_timestamp() << " " << filename << ":" << lineno
<< "\t";
}
else
OStreamLogStream::PreLog(ss, lvl, fname, lineno, nodename);
OStreamLogStream::PreLog(ss, lvl, filename, lineno, nodename);
}
void
@ -71,7 +71,7 @@ namespace llarp
}
void
Win32LogStream::Print(LogLevel lvl, const char*, const std::string& msg)
Win32LogStream::Print(LogLevel lvl, std::string_view, const std::string& msg)
{
if (!isConsoleModern)
{

@ -14,7 +14,7 @@ namespace llarp
PreLog(
std::stringstream& s,
LogLevel lvl,
const char* fname,
std::string_view filename,
int lineno,
const std::string& nodename) const override;
@ -24,7 +24,7 @@ namespace llarp
void Tick(llarp_time_t) override{};
void
Print(LogLevel lvl, const char*, const std::string& msg) override;
Print(LogLevel lvl, std::string_view, const std::string& msg) override;
private:
std::ostream& m_Out;

@ -70,6 +70,15 @@ namespace llarp
return str.size() >= suffix.size() && str.substr(str.size() - suffix.size()) == suffix;
}
/// removes a prefix from a string if it exists
inline std::string_view
strip_prefix(std::string_view str, std::string_view prefix)
{
if (starts_with(str, prefix))
return str.substr(prefix.size());
return str;
}
/// Splits a string on some delimiter string and returns a vector of string_view's pointing into
/// the pieces of the original string. The pieces are valid only as long as the original string
/// remains valid. Leading and trailing empty substrings are not removed. If delim is empty you

@ -205,7 +205,7 @@ namespace llarp::vpn
NetSH(std::string commands)
{
commands = NetSHCommand() + " interface IPv6 " + commands;
LogInfo(commands);
LogInfo("exec: ", commands);
::system(commands.c_str());
}
@ -500,7 +500,7 @@ namespace llarp::vpn
void
Execute(std::string cmd) const
{
LogInfo(cmd);
llarp::LogInfo("exec: ", cmd);
::system(cmd.c_str());
}

@ -18,4 +18,4 @@ pybind11_add_module(pyllarp MODULE
)
target_link_libraries(pyllarp PUBLIC liblokinet)
target_include_directories(pyllarp PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
add_log_tag(pyllarp)

@ -53,7 +53,7 @@ add_executable(testAll
target_link_libraries(testAll PUBLIC liblokinet Catch2::Catch2)
target_include_directories(testAll PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
add_log_tag(testAll)
if(WIN32)
target_sources(testAll PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/win32/test.rc")
target_link_libraries(testAll PUBLIC ws2_32 iphlpapi shlwapi)

@ -57,7 +57,7 @@ TEST_CASE("BEncode", "[RouterVersion]")
CHECK(v1235.BEncode(&buf));
std::string s((const char*)buf.begin(), (buf.end() - buf.begin()));
LogInfo("bencoded: ", buf.begin());
llarp::LogInfo("bencoded: ", buf.begin());
CHECK_THAT((const char*)buf.begin(), Equals("li5ei1ei2ei3ee"));
}

Loading…
Cancel
Save