From b8f654044a65f8545b8019a6a5397d5ae511d1f4 Mon Sep 17 00:00:00 2001 From: Volodymyr Kolesnykov Date: Sat, 21 Sep 2024 01:49:28 +0300 Subject: [PATCH] chore: first commit --- .clang-format | 181 ++++++++++++++++++ .clang-tidy | 4 + .gitignore | 2 + CMakeLists.txt | 27 +++ ...elemetry_exporter_syslog_logs-config.cmake | 15 ++ .../syslog_exporter_factory.h | 33 ++++ .../syslog_interface.h | 20 ++ src/CMakeLists.txt | 74 +++++++ src/default_syslog_implementation.h | 13 ++ src/default_syslog_implementation_p.cpp | 18 ++ src/syslog_exporter.cpp | 138 +++++++++++++ src/syslog_exporter.h | 43 +++++ src/syslog_exporter_factory.cpp | 39 ++++ test/CMakeLists.txt | 22 +++ test/test_logger.cpp | 133 +++++++++++++ 15 files changed, 762 insertions(+) create mode 100644 .clang-format create mode 100644 .clang-tidy create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 cmake/opentelemetry_exporter_syslog_logs-config.cmake create mode 100644 include/opentelemetry_exporter_syslog_logs/syslog_exporter_factory.h create mode 100644 include/opentelemetry_exporter_syslog_logs/syslog_interface.h create mode 100644 src/CMakeLists.txt create mode 100644 src/default_syslog_implementation.h create mode 100644 src/default_syslog_implementation_p.cpp create mode 100644 src/syslog_exporter.cpp create mode 100644 src/syslog_exporter.h create mode 100644 src/syslog_exporter_factory.cpp create mode 100644 test/CMakeLists.txt create mode 100644 test/test_logger.cpp diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..855f3ed --- /dev/null +++ b/.clang-format @@ -0,0 +1,181 @@ +--- +Language: Cpp +AccessModifierOffset: -4 +AlignAfterOpenBracket: BlockIndent +AlignArrayOfStructures: None +AlignConsecutiveAssignments: + Enabled: true + AcrossEmptyLines: false + AcrossComments: true + AlignCompound: true + AlignFunctionPointers: false + PadOperators: true +AlignConsecutiveBitFields: + Enabled: true + AcrossEmptyLines: false + AcrossComments: true +AlignConsecutiveDeclarations: false +AlignConsecutiveMacros: AcrossComments +# AlignConsecutiveShortCaseStatements: false +AlignEscapedNewlines: Left +AlignOperands: Align +AlignTrailingComments: + Kind: Always + OverEmptyLines: 0 +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowBreakBeforeNoexceptSpecifier: OnlyWithParen +AllowShortBlocksOnASingleLine: Empty +AllowShortCaseLabelsOnASingleLine: false +AllowShortCompoundRequirementOnASingleLine: true +AllowShortEnumsOnASingleLine: true +AllowShortFunctionsOnASingleLine: Inline +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterReturnType: None +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: true +BinPackParameters: true +BitFieldColonSpacing: Both +BreakBeforeBraces: Custom +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: MultiLine + AfterEnum: false + AfterExternBlock: false + AfterFunction: true + AfterNamespace: false + AfterStruct: false + AfterUnion: false + BeforeCatch: true + BeforeElse: true + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: false +# BracedInitializerIndentWidth: 4 +# BreakAdjacentStringLiterals: false +BreakAfterAttributes: Never +BreakArrays: false +BreakBeforeBinaryOperators: None +BreakBeforeConceptDeclarations: Always +BreakBeforeInlineASMColon: OnlyMultiline +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeColon +BreakInheritanceList: AfterComma +BreakStringLiterals: true +CommentPragmas: '^ NO(LINT|SONAR)' +CompactNamespaces: false +ColumnLimit: 120 +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: true +DisableFormat: false +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '"stdafx\.h"' + Priority: -1 + SortPriority: 0 + CaseSensitive: false + - Regex: '^<[^.]+>' + Priority: 1 + SortPriority: 0 + CaseSensitive: false + - Regex: '^<.*\.h>' + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: '^<.*' + Priority: 3 + SortPriority: 0 + CaseSensitive: false + - Regex: '.*' + Priority: 4 + SortPriority: 0 + CaseSensitive: false +IncludeIsMainRegex: '([-_](test|unittest))?$' +IncludeIsMainSourceRegex: '' +IndentAccessModifiers: false +IndentCaseBlocks: false +IndentCaseLabels: true +IndentExternBlock: AfterExternBlock +IndentGotoLabels: true +IndentPPDirectives: AfterHash +IndentRequiresClause: false +IndentWidth: 4 +IndentWrappedFunctionNames: false +InsertBraces: false +InsertNewlineAtEOF: false +InsertTrailingCommas: None +IntegerLiteralSeparator: + Binary: 4 + BinaryMinDigits: 8 + Decimal: 3 + DecimalMinDigits: 7 + Hex: 4 + HexMinDigits: 8 +KeepEmptyLinesAtTheStartOfBlocks: false +LambdaBodyIndentation: Signature +LineEnding: DeriveLF +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +PPIndentWidth: -1 +PackConstructorInitializers: BinPack +PointerAlignment: Left +QualifierAlignment: Leave +ReferenceAlignment: Pointer +ReflowComments: true +RemoveBracesLLVM: false +RemoveSemicolon: false +RequiresClausePosition: OwnLine +RequiresExpressionIndentation: OuterScope +SeparateDefinitionBlocks: Leave +ShortNamespaceLines: 0 +SortIncludes: CaseSensitive +SortUsingDeclarations: Lexicographic +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: false +SpaceAroundPointerQualifiers: Default +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +# SpaceBeforeJsonColon: false +SpaceBeforeParens: ControlStatementsExceptControlMacros +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: Never +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 +# SpacesInParens: Never +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Latest +StatementAttributeLikeMacros: + - Q_EMIT + - emit +StatementMacros: + - Q_UNUSED +TabWidth: 4 +UseTab: Never diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..203d5ae --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,4 @@ +--- +Checks: 'bugprone-*,cert-*,clang-analyzer-*,concurrency-*,cppcoreguidelines-*,hicpp-*,misc-*,modernize-*,performance-*,portability-*,readability-*,-modernize-use-trailing-return-type,-bugprone-easily-swappable-parameters,-readability-identifier-length,-readability-named-parameter,-hicpp-special-member-functions,-hicpp-use-equals-default,-hicpp-move-const-arg,-hicpp-named-parameter,-hicpp-vararg,-misc-include-cleaner,-cppcoreguidelines-pro-type-union-access,-misc-non-private-member-variables-in-classes,-*-avoid-do-while' +FormatStyle: none +HeaderFilterRegex: '.*' diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6513c6d --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/.vscode/ +/build/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..6730c0c --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 3.25) + +project(opentelemetry_exporter_syslog_logs VERSION 1.0.0 LANGUAGES CXX) +set(CMAKE_VERBOSE_MAKEFILE ON CACHE BOOL "ON") + +option(WITH_TESTING "Whether to enable tests" ON) +option(INSTALL_SYSLOG_EXPORTER "Whether to install the syslog exporter" ON) + +# Logs signal became stable in v1.11.0, see https://github.com/open-telemetry/opentelemetry-cpp/pull/2229, https://github.com/open-telemetry/opentelemetry-cpp/blob/v1.11.0/CHANGELOG.md +find_package(opentelemetry-cpp CONFIG REQUIRED) +if(opentelemetry-cpp_VERSION VERSION_LESS 1.11.0) + message(FATAL_ERROR "opentelemetry-cpp version must be at least 1.11.0, ${opentelemetry-cpp_VERSION} found") +endif() + +add_subdirectory(src) + +if(WITH_TESTING) + include(FindGTest) + find_package(GTest CONFIG COMPONENTS gtest gmock) + if(TARGET GTest::gtest AND TARGET GTest::gmock) + include(CTest) + enable_testing() + add_subdirectory(test) + else() + message(WARNING "GTest not found, tests will not be built") + endif() +endif() diff --git a/cmake/opentelemetry_exporter_syslog_logs-config.cmake b/cmake/opentelemetry_exporter_syslog_logs-config.cmake new file mode 100644 index 0000000..328b4df --- /dev/null +++ b/cmake/opentelemetry_exporter_syslog_logs-config.cmake @@ -0,0 +1,15 @@ +get_filename_component(OTEL_SYSLOG_EXPORTER_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) + +list(APPEND CMAKE_MODULE_PATH ${OTEL_SYSLOG_EXPORTER_CMAKE_DIR}) + +include(CMakeFindDependencyMacro) +find_dependency(opentelemetry-cpp QUIET REQUIRED COMPONENTS logs) +if(opentelemetry-cpp_VERSION VERSION_LESS 1.11.0) + message(FATAL_ERROR "opentelemetry-cpp version must be at least 1.11.0, ${opentelemetry-cpp_VERSION} found") +endif() + +if(NOT TARGET opentelemetry_exporter_syslog_logs::opentelemetry_exporter_syslog_logs) + include("${OTEL_SYSLOG_EXPORTER_CMAKE_DIR}/opentelemetry_exporter_syslog_logs-target.cmake") +endif() + +set(OTEL_SYSLOG_EXPORTER_LIBRARIES opentelemetry_exporter_syslog_logs::opentelemetry_exporter_syslog_logs) diff --git a/include/opentelemetry_exporter_syslog_logs/syslog_exporter_factory.h b/include/opentelemetry_exporter_syslog_logs/syslog_exporter_factory.h new file mode 100644 index 0000000..c59b3bb --- /dev/null +++ b/include/opentelemetry_exporter_syslog_logs/syslog_exporter_factory.h @@ -0,0 +1,33 @@ +#ifndef FD634219_63FB_4E23_BC1D_6DB0BC20B6BE +#define FD634219_63FB_4E23_BC1D_6DB0BC20B6BE + +#include +#include +#include + +#include "opentelemetry_exporter_syslog_logs_export.h" +#include "syslog_interface.h" + +namespace opentelemetry::exporter::logs { + +class OPENTELEMETRY_EXPORTER_SYSLOG_LOGS_EXPORT SyslogLogRecordExporterFactory { +public: + static std::unique_ptr Create(opentelemetry::nostd::string_view ident); + + static std::unique_ptr + Create(opentelemetry::nostd::string_view ident, int option, int facility); + + static std::unique_ptr + Create(opentelemetry::nostd::string_view ident, const std::shared_ptr& syslog); + + static std::unique_ptr Create( + opentelemetry::nostd::string_view ident, const std::shared_ptr& syslog, int option, + int facility + ); + + static void setSyslogImplementation(const std::shared_ptr& syslog); +}; + +} // namespace opentelemetry::exporter::logs + +#endif /* FD634219_63FB_4E23_BC1D_6DB0BC20B6BE */ diff --git a/include/opentelemetry_exporter_syslog_logs/syslog_interface.h b/include/opentelemetry_exporter_syslog_logs/syslog_interface.h new file mode 100644 index 0000000..c636ed6 --- /dev/null +++ b/include/opentelemetry_exporter_syslog_logs/syslog_interface.h @@ -0,0 +1,20 @@ +#ifndef F6CC3A7F_2A2D_4616_B15D_A7739D98F053 +#define F6CC3A7F_2A2D_4616_B15D_A7739D98F053 + +#include +#include "opentelemetry_exporter_syslog_logs_export.h" + +namespace opentelemetry::exporter::logs { + +// NOLINTNEXTLINE(*-special-member-functions) +class OPENTELEMETRY_EXPORTER_SYSLOG_LOGS_EXPORT SyslogInterface { +public: + virtual ~SyslogInterface() = default; + virtual void openlog(opentelemetry::nostd::string_view ident, int option, int facility) = 0; + virtual void syslog(int priority, opentelemetry::nostd::string_view message) = 0; + virtual void closelog() = 0; +}; + +} // namespace opentelemetry::exporter::logs + +#endif /* F6CC3A7F_2A2D_4616_B15D_A7739D98F053 */ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..19f2253 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,74 @@ +include(GenerateExportHeader) + +set(sources + syslog_exporter.cpp + syslog_exporter_factory.cpp + default_syslog_implementation_p.cpp +) + +set(headers + ${CMAKE_CURRENT_BINARY_DIR}/opentelemetry_exporter_syslog_logs_export.h + ${CMAKE_SOURCE_DIR}/include/opentelemetry_exporter_syslog_logs/syslog_exporter_factory.h + ${CMAKE_SOURCE_DIR}/include/opentelemetry_exporter_syslog_logs/syslog_interface.h +) + +add_library(${PROJECT_NAME} ${sources}) +target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_17) +target_link_libraries(${PROJECT_NAME} PUBLIC opentelemetry-cpp::logs) + +target_include_directories(${PROJECT_NAME} + PUBLIC + $ + $ + $ + PRIVATE + ${CMAKE_CURRENT_BINARY_DIR} +) + +set_target_properties( + ${PROJECT_NAME} + PROPERTIES + CXX_EXTENSIONS OFF + CXX_VISIBILITY_PRESET hidden + VISIBILITY_INLINES_HIDDEN ON + POSITION_INDEPENDENT_CODE ON + PUBLIC_HEADER "${headers}" + INTERFACE_COMPILE_FEATURES cxx_std_17 +) + +generate_export_header(${PROJECT_NAME} BASE_NAME ${PROJECT_NAME}) +if(NOT BUILD_SHARED_LIBS) + set_target_properties(${PROJECT_NAME} PROPERTIES COMPILE_FLAGS -DOPENTELEMETRY_EXPORTER_SYSLOG_LOGS_STATIC_DEFINE) +endif() + +if(INSTALL_SYSLOG_EXPORTER) + include(GNUInstallDirs) + include(CMakePackageConfigHelpers) + + install( + TARGETS ${PROJECT_NAME} + EXPORT "${PROJECT_NAME}-target" + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}" + ) + + install(EXPORT "${PROJECT_NAME}-target" + FILE "${PROJECT_NAME}-target.cmake" + NAMESPACE "${PROJECT_NAME}::" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" + ) + + write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake" + VERSION ${PROJECT_VERSION} + COMPATIBILITY AnyNewerVersion + ) + + install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake" + "${CMAKE_SOURCE_DIR}/cmake/${PROJECT_NAME}-config.cmake" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" + ) +endif() \ No newline at end of file diff --git a/src/default_syslog_implementation.h b/src/default_syslog_implementation.h new file mode 100644 index 0000000..3950506 --- /dev/null +++ b/src/default_syslog_implementation.h @@ -0,0 +1,13 @@ +#ifndef D17D59F2_0FDF_4FF4_96EE_244866D19190 +#define D17D59F2_0FDF_4FF4_96EE_244866D19190 + +#include "opentelemetry_exporter_syslog_logs/syslog_interface.h" + +class DefaultSyslogImplementation : public opentelemetry::exporter::logs::SyslogInterface { +public: + void openlog(opentelemetry::nostd::string_view ident, int option, int facility) override; + void syslog(int priority, opentelemetry::nostd::string_view message) override; + void closelog() override; +}; + +#endif /* D17D59F2_0FDF_4FF4_96EE_244866D19190 */ diff --git a/src/default_syslog_implementation_p.cpp b/src/default_syslog_implementation_p.cpp new file mode 100644 index 0000000..e0e4a8a --- /dev/null +++ b/src/default_syslog_implementation_p.cpp @@ -0,0 +1,18 @@ +#include "default_syslog_implementation.h" + +#include + +void DefaultSyslogImplementation::openlog(opentelemetry::nostd::string_view ident, int option, int facility) +{ + ::openlog(ident.data(), option, facility); +} + +void DefaultSyslogImplementation::syslog(int priority, opentelemetry::nostd::string_view message) +{ + ::syslog(priority, "%s", message.data()); // NOLINT(*-type-vararg) +} + +void DefaultSyslogImplementation::closelog() +{ + ::closelog(); +} diff --git a/src/syslog_exporter.cpp b/src/syslog_exporter.cpp new file mode 100644 index 0000000..81e3883 --- /dev/null +++ b/src/syslog_exporter.cpp @@ -0,0 +1,138 @@ +#include "syslog_exporter.h" + +#include +#include + +#include +#include +#include + +#include "default_syslog_implementation.h" + +namespace { + +const std::array> priority_map{ + LOG_ERR, // INVALID + LOG_DEBUG, LOG_DEBUG, LOG_DEBUG, LOG_DEBUG, // TRACE, TRACE2, TRACE3, TRACE4 + LOG_DEBUG, LOG_DEBUG, LOG_DEBUG, LOG_DEBUG, // DEBUG, DEBUG2, DEBUG3, DEBUG4 + LOG_INFO, LOG_INFO, LOG_NOTICE, LOG_NOTICE, // INFO, INFO2, INFO3, INFO4 + LOG_WARNING, LOG_WARNING, LOG_WARNING, LOG_WARNING, // WARN, WARN2, WARN3, WARN4 + LOG_ERR, LOG_ERR, LOG_CRIT, LOG_ALERT, // ERROR, ERROR2, ERROR3, ERROR4 + LOG_EMERG, LOG_EMERG, LOG_EMERG, LOG_EMERG // FATAL, FATAL2, FATAL3, FATAL4 +}; + +std::string stringify_attribute(const opentelemetry::common::AttributeValue& v) +{ + using namespace opentelemetry::common; + + return opentelemetry::nostd::visit( + [](auto&& arg) -> std::string { + using T = std::decay_t; + if constexpr (std::is_same_v) { + return arg ? "true" : "false"; + } + + if constexpr (std::is_same_v || std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v) + { + return std::to_string(arg); + } + + if constexpr (std::is_same_v) { + return arg; + } + + if constexpr (std::is_same_v) { + return std::string(arg.data(), arg.size()); + } + + return ""; + }, + v + ); +} + +} // namespace + +std::shared_ptr + opentelemetry::exporter::logs::SyslogLogRecordExporter::syslog{nullptr}; + +opentelemetry::exporter::logs::SyslogLogRecordExporter::SyslogLogRecordExporter( + opentelemetry::nostd::string_view ident, int option, int facility +) noexcept +{ + if (!SyslogLogRecordExporter::syslog) { + SyslogLogRecordExporter::syslog = std::make_shared(); + } + + SyslogLogRecordExporter::syslog->openlog(ident.data(), option, facility); +} + +opentelemetry::exporter::logs::SyslogLogRecordExporter::~SyslogLogRecordExporter() +{ + SyslogLogRecordExporter::syslog->closelog(); +} + +std::unique_ptr +opentelemetry::exporter::logs::SyslogLogRecordExporter::MakeRecordable() noexcept +{ + return std::make_unique(); +} + +// NOLINTNEXTLINE(bugprone-exception-escape) +opentelemetry::sdk::common::ExportResult opentelemetry::exporter::logs::SyslogLogRecordExporter::Export( + const opentelemetry::nostd::span>& records +) noexcept +{ + if (this->is_shutdown.load()) { + return opentelemetry::sdk::common::ExportResult::kFailure; + } + + for (auto& record : records) { + auto log_record = std::unique_ptr( + // NOLINTNEXTLINE(*-type-static-cast-downcast) + static_cast(record.release()) + ); + + if (log_record == nullptr) { + continue; + } + + auto severity_index = static_cast(log_record->GetSeverity()); + if (severity_index >= priority_map.size()) { + severity_index = 0; + } + + auto priority = priority_map[severity_index]; // NOLINT(*-bounds-constant-array-index) + const auto name = log_record->GetInstrumentationScope().GetName(); + const auto body = stringify_attribute(log_record->GetBody()); + const auto msg = std::string("[").append(name).append("] ").append(body); + + SyslogLogRecordExporter::syslog->syslog(priority, msg); + } + + return opentelemetry::sdk::common::ExportResult::kSuccess; +} + +bool opentelemetry::exporter::logs::SyslogLogRecordExporter::ForceFlush(std::chrono::microseconds) noexcept +{ + return true; +} + +bool opentelemetry::exporter::logs::SyslogLogRecordExporter::Shutdown(std::chrono::microseconds) noexcept +{ + this->is_shutdown.store(true); + return true; +} + +void opentelemetry::exporter::logs::SyslogLogRecordExporter::setSyslogImplementation( + const std::shared_ptr& impl +) +{ + if (!impl) { + SyslogLogRecordExporter::syslog = std::make_shared(); + } + else { + SyslogLogRecordExporter::syslog = impl; + } +} diff --git a/src/syslog_exporter.h b/src/syslog_exporter.h new file mode 100644 index 0000000..2b4c7ec --- /dev/null +++ b/src/syslog_exporter.h @@ -0,0 +1,43 @@ +#ifndef BB1766CD_2D39_4A14_83BC_75E2A0AE3585 +#define BB1766CD_2D39_4A14_83BC_75E2A0AE3585 + +#include +#include + +#include + +#include +#include + +#include "opentelemetry_exporter_syslog_logs/syslog_interface.h" + +namespace opentelemetry::exporter::logs { + +// NOLINTNEXTLINE(*-special-member-functions) +class SyslogLogRecordExporter final : public opentelemetry::sdk::logs::LogRecordExporter { +public: + explicit SyslogLogRecordExporter( + opentelemetry::nostd::string_view ident, int option = LOG_CONS | LOG_PID, int facility = LOG_USER + ) noexcept; + + ~SyslogLogRecordExporter() override; + + std::unique_ptr MakeRecordable() noexcept override; + + opentelemetry::sdk::common::ExportResult + Export(const opentelemetry::nostd::span>& records + ) noexcept override; + + bool ForceFlush(std::chrono::microseconds timeout = (std::chrono::microseconds::max)()) noexcept override; + bool Shutdown(std::chrono::microseconds timeout = (std::chrono::microseconds::max)()) noexcept override; + + static void setSyslogImplementation(const std::shared_ptr& impl); + +private: + std::atomic is_shutdown{false}; + static std::shared_ptr syslog; +}; + +} // namespace opentelemetry::exporter::logs + +#endif /* BB1766CD_2D39_4A14_83BC_75E2A0AE3585 */ diff --git a/src/syslog_exporter_factory.cpp b/src/syslog_exporter_factory.cpp new file mode 100644 index 0000000..77388de --- /dev/null +++ b/src/syslog_exporter_factory.cpp @@ -0,0 +1,39 @@ +#include "opentelemetry_exporter_syslog_logs/syslog_exporter_factory.h" +#include "syslog_exporter.h" + +namespace opentelemetry::exporter::logs { + +std::unique_ptr +SyslogLogRecordExporterFactory::Create(opentelemetry::nostd::string_view ident) +{ + return std::make_unique(ident); +} + +std::unique_ptr +SyslogLogRecordExporterFactory::Create(opentelemetry::nostd::string_view ident, int option, int facility) +{ + return std::make_unique(ident, option, facility); +} + +std::unique_ptr SyslogLogRecordExporterFactory::Create( + opentelemetry::nostd::string_view ident, const std::shared_ptr& syslog +) +{ + SyslogLogRecordExporter::setSyslogImplementation(syslog); + return std::make_unique(ident); +} + +std::unique_ptr SyslogLogRecordExporterFactory::Create( + opentelemetry::nostd::string_view ident, const std::shared_ptr& syslog, int option, int facility +) +{ + SyslogLogRecordExporter::setSyslogImplementation(syslog); + return std::make_unique(ident, option, facility); +} + +void SyslogLogRecordExporterFactory::setSyslogImplementation(const std::shared_ptr& syslog) +{ + SyslogLogRecordExporter::setSyslogImplementation(syslog); +} + +} // namespace opentelemetry::exporter::logs diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..c9cb3dc --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,22 @@ +add_executable(test_logger) +target_sources(test_logger PRIVATE test_logger.cpp) + +## To test packag installation +# find_package(opentelemetry_exporter_syslog_logs CONFIG REQUIRED) +# target_link_libraries(test_logger PRIVATE GTest::gtest GTest::gmock GTest::gtest_main opentelemetry_exporter_syslog_logs::opentelemetry_exporter_syslog_logs) + +## Normal case +target_link_libraries(test_logger PRIVATE GTest::gtest GTest::gmock GTest::gtest_main ${PROJECT_NAME}) + +set_target_properties( + test_logger + PROPERTIES + CXX_STANDARD 17 + CXX_STANDARD_REQUIRED ON + CXX_EXTENSIONS OFF + CXX_VISIBILITY_PRESET hidden + VISIBILITY_INLINES_HIDDEN ON +) + +include(GoogleTest) +gtest_discover_tests(test_logger) diff --git a/test/test_logger.cpp b/test/test_logger.cpp new file mode 100644 index 0000000..aecbddf --- /dev/null +++ b/test/test_logger.cpp @@ -0,0 +1,133 @@ +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include + +#include "opentelemetry_exporter_syslog_logs/syslog_exporter_factory.h" +#include "opentelemetry_exporter_syslog_logs/syslog_interface.h" + +namespace { + +class MockedSyslog : public opentelemetry::exporter::logs::SyslogInterface { +public: + MOCK_METHOD(void, openlog, (opentelemetry::nostd::string_view ident, int option, int facility), (override)); + MOCK_METHOD(void, syslog, (int priority, opentelemetry::nostd::string_view message), (override)); + MOCK_METHOD(void, closelog, (), (override)); +}; + +class SyslogExporterTest : public testing::Test { +protected: + void SetUp() override + { + const opentelemetry::nostd::string_view ident = "name"; + const int option = LOG_CONS | LOG_PID; + const int facility = LOG_LOCAL0; + + EXPECT_CALL(*this->m_syslog, openlog(testing::Eq(ident), testing::Eq(option), testing::Eq(facility))) + .Times(testing::Exactly(1)); + + auto exporter = opentelemetry::exporter::logs::SyslogLogRecordExporterFactory::Create( + ident, std::static_pointer_cast(this->m_syslog), option, + facility + ); + + auto processor = std::make_unique(std::move(exporter)); + auto provider = std::make_shared(std::move(processor)); + opentelemetry::logs::Provider::SetLoggerProvider( + std::static_pointer_cast(provider) + ); + } + + void TearDown() override + { + EXPECT_CALL(*this->m_syslog, closelog()).Times(testing::Exactly(1)); + + const std::shared_ptr provider; + opentelemetry::logs::Provider::SetLoggerProvider(provider); + + testing::Mock::VerifyAndClearExpectations(this->m_syslog.get()); + + opentelemetry::exporter::logs::SyslogLogRecordExporterFactory::setSyslogImplementation(nullptr); + } + + // NOLINTNEXTLINE(*-non-private-member-variables-in-classes) + std::shared_ptr m_syslog{std::make_shared()}; +}; + +class ParametrizedSyslogExporterTest + : public SyslogExporterTest, + public testing::WithParamInterface> {}; + +TEST_F(SyslogExporterTest, BasicTest) +{ + const opentelemetry::nostd::string_view name = "somelib"; + const opentelemetry::nostd::string_view msg = "test message"; + const std::string expected = "[" + std::string(name) + "] " + std::string(msg); + + EXPECT_CALL(*this->m_syslog, syslog(testing::Eq(LOG_INFO), testing::Eq(expected))).Times(testing::Exactly(1)); + + auto logger = opentelemetry::logs::Provider::GetLoggerProvider()->GetLogger("logger name", name); + logger->Info("test message"); +} + +TEST_P(ParametrizedSyslogExporterTest, MappingTest) +{ + opentelemetry::logs::Severity severity; // NOLINT(*-init-variables) + int priority; // NOLINT(*-init-variables) + std::tie(severity, priority) = GetParam(); + + const opentelemetry::nostd::string_view name = "somelib"; + const opentelemetry::nostd::string_view msg = "test message"; + + EXPECT_CALL(*this->m_syslog, syslog(testing::Eq(priority), testing::HasSubstr(msg))).Times(testing::Exactly(1)); + + auto logger = opentelemetry::logs::Provider::GetLoggerProvider()->GetLogger("logger name", name); + logger->Log(severity, "test message"); +} + +INSTANTIATE_TEST_SUITE_P( + Table, ParametrizedSyslogExporterTest, + testing::Values( + std::make_tuple(opentelemetry::logs::Severity::kInvalid, LOG_ERR), + + std::make_tuple(opentelemetry::logs::Severity::kTrace, LOG_DEBUG), + std::make_tuple(opentelemetry::logs::Severity::kTrace2, LOG_DEBUG), + std::make_tuple(opentelemetry::logs::Severity::kTrace3, LOG_DEBUG), + std::make_tuple(opentelemetry::logs::Severity::kTrace4, LOG_DEBUG), + + std::make_tuple(opentelemetry::logs::Severity::kDebug, LOG_DEBUG), + std::make_tuple(opentelemetry::logs::Severity::kDebug2, LOG_DEBUG), + std::make_tuple(opentelemetry::logs::Severity::kDebug3, LOG_DEBUG), + std::make_tuple(opentelemetry::logs::Severity::kDebug4, LOG_DEBUG), + + std::make_tuple(opentelemetry::logs::Severity::kInfo, LOG_INFO), + std::make_tuple(opentelemetry::logs::Severity::kInfo2, LOG_INFO), + std::make_tuple(opentelemetry::logs::Severity::kInfo3, LOG_NOTICE), + std::make_tuple(opentelemetry::logs::Severity::kInfo4, LOG_NOTICE), + + std::make_tuple(opentelemetry::logs::Severity::kWarn, LOG_WARNING), + std::make_tuple(opentelemetry::logs::Severity::kWarn2, LOG_WARNING), + std::make_tuple(opentelemetry::logs::Severity::kWarn3, LOG_WARNING), + std::make_tuple(opentelemetry::logs::Severity::kWarn4, LOG_WARNING), + + std::make_tuple(opentelemetry::logs::Severity::kError, LOG_ERR), + std::make_tuple(opentelemetry::logs::Severity::kError2, LOG_ERR), + std::make_tuple(opentelemetry::logs::Severity::kError3, LOG_CRIT), + std::make_tuple(opentelemetry::logs::Severity::kError4, LOG_ALERT), + + std::make_tuple(opentelemetry::logs::Severity::kFatal, LOG_EMERG), + std::make_tuple(opentelemetry::logs::Severity::kFatal2, LOG_EMERG), + std::make_tuple(opentelemetry::logs::Severity::kFatal3, LOG_EMERG), + std::make_tuple(opentelemetry::logs::Severity::kFatal4, LOG_EMERG) + ) +); + +} // namespace