From f70307964f49144a4663630697a7f34ad649bd30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kjell=20Hedstr=C3=B6m=2E=20We=20are=20hiring=20=40=20Ganaz?= Date: Sun, 20 Mar 2022 02:16:12 -0600 Subject: [PATCH] c++17 replaces boost dependency (#124) * thanks to xgdgsc, for https://github.com/KjellKod/g3sinks/pull/104/files * commenting out or removing boost for testing with pure std::cpp17 library std::filesystem * big cleanup, some refactoring of redundant code, improved readability * clang-formatting, sublime formatting file added. --- .clang-format | 212 +++ .github/workflows/buildAndRunTests.yml | 4 +- .sublime_formatting | 28 + .vscode/configurationCache.log | 1 + .vscode/dryrun.log | 21 + .vscode/launch.json | 20 + .vscode/settings.json | 75 + .vscode/targets.log | 1299 +++++++++++++++++ .vscode/task.json | 28 + CMakeLists.txt | 6 +- README.md | 10 +- Version.cmake | 8 +- sink_logrotate/CMakeLists.txt | 12 - sink_logrotate/src/LogRotate.cpp | 34 +- sink_logrotate/src/LogRotateHelper.ipp | 29 +- sink_logrotate/src/LogRotateUtility.cpp | 191 +-- sink_logrotate/src/LogRotateWithFilter.cpp | 41 +- sink_logrotate/src/g3sinks/LogRotate.h | 42 +- sink_logrotate/src/g3sinks/LogRotateUtility.h | 17 +- .../src/g3sinks/LogRotateWithFilter.h | 36 +- test/FilterTest.cpp | 262 ++-- test/FilterTest.h | 31 +- test/RotateFileTest.cpp | 113 +- test/RotateFileTest.h | 34 +- test/RotateTestHelper.cpp | 29 +- test/RotateTestHelper.h | 11 +- 26 files changed, 2144 insertions(+), 450 deletions(-) create mode 100644 .clang-format create mode 100644 .sublime_formatting create mode 100644 .vscode/configurationCache.log create mode 100644 .vscode/dryrun.log create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 .vscode/targets.log create mode 100644 .vscode/task.json diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..d4d0a55 --- /dev/null +++ b/.clang-format @@ -0,0 +1,212 @@ +--- +Language: Cpp +# BasedOnStyle: Google +AccessModifierOffset: -1 +AlignAfterOpenBracket: Align +AlignArrayOfStructures: None +AlignConsecutiveMacros: None +AlignConsecutiveAssignments: None +AlignConsecutiveBitFields: None +AlignConsecutiveDeclarations: None +AlignEscapedNewlines: Left +AlignOperands: Align +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllConstructorInitializersOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortEnumsOnASingleLine: true +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortLambdasOnASingleLine: All +AllowShortIfStatementsOnASingleLine: WithoutElse +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: Yes +AttributeMacros: + - __capability +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeConceptDeclarations: true +BreakBeforeBraces: Attach +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 120 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DeriveLineEnding: true +DerivePointerAlignment: false +DisableFormat: false +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IfMacros: + - KJ_IF_MAYBE +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^' + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: '^<.*\.h>' + Priority: 1 + SortPriority: 0 + CaseSensitive: false + - Regex: '^<.*' + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: '.*' + Priority: 3 + SortPriority: 0 + CaseSensitive: false +IncludeIsMainRegex: '([-_](test|unittest))?$' +IncludeIsMainSourceRegex: '' +IndentAccessModifiers: false +IndentCaseLabels: true +IndentCaseBlocks: false +IndentGotoLabels: true +IndentPPDirectives: None +IndentExternBlock: AfterExternBlock +IndentRequires: false +IndentWidth: 3 +IndentWrappedFunctionNames: false +InsertTrailingCommas: None +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +LambdaBodyIndentation: Signature +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: All +ObjCBinPackProtocolList: Never +ObjCBlockIndentWidth: 2 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 200 +PenaltyIndentedWhitespace: 0 +PointerAlignment: Left +PPIndentWidth: -1 +RawStringFormats: + - Language: Cpp + Delimiters: + - cc + - CC + - cpp + - Cpp + - CPP + - 'c++' + - 'C++' + CanonicalDelimiter: '' + BasedOnStyle: google + - Language: TextProto + Delimiters: + - pb + - PB + - proto + - PROTO + EnclosingFunctions: + - EqualsProto + - EquivToProto + - PARSE_PARTIAL_TEXT_PROTO + - PARSE_TEST_PROTO + - PARSE_TEXT_PROTO + - ParseTextOrDie + - ParseTextProtoOrDie + - ParseTestProto + - ParsePartialTestProto + CanonicalDelimiter: pb + BasedOnStyle: google +ReferenceAlignment: Pointer +ReflowComments: true +ShortNamespaceLines: 1 +SortIncludes: CaseSensitive +SortJavaStaticImport: Before +SortUsingDeclarations: true +SpaceAfterCStyleCast: true +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceAroundPointerQualifiers: Default +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: Never +SpacesInConditionalStatement: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 +SpacesInParentheses: false +SpacesInSquareBrackets: false +SpaceBeforeSquareBrackets: false +BitFieldColonSpacing: Both +Standard: Auto +StatementAttributeLikeMacros: + - Q_EMIT +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 8 +UseCRLF: false +UseTab: Never +WhitespaceSensitiveMacros: + - STRINGIZE + - PP_STRINGIZE + - BOOST_PP_STRINGIZE + - NS_SWIFT_NAME + - CF_SWIFT_NAME +... + diff --git a/.github/workflows/buildAndRunTests.yml b/.github/workflows/buildAndRunTests.yml index 25dad0e..b19e45a 100644 --- a/.github/workflows/buildAndRunTests.yml +++ b/.github/workflows/buildAndRunTests.yml @@ -34,7 +34,7 @@ jobs: run: | echo "MacOS Latest" brew update --preinstall - brew install boost + - name: Linux Bootstrap @@ -46,7 +46,7 @@ jobs: sudo apt-get update sudo apt-get install ninja-build cmake gcc-9 g++-9 sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 90 --slave /usr/bin/g++ g++ /usr/bin/g++-9 --slave /usr/bin/gcov gcov /usr/bin/gcov-9 - sudo apt-get install -y unzip zlib1g-dev libboost-all-dev libc6-dev + sudo apt-get install -y unzip zlib1g-dev libc6-dev echo gcc version after gcc --version cmake --version diff --git a/.sublime_formatting b/.sublime_formatting new file mode 100644 index 0000000..4722bc3 --- /dev/null +++ b/.sublime_formatting @@ -0,0 +1,28 @@ +{ + "ignored_packages": + [ + "Vintage", + ], + "AStyleFormatter": + { + "options_default": + { + "indent": "spaces", + "indent-modifiers": false, + "indent-namespaces": true, + "indent-preproc-block": true, + "indent-spaces": 3, + //"style": "googles" + } + }, + //"color_scheme": "Packages/Color Scheme - Default/Twilight.tmTheme", + "font_size": 11, + "highlight_modified_tabs": true, + "ignored_packages": + [ + "Vintage" + ], + "tab_size": 3, + "translate_tabs_to_spaces": true, + "word_wrap": true +} diff --git a/.vscode/configurationCache.log b/.vscode/configurationCache.log new file mode 100644 index 0000000..d9e3dd6 --- /dev/null +++ b/.vscode/configurationCache.log @@ -0,0 +1 @@ +{"buildTargets":[".PHONY",".SILENT",".SUFFIXES","Continuous","Continuous/fast","ContinuousBuild","ContinuousBuild/fast","ContinuousConfigure","ContinuousConfigure/fast","ContinuousCoverage","ContinuousCoverage/fast","ContinuousMemCheck","ContinuousMemCheck/fast","ContinuousStart","ContinuousStart/fast","ContinuousSubmit","ContinuousSubmit/fast","ContinuousTest","ContinuousTest/fast","ContinuousUpdate","ContinuousUpdate/fast","Experimental","Experimental/fast","ExperimentalBuild","ExperimentalBuild/fast","ExperimentalConfigure","ExperimentalConfigure/fast","ExperimentalCoverage","ExperimentalCoverage/fast","ExperimentalMemCheck","ExperimentalMemCheck/fast","ExperimentalStart","ExperimentalStart/fast","ExperimentalSubmit","ExperimentalSubmit/fast","ExperimentalTest","ExperimentalTest/fast","ExperimentalUpdate","ExperimentalUpdate/fast","Nightly","Nightly/fast","NightlyBuild","NightlyBuild/fast","NightlyConfigure","NightlyConfigure/fast","NightlyCoverage","NightlyCoverage/fast","NightlyMemCheck","NightlyMemCheck/fast","NightlyMemoryCheck","NightlyMemoryCheck/fast","NightlyStart","NightlyStart/fast","NightlySubmit","NightlySubmit/fast","NightlyTest","NightlyTest/fast","NightlyUpdate","NightlyUpdate/fast","all","clean","clean-cmake","clean-cmake/fast","clean/fast","cmake_check_build_system","cmake_force","default_target","depend","edit_cache","edit_cache/fast","example_coloredcout","example_coloredcout/fast","example_filedescriptor","example_filedescriptor/fast","example_logrotate_and_filter","example_logrotate_and_filter/fast","example_syslog","example_syslog/fast","g3logrotate","g3logrotate/fast","g3syslog","g3syslog/fast","gmock","gmock/fast","gmock_main","gmock_main/fast","gtest","gtest/fast","gtest_main","gtest_main/fast","help","install","install/fast","install/local","install/local/fast","install/strip","install/strip/fast","list_install_components","list_install_components/fast","package","package/fast","package_source","package_source/fast","preinstall","preinstall/fast","rebuild_cache","rebuild_cache/fast","test","test/fast","test_logrotate","test_logrotate/fast"],"launchTargets":[],"customConfigurationProvider":{"workspaceBrowse":{"browsePath":[],"compilerArgs":[]},"fileIndex":[]}} \ No newline at end of file diff --git a/.vscode/dryrun.log b/.vscode/dryrun.log new file mode 100644 index 0000000..e4a4710 --- /dev/null +++ b/.vscode/dryrun.log @@ -0,0 +1,21 @@ +make -f /Users/kjell/ws/test/g3sinks/build/Makefile --dry-run --keep-going --print-directory +make: Entering directory `/Users/kjell/ws/test/g3sinks' +/usr/local/Cellar/cmake/3.21.4/bin/cmake -S/Users/kjell/ws/test/g3sinks -B/Users/kjell/ws/test/g3sinks/build --check-build-system CMakeFiles/Makefile.cmake 0 +/usr/local/Cellar/cmake/3.21.4/bin/cmake -E cmake_progress_start /Users/kjell/ws/test/g3sinks/build/CMakeFiles /Users/kjell/ws/test/g3sinks/build//CMakeFiles/progress.marks +/Applications/Xcode.app/Contents/Developer/usr/bin/make -s -f CMakeFiles/Makefile2 all + +make[1]: Entering directory `/Users/kjell/ws/test/g3sinks' + +make[1]: CMakeFiles/Makefile2: No such file or directory + +make[1]: *** No rule to make target `CMakeFiles/Makefile2'. +make[1]: Failed to remake makefile `CMakeFiles/Makefile2'. + +make[1]: *** No rule to make target `all'. + +make[1]: Leaving directory `/Users/kjell/ws/test/g3sinks' + +make: *** [all] Error 2 + +make: Leaving directory `/Users/kjell/ws/test/g3sinks' + diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..3f305ae --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,20 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "(lldb) Launch", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/build/test/test_logrotate", + "args": ["--gtest_filter=*getDateFromFileName"], + "stopAtEntry": false, + "cwd": "${fileDirname}", + "environment": [], + "externalConsole": false, + "MIMode": "lldb" + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..9d24e86 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,75 @@ +{ + "files.associations": { + "regex": "cpp", + "string": "cpp", + "__bit_reference": "cpp", + "__bits": "cpp", + "__config": "cpp", + "__debug": "cpp", + "__errc": "cpp", + "__functional_base": "cpp", + "__locale": "cpp", + "__mutex_base": "cpp", + "__node_handle": "cpp", + "__nullptr": "cpp", + "__split_buffer": "cpp", + "__string": "cpp", + "__threading_support": "cpp", + "__tree": "cpp", + "__tuple": "cpp", + "algorithm": "cpp", + "any": "cpp", + "array": "cpp", + "atomic": "cpp", + "bit": "cpp", + "bitset": "cpp", + "cctype": "cpp", + "chrono": "cpp", + "clocale": "cpp", + "condition_variable": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "deque": "cpp", + "exception": "cpp", + "fstream": "cpp", + "functional": "cpp", + "future": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "ios": "cpp", + "iosfwd": "cpp", + "iostream": "cpp", + "istream": "cpp", + "iterator": "cpp", + "limits": "cpp", + "locale": "cpp", + "map": "cpp", + "memory": "cpp", + "mutex": "cpp", + "new": "cpp", + "optional": "cpp", + "ostream": "cpp", + "ratio": "cpp", + "set": "cpp", + "sstream": "cpp", + "stack": "cpp", + "stdexcept": "cpp", + "streambuf": "cpp", + "string_view": "cpp", + "system_error": "cpp", + "thread": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "typeinfo": "cpp", + "utility": "cpp", + "variant": "cpp", + "vector": "cpp" + } +} \ No newline at end of file diff --git a/.vscode/targets.log b/.vscode/targets.log new file mode 100644 index 0000000..55d3c91 --- /dev/null +++ b/.vscode/targets.log @@ -0,0 +1,1299 @@ +make all -f /Users/kjell/ws/test/g3sinks/build/Makefile --print-data-base --no-builtin-variables --no-builtin-rules --question +# GNU Make 3.81 +# Copyright (C) 2006 Free Software Foundation, Inc. +# This is free software; see the source for copying conditions. +# There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +# This program built for i386-apple-darwin11.3.0 + + +# Make data base, printed on Sat Mar 19 20:18:38 2022 + +# Variables + +# automatic + -G3sinks requires [boost](http://www.boost.org/doc/libs/1_63_0/index.html) to be installed. -For installing boost please follow their [instructions](http://www.boost.org/doc/libs/1_63_0/more/getting_started/unix-variants.html). - -The boost install location `BOOST_ROOT` or `Boost_INCLUDE_DIR` has to be specified during the cmake build step. -In the example below the boost installation -location is `/usr/local` - **ZLIB**
The ZLIB library must be installed for the logrotate to be able to compress the old log files in Ubuntu it can be installed with `sudo apt-get install zlib1g-dev`. Please see your specific platform for details or go to the [zlib page](http://www.zlib.net/) @@ -88,7 +80,7 @@ in Ubuntu it can be installed with `sudo apt-get install zlib1g-dev`. Please see cd g3sinks mkdir build cd build -cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=/usr/local/ -DBOOST_ROOT=/usr/local -DBUILD_TEST=ON .. +cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=/usr/local/ -DBUILD_TEST=ON .. make -j ``` diff --git a/Version.cmake b/Version.cmake index df0e897..786c898 100644 --- a/Version.cmake +++ b/Version.cmake @@ -12,10 +12,10 @@ if (${VERSION}.x STREQUAL ".x") endif() execute_process(COMMAND bash "-c" "git rev-list --branches HEAD | wc -l | tr -d ' ' | tr -d '\n'" OUTPUT_VARIABLE GIT_VERSION WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) endif() - # TODO: Kjell, remove this after evaluation - math(EXPR VERSION-BASE ${GIT_VERSION}/255) - math(EXPR VERSION-REMAINDER ${GIT_VERSION}%255) + MESSAGE("VERSION SO FAR ${GIT_VERSION}") + math(EXPR VERSION-BASE ${GIT_VERSION}/255) # TODO the math lines can just be commented out when debugging in VSCode. + math(EXPR VERSION-REMAINDER ${GIT_VERSION}%255) # as the GIT_VERSION can not easily be retrieved in VS SET(BUILD_NUMBER ${VERSION-BASE}) SET(VERSION ${MAJOR_VERSION}.${MINOR_VERSION}.${BUILD_NUMBER}-${VERSION-REMAINDER}) endif() -message("Software Version: ${VERSION}") +message("Software Version: ${VERSION}") \ No newline at end of file diff --git a/sink_logrotate/CMakeLists.txt b/sink_logrotate/CMakeLists.txt index eedb62e..4e3645e 100644 --- a/sink_logrotate/CMakeLists.txt +++ b/sink_logrotate/CMakeLists.txt @@ -1,13 +1,5 @@ project(logrotate) -# =============================== -# boost filesystem + boost system -set( Boost_USE_STATIC_LIBS ON ) -set( Boost_USE_MULTITHREAD ON ) -find_package( Boost REQUIRED COMPONENTS filesystem system ) -message( "boost [filesystem, system] found: ${Boost_FOUND} ") -include_directories( ${Boost_INCLUDE_DIRS} ) - # zlib find_package( ZLIB REQUIRED ) @@ -35,15 +27,11 @@ include_directories(${LOGROTATE_SRC}) target_link_libraries( g3logrotate PUBLIC ${G3LOG_LIBRARY} - #${Boost_LIBRARIES} - PRIVATE Boost::filesystem - PRIVATE Boost::system PRIVATE ${ZLIB_LIBRARY} ) message( "target_link_libraries: g3log: ${G3LOG_LIBRARY}, - boost: ${Boost_LIBRARIES}, zlib: ${ZLIB_LIBRARIES}" ) diff --git a/sink_logrotate/src/LogRotate.cpp b/sink_logrotate/src/LogRotate.cpp index 181d623..f6b7cc3 100644 --- a/sink_logrotate/src/LogRotate.cpp +++ b/sink_logrotate/src/LogRotate.cpp @@ -20,24 +20,24 @@ LogRotate::LogRotate(const std::string& log_prefix, const std::string& log_direc LogRotate::~LogRotate() { - std::cerr << "\nExiting, log location: " << pimpl_->log_file_with_path_ << std::endl; + std::cerr << "\nExiting, log location: " << pimpl_->log_file_with_path_ << std::endl; } /// @param logEntry to write to file void LogRotate::save(std::string logEntry) { - pimpl_->fileWrite(logEntry); + pimpl_->fileWrite(logEntry); } /// Attempt to change the current log file to another name/location. /// @return filename with full path if successful, else empty string std::string LogRotate::changeLogFile(const std::string& log_directory, const std::string& new_name) { - return pimpl_->changeLogFile(log_directory, new_name); + return pimpl_->changeLogFile(log_directory, new_name); } /// @return the current file name to write to std::string LogRotate::logFileName() { - return pimpl_->logFileName(); + return pimpl_->logFileName(); } /** @@ -45,8 +45,8 @@ std::string LogRotate::logFileName() { * @param max_size */ void LogRotate::setMaxArchiveLogCount(int max_size) { - pimpl_->setMaxArchiveLogCount(max_size); - } + pimpl_->setMaxArchiveLogCount(max_size); +} int LogRotate::getMaxArchiveLogCount() { @@ -54,16 +54,16 @@ int LogRotate::getMaxArchiveLogCount() { } /** -* Flush policy: Default is every single time (i.e. policy of 1). +* Flush policy: Default is every single time (i.e. policy of 1). * * If the system logs A LOT then it is likely better to allow for the system to buffer and write -* all the entries at once. -* +* all the entries at once. +* * 0: System decides, potentially very long time -* 1....N: Flush logs every n entry +* 1....N: Flush logs every n entry */ -void LogRotate::setFlushPolicy(size_t flush_policy){ - pimpl_->setFlushPolicy(flush_policy); +void LogRotate::setFlushPolicy(size_t flush_policy) { + pimpl_->setFlushPolicy(flush_policy); } /** @@ -71,8 +71,8 @@ void LogRotate::setFlushPolicy(size_t flush_policy){ * but is great for unit testing and if there are special circumstances where you want to see * the logs faster than the flush_policy */ -void LogRotate::flush(){ - pimpl_->flush(); +void LogRotate::flush() { + pimpl_->flush(); } @@ -82,13 +82,13 @@ void LogRotate::flush(){ * @param max_file_size */ void LogRotate::setMaxLogSize(int max_file_size) { - pimpl_->setMaxLogSize(max_file_size); + pimpl_->setMaxLogSize(max_file_size); } int LogRotate::getMaxLogSize() { return pimpl_->getMaxLogSize(); } -bool LogRotate::rotateLog(){ - return pimpl_->rotateLog(); +bool LogRotate::rotateLog() { + return pimpl_->rotateLog(); } \ No newline at end of file diff --git a/sink_logrotate/src/LogRotateHelper.ipp b/sink_logrotate/src/LogRotateHelper.ipp index 178d14f..f7eda97 100644 --- a/sink_logrotate/src/LogRotateHelper.ipp +++ b/sink_logrotate/src/LogRotateHelper.ipp @@ -27,6 +27,8 @@ #include #include #include +#include + #include "g3sinks/LogRotateUtility.h" @@ -99,9 +101,8 @@ LogRotateHelper::LogRotateHelper(const std::string& log_prefix, const std::strin std::cerr << "g3log: forced abort due to illegal log prefix [" << log_prefix << "]" << std::endl; abort(); } - auto logfile = changeLogFile(log_directory, log_prefix_backup_); - assert((nullptr != outptr_) && "cannot open log file at startup"); + assert((nullptr != outptr_) && "bad directory or file path, cannot open log file at startup"); } /** @@ -184,7 +185,16 @@ std::string LogRotateHelper::changeLogFile(const std::string& directory, const s file_name = log_prefix_backup_; } - auto prospect_log = createPath(directory, file_name); + + auto prospect_path = sanityFixPath(directory); + auto prospect_log = createPathToFile(prospect_path, file_name); + + namespace fs = std::filesystem; + fs::path log_dir_path = fs::path(prospect_path); + if (!fs::exists(log_dir_path)) { + fs::create_directories(log_dir_path); + } + prospect_log = addLogSuffix(prospect_log); std::unique_ptr log_stream = createLogFile(prospect_log); @@ -209,6 +219,13 @@ std::string LogRotateHelper::changeLogFile(const std::string& directory, const s */ bool LogRotateHelper::rotateLog() { std::ofstream& is(filestream()); + namespace fs = std::filesystem; + fs::path log_file_path = fs::path(log_file_with_path_); + if (!fs::exists(log_file_path)) { + return false; + } + + if (is.is_open()) { is << std::flush; std::ostringstream gz_file_name; @@ -221,8 +238,10 @@ bool LogRotateHelper::rotateLog() { return false; } is.close(); - if (remove(log_file_with_path_.c_str()) == -1) { - fileWriteWithoutRotate("Failed to remove old log!"); + std::error_code ec_file; + if (false == fs::remove(log_file_with_path_, ec_file)) { + fileWriteWithoutRotate("Failed to remove old log: " + log_file_with_path_ + ". Error: " + + ec_file.message()); } changeLogFile(log_directory_); std::ostringstream ss; diff --git a/sink_logrotate/src/LogRotateUtility.cpp b/sink_logrotate/src/LogRotateUtility.cpp index e05d7e0..0522917 100644 --- a/sink_logrotate/src/LogRotateUtility.cpp +++ b/sink_logrotate/src/LogRotateUtility.cpp @@ -1,31 +1,33 @@ /** ========================================================================== -* 2011 by KjellKod.cc, modified by Vrecan in https://bitbucket.org/vrecan/g2log-dev -* 2015, adopted by KjellKod for g3log at:https://github.com/KjellKod/g3sinks -* -* This code 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. -* ============================================================================* -* PUBLIC DOMAIN and Not copywrited. First published at KjellKod.cc -* ********************************************* */ + * 2011 by KjellKod.cc, modified by Vrecan in https://bitbucket.org/vrecan/g2log-dev + * 2015, adopted by KjellKod for g3log at:https://github.com/KjellKod/g3sinks + * + * This code 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. + * ============================================================================* + * PUBLIC DOMAIN and Not copywrited. First published at KjellKod.cc + * ********************************************* */ #include "g3sinks/LogRotateUtility.h" -#include -#include + #include -#include -#include -#include -#include +#include #include +#include #include +#include +#include +#include +#include +#include - -namespace LogRotateUtility { +namespace fs = std::filesystem; +namespace LogRotateUtility { #if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) && !defined(__MINGW32__) - //http://stackoverflow.com/questions/321849/strptime-equivalent-on-windows + // http://stackoverflow.com/questions/321849/strptime-equivalent-on-windows char* strptime(const char* s, const char* f, struct tm* tm) { // Isn't the C++ standard lib nice? std::get_time is defined such that its // format parameters are the exact same as strptime. Of course, we have to @@ -39,18 +41,17 @@ namespace LogRotateUtility { if (input.fail()) { return nullptr; } - return (char*)(s + input.tellg()); + return (char*) (s + input.tellg()); } #endif - - // 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; + 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; @@ -63,9 +64,9 @@ namespace LogRotateUtility { /// illegal characters are removed from @param prefix input 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()); // '/' + prefix.erase(std::remove(prefix.begin(), prefix.end(), '\\'), prefix.end()); // '\\' + prefix.erase(std::remove(prefix.begin(), prefix.end(), '.'), prefix.end()); // '.' if (!isValidFilename(prefix)) { return ""; } @@ -75,52 +76,50 @@ namespace LogRotateUtility { /// @return the file header std::string header() { 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 << "\ng3log: created log file at: " << g3::localtime_formatted(now, "%a %b %d %H:%M:%S %Y") << "\n"; + // 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 << "\ng3log: created log file at: " << g3::localtime_formatted(now, "%a %b %d %H:%M:%S %Y") << "\n"; return ss_entry.str(); } - /// @return result as time from the file name - bool getDateFromFileName(const std::string& app_name, const std::string& file_name, long& result) { + /// @return result as time from the file name, return by reference also the complete date string + long getDateFromFileName(const std::string& app_name, const std::string& file_name, std::string& r_date_string) { if (file_name.find(app_name) != std::string::npos) { - std::string suffix = file_name.substr(app_name.size()); - if (suffix.empty()) { - //this is the main log file - return false; - } using namespace std; - - regex date_regex("\\.(\\d{4}-\\d{2}-\\d{2}-\\d{2}-\\d{2}-\\d{2})\\.gz"); + const regex date_regex(".*([0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{2}-[0-9]{2}-[0-9]{2})+\\.gz"); smatch date_match; - if (regex_match(suffix, date_match, date_regex)) { + if (regex_match(file_name, date_match, date_regex)) { if (date_match.size() == 2) { - std::string date = date_match[1].str(); + r_date_string = date_match[1].str(); struct tm tm = {0}; time_t t; - if (strptime(date.c_str(), "%Y-%m-%d-%H-%M-%S", &tm) == nullptr) { - return false; + if (strptime(r_date_string.c_str(), "%Y-%m-%d-%H-%M-%S", &tm) == nullptr) { + return (long) 0; } t = mktime(&tm); if (t == -1) { - return false; + return (long) 0; } - result = (long) t; - return true; + return (long) t; } } } - return false; + return (long) 0; } - std::string createPath(std::string path, std::string file_name) { + std::string sanityFixPath(std::string path) { // Unify the delimeters,. maybe sketchy solution but it seems to work // on at least win7 + ubuntu. All bets are off for older windows + std::string last_character{path.back()}; + if ("/" != last_character) { + path += "/"; + } + std::replace(path.begin(), path.end(), '\\', '/'); // clean up in case of multiples - auto contains_end = [&](std::string & in) -> bool { + auto contains_end = [&](std::string& in) -> bool { size_t size = in.size(); if (!size) return false; char end = in[size - 1]; @@ -135,66 +134,75 @@ namespace LogRotateUtility { path.insert(path.end(), '/'); } + return path; + } + + std::string createPathToFile(std::string path, std::string file_name) { path.insert(path.size(), file_name); return path; } /** - * Loop through the files in the folder + * Loop through the files in the folder. Delete old compressed files until max_log_count is reached * @param dir * @param file_name */ void expireArchives(const std::string& dir, const std::string& app_name, unsigned long max_log_count) { - std::map files; - boost::filesystem::path dir_path(dir); - - - boost::filesystem::directory_iterator end_itr; - if (!boost::filesystem::exists(dir_path)) return; - - for (boost::filesystem::directory_iterator itr(dir_path); itr != end_itr; ++itr) { - std::string current_file(itr->path().filename().string()); - long time = 0; - if (getDateFromFileName(app_name, current_file, time)) { - files.insert(std::pair (time, current_file)); + auto compressed_files = getCompressedLogFilesInDirectory(dir, app_name); + if (compressed_files.size() > max_log_count) { + auto logs_to_delete = compressed_files.size() - max_log_count; + for (const auto& p : compressed_files) { + if (logs_to_delete <= 0) { + break; + } + --logs_to_delete; // decrement the number of files to delete, even if the file could not be deleted + std::error_code ec_file; + auto filename_with_path = p.second; + if (false == fs::remove(filename_with_path, ec_file)) { + std::cerr << " Unable to delete " << filename_with_path << " " << ec_file.message() << std::endl; + } } } + } + /// return all files in the directory + std::vector getFilesInDirectory(const std::string& path) { + if (!fs::exists(path)) { + return {}; + } - //delete old logs. - ptrdiff_t logs_to_delete = files.size() - max_log_count; - if (logs_to_delete > 0) { + std::vector files; + for (const auto& entry : fs::directory_iterator(path)) { + files.push_back(entry.path().string()); + } + return files; + } - for (std::map::iterator it = files.begin(); it != files.end(); ++it) { - if (logs_to_delete <= 0) { - break; - } + /// returns both compressed and plain text log files + std::vector getAllLogFilesInDirectory(const std::string& path, const std::string& app_name) { + auto any_files = getFilesInDirectory(path); - std::string filename_with_path(createPath(dir, it->second)); - remove(filename_with_path.c_str()); - --logs_to_delete; + std::vector files; + for (const auto& current_file : any_files) { + if (current_file.find(app_name) != std::string::npos) { + files.push_back(current_file); } } + return files; } - std::map getLogFilesInDirectory(const std::string& dir, const std::string& app_name) { - std::map files; - boost::filesystem::path dir_path(dir); - - - boost::filesystem::directory_iterator end_itr; - if (!boost::filesystem::exists(dir_path)) return {}; - - for (boost::filesystem::directory_iterator itr(dir_path); itr != end_itr; ++itr) { - std::string current_file(itr->path().filename().string()); - long time = 0; - if (getDateFromFileName(app_name, current_file, time)) { - files.insert(std::pair (time, current_file)); - } - } - - return files; - } + std::map getCompressedLogFilesInDirectory(const std::string& path, const std::string& app_name) { + auto log_type_files = getAllLogFilesInDirectory(path, app_name); + std::map files; + for (const auto& current_file : log_type_files) { + std::string r_date_result; + auto time = getDateFromFileName(app_name, current_file, r_date_result); + if (time > 0) { + files.insert(std::pair(time, current_file)); + } + } + return files; + } /// create the file name std::string addLogSuffix(const std::string& raw_name) { @@ -203,11 +211,10 @@ namespace LogRotateUtility { return oss_name.str(); } - /// @return true if @param complete_file_with_path could be opened /// @param outstream is the file stream 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 + std::ios_base::openmode mode = std::ios_base::out; // for clarity: it's really overkill since it's an ofstream mode |= std::ios_base::app; outstream.open(complete_file_with_path, mode); if (!outstream.is_open()) { @@ -231,4 +238,4 @@ namespace LogRotateUtility { } return out; } -} +} // namespace LogRotateUtility diff --git a/sink_logrotate/src/LogRotateWithFilter.cpp b/sink_logrotate/src/LogRotateWithFilter.cpp index e034abe..b480e59 100644 --- a/sink_logrotate/src/LogRotateWithFilter.cpp +++ b/sink_logrotate/src/LogRotateWithFilter.cpp @@ -15,8 +15,8 @@ // helper function to create an logging sink with filter std::unique_ptr LogRotateWithFilter::CreateLogRotateWithFilter(std::string filename, std::string directory, std::vector filter) { - auto logRotatePtr = std::make_unique(filename, directory); - return std::make_unique(std::move(logRotatePtr), filter); + auto logRotatePtr = std::make_unique(filename, directory); + return std::make_unique(std::move(logRotatePtr), filter); } @@ -24,56 +24,55 @@ std::unique_ptr LogRotateWithFilter::CreateLogRotateWithFil /// @param logToFile rotate file logging sink /// @param removes all log entries with LEVELS in this filter LogRotateWithFilter::LogRotateWithFilter(LogRotateUniquePtr logToFile, IgnoreLogLevelsFilter ignoreLevels) - : _logger(std::move(logToFile)) - , _filter(std::move(ignoreLevels)) - , _log_details_func(&g3::LogMessage::DefaultLogDetailsToString) - {} + : _logger(std::move(logToFile)) + , _filter(std::move(ignoreLevels)) + , _log_details_func(&g3::LogMessage::DefaultLogDetailsToString){} LogRotateWithFilter::~LogRotateWithFilter() {} /// @param logEntry saves log entry that are not in the filter void LogRotateWithFilter::save(g3::LogMessageMover logEntry) { - auto level = logEntry.get()._level; - bool isNotInFilter_ = (_filter.end() == std::find(_filter.begin(), _filter.end(), level)); + auto level = logEntry.get()._level; + bool isNotInFilter_ = (_filter.end() == std::find(_filter.begin(), _filter.end(), level)); - if(isNotInFilter_) { + if (isNotInFilter_) { _logger->save(logEntry.get().toString(_log_details_func)); } } std::string LogRotateWithFilter::changeLogFile(const std::string& log_directory) { - return _logger->changeLogFile(log_directory); + return _logger->changeLogFile(log_directory); } /// @return the current filename std::string LogRotateWithFilter::logFileName() { - return _logger->logFileName(); + return _logger->logFileName(); } /// @param max_size sets the maximum number of archived logs /// when logging and it finally gets out of archived logs to /// write to then it will overwrite old compressed logs void LogRotateWithFilter::setMaxArchiveLogCount(int max_size) { - _logger->setMaxArchiveLogCount(max_size); + _logger->setMaxArchiveLogCount(max_size); } /// @param max_file_size sets the max log size in bytes void LogRotateWithFilter::setMaxLogSize(int max_file_size) { - _logger->setMaxLogSize(max_file_size); + _logger->setMaxLogSize(max_file_size); } /** -* Flush policy: Default is every single time (i.e. policy of 1). +* Flush policy: Default is every single time (i.e. policy of 1). * * If the system logs A LOT then it is likely better to allow for the system to buffer and write -* all the entries at once. -* +* all the entries at once. +* * 0: System decides, potentially very long time -* 1....N: Flush logs every n entry +* 1....N: Flush logs every n entry */ -void LogRotateWithFilter::setFlushPolicy(size_t flush_policy){ +void LogRotateWithFilter::setFlushPolicy(size_t flush_policy) { _logger->setFlushPolicy(flush_policy); } @@ -82,13 +81,13 @@ void LogRotateWithFilter::setFlushPolicy(size_t flush_policy){ * but is great for unit testing and if there are special circumstances where you want to see * the logs faster than the flush_policy */ -void LogRotateWithFilter::flush(){ +void LogRotateWithFilter::flush() { _logger->flush(); } -/** -* Override the defualt log formatting. +/** +* Override the defualt log formatting. * Please see https://github.com/KjellKod/g3log/API.markdown for more details */ void LogRotateWithFilter::overrideLogDetails(g3::LogMessage::LogDetailsFunc func) { diff --git a/sink_logrotate/src/g3sinks/LogRotate.h b/sink_logrotate/src/g3sinks/LogRotate.h index c5b8044..08dfdc2 100644 --- a/sink_logrotate/src/g3sinks/LogRotate.h +++ b/sink_logrotate/src/g3sinks/LogRotate.h @@ -22,35 +22,35 @@ struct LogRotateHelper; * \param log_prefix is the 'name' of the binary, this give the log name 'LOG-'name'-... * \param log_directory gives the directory to put the log files */ class LogRotate { - public: - LogRotate(const LogRotate&) = delete; - LogRotate& operator=(const LogRotate&) = delete; +public: + LogRotate(const LogRotate&) = delete; + LogRotate& operator=(const LogRotate&) = delete; - LogRotate(const std::string& log_prefix, const std::string& log_directory); - virtual ~LogRotate(); + LogRotate(const std::string& log_prefix, const std::string& log_directory); + virtual ~LogRotate(); - void save(std::string logEnty); - std::string changeLogFile(const std::string& log_directory, const std::string& new_name=""); - std::string logFileName(); + void save(std::string logEnty); + std::string changeLogFile(const std::string& log_directory, const std::string& new_name = ""); + std::string logFileName(); - // After hitting the max, the oldest compressed log file will be deleted - void setMaxArchiveLogCount(int max_size); - int getMaxArchiveLogCount(); - - void setFlushPolicy(size_t flush_policy); // 0: never (system auto flush), 1 ... N: every n times - void flush(); + // After hitting the max, the oldest compressed log file will be deleted + void setMaxArchiveLogCount(int max_size); + int getMaxArchiveLogCount(); + void setFlushPolicy(size_t flush_policy); // 0: never (system auto flush), 1 ... N: every n times + void flush(); - // After max_file_size_in_bytes the next log entry will trigger log - // compression to a gz file and log entries will start fresh - void setMaxLogSize(int max_file_size_in_bytes); - int getMaxLogSize(); - bool rotateLog(); + // After max_file_size_in_bytes the next log entry will trigger log + // compression to a gz file and log entries will start fresh + void setMaxLogSize(int max_file_size_in_bytes); + int getMaxLogSize(); - private: - std::unique_ptr pimpl_; + bool rotateLog(); + +private: + std::unique_ptr pimpl_; }; diff --git a/sink_logrotate/src/g3sinks/LogRotateUtility.h b/sink_logrotate/src/g3sinks/LogRotateUtility.h index dac4472..420bdd7 100644 --- a/sink_logrotate/src/g3sinks/LogRotateUtility.h +++ b/sink_logrotate/src/g3sinks/LogRotateUtility.h @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -33,13 +34,14 @@ namespace LogRotateUtility { /// illegal characters are removed from @param prefix input std::string prefixSanityFix(std::string prefix); - std::string createPath(std::string path, std::string file_name); + std::string sanityFixPath(std::string path); + std::string createPathToFile(std::string path, std::string file_name); /// @return the file header std::string header(); /// @return result as time from the file name - bool getDateFromFileName(const std::string& app_name, const std::string& file_name, long& result); + long getDateFromFileName(const std::string& app_name, const std::string& file_name, std::string& date_string); /** * Loop through the files in the folder @@ -48,9 +50,16 @@ namespace LogRotateUtility { */ void expireArchives(const std::string& dir, const std::string& app_name, unsigned long max_log_count); - /// @return all the found files in the directory that follow the expected log name pattern + + // return any found files in the directory + std::vector getFilesInDirectory(const std::string& path); + + // return app_name matching log type files in the directory. This can be compressed and plain log files + std::vector getAllLogFilesInDirectory(const std::string& path, const std::string& app_name); + + /// @return all the compressed files in the directory that follow the expected log name pattern /// std::map - std::map getLogFilesInDirectory(const std::string& dir, const std::string& app_name); + std::map getCompressedLogFilesInDirectory(const std::string& dir, const std::string& app_name); /// just adds the suffix to the log name std::string addLogSuffix(const std::string& raw_file_name); diff --git a/sink_logrotate/src/g3sinks/LogRotateWithFilter.h b/sink_logrotate/src/g3sinks/LogRotateWithFilter.h index 80b9bcc..e38b852 100644 --- a/sink_logrotate/src/g3sinks/LogRotateWithFilter.h +++ b/sink_logrotate/src/g3sinks/LogRotateWithFilter.h @@ -23,33 +23,33 @@ * that are NOT in the filter */ class LogRotateWithFilter { - using LogRotateUniquePtr = std::unique_ptr; - using IgnoreLogLevelsFilter = std::vector; + using LogRotateUniquePtr = std::unique_ptr; + using IgnoreLogLevelsFilter = std::vector; - public: +public: - static std::unique_ptr CreateLogRotateWithFilter(std::string filename, std::string directory, std::vector filter); + static std::unique_ptr CreateLogRotateWithFilter(std::string filename, std::string directory, std::vector filter); - LogRotateWithFilter(LogRotateUniquePtr logToFile, IgnoreLogLevelsFilter ignoreLevels); - virtual ~LogRotateWithFilter(); + LogRotateWithFilter(LogRotateUniquePtr logToFile, IgnoreLogLevelsFilter ignoreLevels); + virtual ~LogRotateWithFilter(); - void save(g3::LogMessageMover logEntry); - std::string changeLogFile(const std::string& log_directory); - std::string logFileName(); - void setMaxArchiveLogCount(int max_size); - void setMaxLogSize(int max_file_size); - void setFlushPolicy(size_t flush_policy); // 0: never (system auto flush), 1 ... N: every n times - void flush(); - void overrideLogDetails(g3::LogMessage::LogDetailsFunc func); + void save(g3::LogMessageMover logEntry); + std::string changeLogFile(const std::string& log_directory); + std::string logFileName(); + void setMaxArchiveLogCount(int max_size); + void setMaxLogSize(int max_file_size); + void setFlushPolicy(size_t flush_policy); // 0: never (system auto flush), 1 ... N: every n times + void flush(); + void overrideLogDetails(g3::LogMessage::LogDetailsFunc func); - private: - LogRotateUniquePtr _logger; - IgnoreLogLevelsFilter _filter; - g3::LogMessage::LogDetailsFunc _log_details_func; +private: + LogRotateUniquePtr _logger; + IgnoreLogLevelsFilter _filter; + g3::LogMessage::LogDetailsFunc _log_details_func; }; \ No newline at end of file diff --git a/test/FilterTest.cpp b/test/FilterTest.cpp index 4dcd7e2..e221530 100644 --- a/test/FilterTest.cpp +++ b/test/FilterTest.cpp @@ -11,25 +11,28 @@ #include #include #include - +#include +#include #include "FilterTest.h" #include #include "RotateTestHelper.h" +namespace fs = std::filesystem; + #if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) && !defined(__MINGW32__) -#define F_OK 0 + #define F_OK 0 #else -#include + #include #endif using namespace RotateTestHelper; namespace { // anonymous - g3::LogMessageMover CreateLogEntry(const LEVELS level, std::string content, std::string file, int line, std::string function) { - auto message = g3::LogMessage(file, line, function, level); - message.write().append(content); - return g3::MoveOnCopy(std::move(message)); - } + g3::LogMessageMover CreateLogEntry(const LEVELS level, std::string content, std::string file, int line, std::string function) { + auto message = g3::LogMessage(file, line, function, level); + message.write().append(content); + return g3::MoveOnCopy(std::move(message)); + } #define CREATE_LOG_ENTRY(level, content) CreateLogEntry(level, content, __FILE__, __LINE__, __FUNCTION__) @@ -38,135 +41,136 @@ namespace { // anonymous } // anonymous TEST_F(FilterTest, CreateObject) { - std::string logfilename; - { - auto logRotatePtr = std::make_unique(_filename, _directory); - - LogRotateWithFilter logWithFilter(std::move(logRotatePtr), {}); - } // RAII flush of log - auto name = std::string{_directory + _filename + ".log"}; - int check = access(name.c_str(), F_OK); // check that the file exists - EXPECT_EQ(check, 0) << std::strerror(errno) << " : " << name; + std::string logfilename; + { + auto logRotatePtr = std::make_unique(_filename, _directory); + LogRotateWithFilter logWithFilter(std::move(logRotatePtr), {}); + logfilename = logWithFilter.logFileName(); + } // RAII flush of log + + auto file = std::string{_directory + "/" + _filename + ".log"}; + EXPECT_TRUE(fs::exists(file)); + EXPECT_EQ(logfilename, file); } TEST_F(FilterTest, CreateObjectUsingHelper) { - auto name = std::string{_directory +"/" + _filename + ".log"}; - int check = access(name.c_str(), F_OK); // check that the file exists - EXPECT_NE(check, 0) << std::strerror(errno) << " : " << name; - - { - auto filterSinkPtr = LogRotateWithFilter::CreateLogRotateWithFilter(_filename, _directory, {}); - } // raii - check = access(name.c_str(), F_OK); // check that the file exists - EXPECT_EQ(check, 0) << std::strerror(errno) << " : " << name; - auto content = ReadContent(name); - EXPECT_TRUE(Exists(content, "g3log: created log file at:")) << ", content: [" << "]" << ", name: " << name; - EXPECT_TRUE(Exists(content, "file shutdown at:")) << ", content: [" << "]"<< ", name: " << name; + auto name = std::string{_directory + "/" + _filename + ".log"}; + int check = access(name.c_str(), F_OK); // check that the file exists + EXPECT_NE(check, 0) << std::strerror(errno) << " : " << name; + + { + auto filterSinkPtr = LogRotateWithFilter::CreateLogRotateWithFilter(_filename, _directory, {}); + } // raii + check = access(name.c_str(), F_OK); // check that the file exists + EXPECT_EQ(check, 0) << std::strerror(errno) << " : " << name; + auto content = ReadContent(name); + EXPECT_TRUE(Exists(content, "g3log: created log file at:")) << ", content: [" << "]" << ", name: " << name; + EXPECT_TRUE(Exists(content, "file shutdown at:")) << ", content: [" << "]" << ", name: " << name; } - /** - * The default for log details can be found at g3log. - * It's something like this - * 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; - } - - - We will override this to only have level - } - * */ +/** + * The default for log details can be found at g3log. + * It's something like this + * 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; + } + + + We will override this to only have level + } + * */ TEST_F(FilterTest, OverrideLogDetails) { - auto formatting = [](const g3::LogMessage& msg) -> std::string { - std::string out; - out.append(std::string(" === ") + msg.level() + (" !!! ")); - return out; - }; - - { - auto filterSinkPtr = LogRotateWithFilter::CreateLogRotateWithFilter(_filename, _directory, {}); - filterSinkPtr->overrideLogDetails(formatting); - - auto message0 = CREATE_LOG_ENTRY(INFO, "Hello World"); - filterSinkPtr->save(message0); - } // raii - - auto name = std::string{_directory + _filename + ".log"}; - auto content = ReadContent(name); - EXPECT_TRUE(Exists(content, "=== INFO !!! Hello World")) << content; + auto formatting = [](const g3::LogMessage & msg) -> std::string { + std::string out; + out.append(std::string(" === ") + msg.level() + (" !!! ")); + return out; + }; + std::string filename; + { + auto filterSinkPtr = LogRotateWithFilter::CreateLogRotateWithFilter(_filename, _directory, {}); + filterSinkPtr->overrideLogDetails(formatting); + auto message0 = CREATE_LOG_ENTRY(INFO, "Hello World"); + filterSinkPtr->save(message0); + filename = filterSinkPtr->logFileName(); + } // raii + + auto content = ReadContent(filename); + EXPECT_TRUE(Exists(content, "=== INFO !!! Hello World")) << content; } TEST_F(FilterTest, NothingFiltered) { - { - auto filterSinkPtr = LogRotateWithFilter::CreateLogRotateWithFilter(_filename, _directory, {}); - auto message0 = CREATE_LOG_ENTRY(INFO, "Hello World"); - filterSinkPtr->save(message0); + std::string filename; + { + auto filterSinkPtr = LogRotateWithFilter::CreateLogRotateWithFilter(_filename, _directory, {}); + auto message0 = CREATE_LOG_ENTRY(INFO, "Hello World"); + filterSinkPtr->save(message0); + filename = filterSinkPtr->logFileName(); - auto message1 = CREATE_LOG_ENTRY(G3LOG_DEBUG, "Hello D World"); - filterSinkPtr->save(message1); + auto message1 = CREATE_LOG_ENTRY(G3LOG_DEBUG, "Hello D World"); + filterSinkPtr->save(message1); - auto message2 = CREATE_LOG_ENTRY(WARNING, "Hello W World"); - filterSinkPtr->save(message2); + auto message2 = CREATE_LOG_ENTRY(WARNING, "Hello W World"); + filterSinkPtr->save(message2); - auto message3 = CREATE_LOG_ENTRY(FATAL, "Hello F World"); - filterSinkPtr->save(message3); + auto message3 = CREATE_LOG_ENTRY(FATAL, "Hello F World"); + filterSinkPtr->save(message3); - } // raii + } // raii - auto name = std::string{_directory + _filename + ".log"}; - auto content = ReadContent(name); - EXPECT_TRUE(Exists(content, "Hello World")) << content; - EXPECT_TRUE(Exists(content, "Hello D World")) << content; - EXPECT_TRUE(Exists(content, "Hello W World")) << content; - EXPECT_TRUE(Exists(content, "Hello F World")) << content; + auto content = ReadContent(filename); + EXPECT_TRUE(Exists(content, "Hello World")) << content; + EXPECT_TRUE(Exists(content, "Hello D World")) << content; + EXPECT_TRUE(Exists(content, "Hello W World")) << content; + EXPECT_TRUE(Exists(content, "Hello F World")) << content; } TEST_F(FilterTest, FilteredAndNotFiltered) { - { - auto filterSinkPtr = LogRotateWithFilter::CreateLogRotateWithFilter(_filename, _directory, {G3LOG_DEBUG, INFO, WARNING, FATAL}); - auto message0 = CREATE_LOG_ENTRY(INFO, "Hello World"); - filterSinkPtr->save(message0); - - auto message1 = CREATE_LOG_ENTRY(G3LOG_DEBUG, "Hello D World"); - filterSinkPtr->save(message1); - - auto message2 = CREATE_LOG_ENTRY(WARNING, "Hello W World"); - filterSinkPtr->save(message2); - - auto message3 = CREATE_LOG_ENTRY(FATAL, "Hello F World"); - filterSinkPtr->save(message3); - - auto newLevel = LEVELS{123, "MadeUpLevel"}; - auto message4 = CREATE_LOG_ENTRY(newLevel, "Hello New World"); - filterSinkPtr->save(message4); - - } // raii - - auto name = std::string{_directory + _filename + ".log"}; - auto content = ReadContent(name); - EXPECT_FALSE(Exists(content, "Hello World")) << content; - EXPECT_FALSE(Exists(content, "Hello D World")) << content; - EXPECT_FALSE(Exists(content, "Hello W World")) << content; - EXPECT_FALSE(Exists(content, "Hello F World")) << content; - - // Not filtered will exist - EXPECT_TRUE(Exists(content, "Hello New World")) << content; - - const auto level1 = INFO; - const auto level2 = INFO; - EXPECT_TRUE(level1 == level2); + std::string filename; + { + auto filterSinkPtr = LogRotateWithFilter::CreateLogRotateWithFilter(_filename, _directory, {G3LOG_DEBUG, INFO, WARNING, FATAL}); + auto message0 = CREATE_LOG_ENTRY(INFO, "Hello World"); + filterSinkPtr->save(message0); + + auto message1 = CREATE_LOG_ENTRY(G3LOG_DEBUG, "Hello D World"); + filterSinkPtr->save(message1); + + auto message2 = CREATE_LOG_ENTRY(WARNING, "Hello W World"); + filterSinkPtr->save(message2); + + auto message3 = CREATE_LOG_ENTRY(FATAL, "Hello F World"); + filterSinkPtr->save(message3); + + auto newLevel = LEVELS{123, "MadeUpLevel"}; + auto message4 = CREATE_LOG_ENTRY(newLevel, "Hello New World"); + filterSinkPtr->save(message4); + filename = filterSinkPtr->logFileName(); + } // raii + + auto content = ReadContent(filename); + EXPECT_FALSE(Exists(content, "Hello World")) << content; + EXPECT_FALSE(Exists(content, "Hello D World")) << content; + EXPECT_FALSE(Exists(content, "Hello W World")) << content; + EXPECT_FALSE(Exists(content, "Hello F World")) << content; + + // Not filtered will exist + EXPECT_TRUE(Exists(content, "Hello New World")) << content; + + const auto level1 = INFO; + const auto level2 = INFO; + EXPECT_TRUE(level1 == level2); } @@ -180,10 +184,10 @@ TEST_F(FilterTest, setFlushPolicy__default__every_time) { filterSinkPtr->save(CREATE_LOG_ENTRY(INFO, msg)); auto content = ReadContent(logfilename); #if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) && !defined(__MINGW32__) - msg.replace(msg.find("\n") , 2, "\r\n"); + msg.replace(msg.find("\n") , 2, "\r\n"); auto exists = Exists(content, msg); #else - auto exists = Exists(content, msg); + auto exists = Exists(content, msg); #endif ASSERT_TRUE(exists) << "\n\tcontent:" << content << "-\n\tentry: " << msg; } @@ -195,7 +199,7 @@ TEST_F(FilterTest, setFlushPolicy__only_when_buffer_is_full) { filterSinkPtr->setFlushPolicy(0); // auto buffer size if by default 1024 - for(int i = 0; i < 10; ++i) { + for (int i = 0; i < 10; ++i) { filterSinkPtr->save(CREATE_LOG_ENTRY(INFO, "this is a messagen\n")); } @@ -211,19 +215,19 @@ TEST_F(FilterTest, setFlushPolicy__every_third_write) { std::string content; auto checkIfExist = [&](std::string expected) -> bool { - content = ReadContent(logfilename); - bool exists = Exists(content, expected); - return exists; + content = ReadContent(logfilename); + bool exists = Exists(content, expected); + return exists; }; // auto buffer size if by default 1024 filterSinkPtr->save(CREATE_LOG_ENTRY(INFO, "msg1\n")); ASSERT_FALSE(checkIfExist("msg1")) << "\n\tcontent:" << content; - filterSinkPtr->save(CREATE_LOG_ENTRY(INFO,"msg2\n")); + filterSinkPtr->save(CREATE_LOG_ENTRY(INFO, "msg2\n")); ASSERT_FALSE(checkIfExist("msg2")) << "\n\tcontent:" << content; - - filterSinkPtr->save(CREATE_LOG_ENTRY(INFO,"msg3\n")); + + filterSinkPtr->save(CREATE_LOG_ENTRY(INFO, "msg3\n")); ASSERT_TRUE(checkIfExist("msg3")) << "\n\tcontent:" << content; // 3rd write flushes it + previous filterSinkPtr->save(CREATE_LOG_ENTRY(INFO, "msg4\n")); @@ -238,18 +242,18 @@ TEST_F(FilterTest, setFlushPolicy__force_flush) { std::string content; auto checkIfExist = [&](std::string expected) -> bool { - content = ReadContent(logfilename); - bool exists = Exists(content, expected); - return exists; + content = ReadContent(logfilename); + bool exists = Exists(content, expected); + return exists; }; // auto buffer size if by default 1024 - filterSinkPtr->save(CREATE_LOG_ENTRY(INFO,"msg1\n")); + filterSinkPtr->save(CREATE_LOG_ENTRY(INFO, "msg1\n")); ASSERT_FALSE(checkIfExist("msg1")) << "\n\tcontent:" << content; filterSinkPtr->save(CREATE_LOG_ENTRY(INFO, "msg2\n")); ASSERT_FALSE(checkIfExist("msg2")) << "\n\tcontent:" << content; - + filterSinkPtr->save(CREATE_LOG_ENTRY(INFO, "msg3\n")); filterSinkPtr->flush(); ASSERT_TRUE(checkIfExist("msg3")) << "\n\tcontent:" << content; // 3rd write flushes it + previous diff --git a/test/FilterTest.h b/test/FilterTest.h index e9cef0c..68cbf82 100644 --- a/test/FilterTest.h +++ b/test/FilterTest.h @@ -10,38 +10,41 @@ #pragma once #include -#include -#include -#include +#include +#include "g3sinks/LogRotateUtility.h" + +namespace fs = std::filesystem; class FilterTest : public ::testing::Test { - public: +public: FilterTest() { }; - protected: +protected: virtual void SetUp() { _filename = "g3sink_filter_rotatefile_test"; #if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) && !defined(__MINGW32__) - _directory = "./"; + _directory = "./g3log_test_directory"; #else - _directory = "/tmp/"; + _directory = "/tmp/g3log_test_directory"; #endif - _filesToRemove.push_back(std::string(_directory + _filename + ".log")); } virtual void TearDown() { - for (auto filename : _filesToRemove) { - auto success = std::remove(filename.c_str()); - if (0 != success) { - std::cerr << "error deleting: " << filename << ": " << std::strerror(errno) << std::endl; + auto allFiles = LogRotateUtility::getFilesInDirectory(_directory); + for (auto& filename : allFiles) { + std::error_code ec_file; + if (false == fs::remove(filename, ec_file)) { + ADD_FAILURE() << "UNABLE to remove file: " << filename << " " << ec_file.message() << std::endl; + } + std::error_code ec_dir; + if (fs::exists(_directory) && !fs::remove(_directory, ec_dir)) { + ADD_FAILURE() << "UNABLE to remove directory: " << _directory << " " << ec_dir.message() << std::endl; } } } std::string _filename; std::string _directory; - std::vector _filesToRemove; - }; diff --git a/test/RotateFileTest.cpp b/test/RotateFileTest.cpp index 28890bf..3108416 100644 --- a/test/RotateFileTest.cpp +++ b/test/RotateFileTest.cpp @@ -19,10 +19,12 @@ #include "g3sinks/LogRotateWithFilter.h" using namespace RotateTestHelper; +namespace fs = std::filesystem; + #if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) && !defined(__MINGW32__) -#define F_OK 0 + #define F_OK 0 #else -#include + #include #endif TEST_F(RotateFileTest, CreateObject) { @@ -30,7 +32,6 @@ TEST_F(RotateFileTest, CreateObject) { { LogRotate logrotate(_filename, _directory); logfilename = logrotate.logFileName(); - std::cout << logfilename << std::endl; logrotate.save("test"); } // RAII flush of log auto name = std::string{_directory + _filename + ".log"}; @@ -43,15 +44,13 @@ TEST_F(RotateFileTest, ChangeLogFile) { { LogRotate logrotate(_filename, _directory); logfilename = logrotate.logFileName(); - std::cout << logfilename << std::endl; logrotate.save("test"); auto name = std::string{_directory + _filename + ".log"}; - EXPECT_TRUE(DoesFileEntityExist(name)); + EXPECT_TRUE(fs::exists(name)); auto expected_newname = std::string{_directory + "some_new_file.log"}; - EXPECT_FALSE(DoesFileEntityExist(expected_newname)); + EXPECT_FALSE(fs::exists(expected_newname)); // by default, if name is not given then the default name is kept - _filesToRemove.push_back(expected_newname); auto newname = logrotate.changeLogFile(_directory); EXPECT_NE(expected_newname, newname); EXPECT_EQ(logfilename, newname); @@ -79,14 +78,13 @@ TEST_F(RotateFileTest, setMaxLogSize) { EXPECT_TRUE(exists) << "\n\tcontent:" << content << "-\n\tentry: " << gone; } -TEST_F(RotateFileTest, setMaxLogSizeAndRotate_ValidNewName) { +TEST_F(RotateFileTest, setMaxLogSizeAndRotate) { std::string newFileName = "new_sink_name"; LogRotate logrotate(_filename, _directory); logrotate.changeLogFile(_directory, newFileName); auto logfilename = logrotate.logFileName(); EXPECT_EQ(_directory + newFileName + ".log", logfilename); - _filesToRemove.push_back(logfilename); std::string gone{"Soon to be missing words"}; logrotate.save(gone); @@ -94,49 +92,29 @@ TEST_F(RotateFileTest, setMaxLogSizeAndRotate_ValidNewName) { std::string first_message_in_new_log = "first message"; logrotate.save(first_message_in_new_log); - + EXPECT_TRUE(fs::exists(logfilename)); auto content = ReadContent(logfilename); auto exists = Exists(content, gone); EXPECT_FALSE(exists) << "\n\tcontent:" << content << "-\n\tentry: " << gone; - + EXPECT_TRUE(fs::exists(logfilename)); exists = Exists(content, first_message_in_new_log); EXPECT_TRUE(exists) << "\n\tcontent:" << content << "-\n\tentry: " << gone; - - auto allFiles = LogRotateUtility::getLogFilesInDirectory(_directory, newFileName + ".log"); - EXPECT_EQ(allFiles.size(), 1) << "direc: " << _directory << ", name: " << newFileName << std::endl; - const int kFilePathIndex = 1; - for (auto p : allFiles) { - std::cout << "to remove: " << _directory + std::get(p) << std::endl; - _filesToRemove.push_back(_directory + std::get(p)); - } + EXPECT_TRUE(fs::exists(logfilename)); + auto allFiles = LogRotateUtility::getAllLogFilesInDirectory(_directory, newFileName + ".log"); + auto allCompressedFiles = LogRotateUtility::getCompressedLogFilesInDirectory(_directory, newFileName + ".log"); + EXPECT_EQ(allFiles.size(), 2) << FlattenToString(allFiles); + EXPECT_EQ(allCompressedFiles.size(), 1) << FlattenToString(allCompressedFiles); } -TEST_F(RotateFileTest, setMaxLogSizeAndRotate_EmptyNewName) { +TEST_F(RotateFileTest, logRotate_setEmptyNewName) { LogRotate logrotate(_filename, _directory); - logrotate.changeLogFile(_directory, ""); auto logfilename = logrotate.logFileName(); EXPECT_EQ(_directory + _filename + ".log", logfilename); - - std::string gone{"Soon to be missing words"}; - logrotate.save(gone); - logrotate.setMaxLogSize(static_cast(gone.size())); - - std::string first_message_in_new_log = "first message"; - logrotate.save(first_message_in_new_log); - - auto content = ReadContent(logfilename); - auto exists = Exists(content, gone); - EXPECT_FALSE(exists) << "\n\tcontent:" << content << "-\n\tentry: " << gone; - - exists = Exists(content, first_message_in_new_log); - EXPECT_TRUE(exists) << "\n\tcontent:" << content << "\n\tentry: " << gone; - - auto allFiles = LogRotateUtility::getLogFilesInDirectory(_directory, _filename + ".log"); - EXPECT_EQ(allFiles.size(), 1) << "direc: " << _directory << ", name: " << _filename; } namespace { + const size_t kMaxArchiveLogCount = 3; void RotateAndExpireOldLogs(std::string filename, std::string directory, bool changeLogFile = false) { LogRotate logrotate(filename, directory); @@ -144,9 +122,13 @@ namespace { logrotate.changeLogFile(directory, ""); } auto logfilename = logrotate.logFileName(); - EXPECT_EQ(directory + filename + ".log", logfilename); + std::string trailing_directory_character{directory.back()}; + if (trailing_directory_character != std::string{"/"}) { // for tests where directory lack trailing "/" + directory = directory + "/"; + } - logrotate.setMaxArchiveLogCount(3); + EXPECT_EQ(directory + filename + ".log", logfilename); + logrotate.setMaxArchiveLogCount(kMaxArchiveLogCount); std::string gone{"Soon to be missing words"}; logrotate.save(gone); logrotate.setMaxLogSize(static_cast(gone.size())); @@ -160,8 +142,6 @@ namespace { exists = Exists(content, first_message_in_new_log); EXPECT_TRUE(exists) << "\n\tcontent:" << content << "-\n\tentry: " << gone; gone = first_message_in_new_log; - logrotate.setMaxLogSize(static_cast(gone.size())); - // force sleep 1 s to trigger new time std::this_thread::sleep_for(std::chrono::seconds(1)); } @@ -171,24 +151,38 @@ namespace { TEST_F(RotateFileTest, rotateAndExpireOldLogs) { RotateAndExpireOldLogs(_filename, _directory); auto app_name = _filename + ".log"; - auto allFiles = LogRotateUtility::getLogFilesInDirectory(_directory, app_name); - - const int kFilePathIndex = 1; - for (auto p : allFiles) { - _filesToRemove.push_back(_directory + std::get(p)); + auto allFiles = LogRotateUtility::getAllLogFilesInDirectory(_directory, app_name); + auto allCompressedFiles = LogRotateUtility::getCompressedLogFilesInDirectory(_directory, app_name); + EXPECT_EQ(allFiles.size(), allCompressedFiles.size() + 1); + EXPECT_EQ(allCompressedFiles.size(), kMaxArchiveLogCount) << " Failure " << FlattenToString(allFiles); + for (auto& file : allFiles) { + ; + EXPECT_TRUE(fs::exists(file)) << "It should exist but it doesn't: " << file << std::endl; } +} - EXPECT_EQ(allFiles.size(), size_t{3}) << " Failure " << ExtractContent(allFiles); +TEST_F(RotateFileTest, getDateFromFileName) { + std::string file_name = "g3sink_rotatefile_test.log.2022-03-19-14-41-43.gz"; + std::string app_name = "g3sink_rotatefile_test"; + std::string r_date_string; + std::string expected_date_string = "2022-03-19-14-41-43"; + EXPECT_NE((long) 0, LogRotateUtility::getDateFromFileName(app_name, file_name, r_date_string)); + EXPECT_EQ(r_date_string, expected_date_string); } + TEST_F(RotateFileTest, rotateAndExpireOldLogsWithoutTrailingSlashForDirectory) { - auto filename = _filename.substr(0, _filename.size() - 1); // remove the trailing '/' - RotateAndExpireOldLogs(_filename, _directory); + auto directory = _directory; //_directory.substr(0, _directory.size() - 1); // remove the trailing '/' + RotateAndExpireOldLogs(_filename, directory); auto app_name = _filename + ".log"; - std::cout << "dire: " << _directory << std::endl; - std::cout << "app: " << app_name << std::endl; - auto allFiles = LogRotateUtility::getLogFilesInDirectory(_directory, app_name); - EXPECT_EQ(allFiles.size(), size_t{3}) << " Failure " << ExtractContent(allFiles); + auto allFiles = LogRotateUtility::getAllLogFilesInDirectory(directory, app_name); + auto allCompressedFiles = LogRotateUtility::getCompressedLogFilesInDirectory(directory, app_name); + + EXPECT_NE(allFiles.size(), allCompressedFiles.size()); + EXPECT_EQ(allCompressedFiles.size(), kMaxArchiveLogCount) << " Failure " << FlattenToString(allCompressedFiles); + for (auto& file : allFiles) { + EXPECT_TRUE(fs::exists(file)) << "It should exist but it doesn't: " << file << std::endl; + } } TEST_F(RotateFileTest, setFlushPolicy__default__every_time) { @@ -279,12 +273,11 @@ TEST_F(RotateFileTest, setFlushPolicy__force_flush) { ASSERT_TRUE(checkIfExist("msg4")) << "\n\tcontent:" << content; // 3rd write flushes it + previous } -TEST_F(RotateFileTest, DISABLED_setMaxArchiveLogCount) { EXPECT_FALSE(true); } + TEST_F(RotateFileTest, rotateLog) { LogRotate logrotate(_filename, _directory); std::string logfilename = logrotate.logFileName(); - std::cout << logfilename << std::endl; std::string content; auto checkIfExist = [&](std::string expected) -> bool { content = ReadContent(logfilename); @@ -293,7 +286,7 @@ TEST_F(RotateFileTest, rotateLog) { }; logrotate.save("test1"); ASSERT_TRUE(checkIfExist("test1")) << "\n\tcontent:" << content; - EXPECT_TRUE(DoesFileEntityExist(logfilename)); + EXPECT_TRUE(fs::exists(logfilename)); logrotate.rotateLog(); logrotate.save("test2"); ASSERT_FALSE(checkIfExist("test1")) << "\n\tcontent:" << content; @@ -301,6 +294,8 @@ TEST_F(RotateFileTest, rotateLog) { auto app_name = _filename + ".log"; logrotate.rotateLog(); logrotate.save("test3"); - auto allFiles = LogRotateUtility::getLogFilesInDirectory(_directory, app_name); - EXPECT_EQ(allFiles.size(), size_t{1}); + auto allFiles = LogRotateUtility::getAllLogFilesInDirectory(_directory, app_name); + auto compressedFiles = LogRotateUtility::getCompressedLogFilesInDirectory(_directory, app_name); + EXPECT_EQ(allFiles.size(), size_t{2}) << "Failure: " << FlattenToString(allFiles); + EXPECT_EQ(compressedFiles.size(), size_t{1}) << "Failure: " << FlattenToString(compressedFiles); } \ No newline at end of file diff --git a/test/RotateFileTest.h b/test/RotateFileTest.h index d2492d5..3e974ab 100644 --- a/test/RotateFileTest.h +++ b/test/RotateFileTest.h @@ -10,51 +10,45 @@ #pragma once #include -#include -#include -#include #include #include #include #include +#include - +namespace fs = std::filesystem; class RotateFileTest : public ::testing::Test { - public: +public: RotateFileTest() { }; - protected: +protected: virtual void SetUp() { _filename = "g3sink_rotatefile_test"; #if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) && !defined(__MINGW32__) - _directory = "./"; + _directory = "./g3log_test_directory/"; #else - _directory = "/tmp/"; + _directory = "/tmp/g3log_test_directory/"; #endif - _filesToRemove.push_back(std::string(_directory + _filename + ".log")); } virtual void TearDown() { - auto allFiles = LogRotateUtility::getLogFilesInDirectory(_directory, _filename + ".log"); - const int kFilePathIndex = 1; - for (auto& p : allFiles) { - std::string file = std::get(p); - if ((std::find(_filesToRemove.begin(), _filesToRemove.end(), file) == _filesToRemove.end())) { - _filesToRemove.push_back(_directory + std::get(p)); + auto allFiles = LogRotateUtility::getFilesInDirectory(_directory); + for (auto& filename : allFiles) { + std::error_code ec_file; + if (false == fs::remove(filename, ec_file)) { + ADD_FAILURE() << "UNABLE to remove file: " << filename << " " << ec_file.message() << std::endl; } } - - - for (auto filename : _filesToRemove) { - std::remove(filename.c_str()); + std::error_code ec_dir; + if (fs::exists(_directory) && !fs::remove(_directory, ec_dir)) { + ADD_FAILURE() << "UNABLE to remove directory: " << _directory << " " << ec_dir.message() << std::endl; } } std::string _filename; std::string _directory; - std::vector _filesToRemove; }; diff --git a/test/RotateTestHelper.cpp b/test/RotateTestHelper.cpp index bdcee74..eb6f63f 100644 --- a/test/RotateTestHelper.cpp +++ b/test/RotateTestHelper.cpp @@ -18,10 +18,10 @@ #include #if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) && !defined(__MINGW32__) -#include -#define F_OK 0 + #include + #define F_OK 0 #else -#include + #include #endif namespace RotateTestHelper { @@ -31,7 +31,7 @@ namespace RotateTestHelper { std::ifstream readIn(filename.c_str(), std::ios::in | std::ios::binary); if (readIn) { std::shared_ptr raii(nullptr, [&](void*) { - readIn.close(); + readIn.close(); }); std::string contents; @@ -45,7 +45,16 @@ namespace RotateTestHelper { } - std::string ExtractContent(const std::map& content) { + std::string FlattenToString(const std::vector& content) { + std::string extracted = "\n "; + for (const auto& file : content) { + extracted += file + ", \n"; + } + extracted += "\n"; + return extracted; + } + + std::string FlattenToString(const std::map& content) { std::string extracted = "\n "; for (const auto& pair : content) { std::string file = pair.second; @@ -61,14 +70,4 @@ namespace RotateTestHelper { auto found = content.find(expected); return found != std::string::npos; } - - bool DoesFileEntityExist(const std::string& pathToFile) { - int check = access(pathToFile.c_str(), F_OK); - bool found = (0 == check); - if (!found) { - std::cerr << pathToFile << " was not found: " << std::strerror(errno) << std::endl; - } - return found; - } - } // RotateTestHelper diff --git a/test/RotateTestHelper.h b/test/RotateTestHelper.h index f80dbaf..a478b7e 100644 --- a/test/RotateTestHelper.h +++ b/test/RotateTestHelper.h @@ -8,15 +8,16 @@ * PUBLIC DOMAIN and Not copywrited. First published at KjellKod.cc * ********************************************* */ -#pragma once +#pragma once #include +#include #include namespace RotateTestHelper { - std::string ReadContent(const std::string filename); - std::string ExtractContent(const std::map& content); - bool Exists(const std::string content, const std::string expected); - bool DoesFileEntityExist(const std::string& pathToFile); + std::string ReadContent(const std::string filename); + std::string FlattenToString(const std::vector& content); + std::string FlattenToString(const std::map& content); + bool Exists(const std::string content, const std::string expected); }