32 Commits

Author SHA1 Message Date
6ff484fef8 添加日志携带位置信息 2025-05-15 10:03:17 +08:00
youmetme
8e512563e4 Dev (#19)
Some checks failed
test on Windows / test (push) Has been cancelled
test on Linux / test (push) Failing after 7m1s
* #feat 增强Fatal级别的底色,修改logging类的方法

* 更新版本号

* 加入test脚本

* fix:conanfile

* test action

* 修复错别字

* add test on windows action

* fix test on windows action

* fix action on windows

* fix

* fix 内存分配错误

* fix msvc 不支持中文注释,删除中文注释

* test on windows and test  chinese char

* ersion 0.2.4

* feature:根据文件大小分割日志

* fix:内存泄露

* fix:使用char偏移单位

* add english brief

* 修改api函数名,加入Default关键字,减除歧义

* Multiple substring interceptors

* 更新版本号

* #fix 拦截器对level参数无效

* fix

* #将拦截器改名为过滤器,更加接近职能

* 更新自述文件

* 更新一些自述文件
2024-11-24 20:26:04 +08:00
youmetme
9d3a7bbb8d 更新自述文件 (#18)
* #feat 增强Fatal级别的底色,修改logging类的方法

* 更新版本号

* 加入test脚本

* fix:conanfile

* test action

* 修复错别字

* add test on windows action

* fix test on windows action

* fix action on windows

* fix

* fix 内存分配错误

* fix msvc 不支持中文注释,删除中文注释

* test on windows and test  chinese char

* ersion 0.2.4

* feature:根据文件大小分割日志

* fix:内存泄露

* fix:使用char偏移单位

* add english brief

* 修改api函数名,加入Default关键字,减除歧义

* Multiple substring interceptors

* 更新版本号

* #fix 拦截器对level参数无效

* fix

* #将拦截器改名为过滤器,更加接近职能

* 更新自述文件
2024-11-23 09:02:24 +08:00
youmetme
064881c0ad Dev (#17)
* #将拦截器改名为过滤器,更加接近职能
2024-11-23 08:47:48 +08:00
youmetme
f0c3a5d56a Update conanfile.py 2024-11-21 16:03:18 +08:00
youmetme
56e1ac52ff fix 拦截器level参数无效 (#15)
* #feat 增强Fatal级别的底色,修改logging类的方法

* 更新版本号

* 加入test脚本

* fix:conanfile

* test action

* 修复错别字

* add test on windows action

* fix test on windows action

* fix action on windows

* fix

* fix 内存分配错误

* fix msvc 不支持中文注释,删除中文注释

* test on windows and test  chinese char

* ersion 0.2.4

* feature:根据文件大小分割日志

* fix:内存泄露

* fix:使用char偏移单位

* add english brief

* 修改api函数名,加入Default关键字,减除歧义

* Multiple substring interceptors

* 更新版本号

* #fix 拦截器对level参数无效

* fix
2024-11-21 16:02:40 +08:00
youmetme
cc53e07788 V0.4.0 (#14)
* #feat 增强Fatal级别的底色,修改logging类的方法

* 更新版本号

* 加入test脚本

* fix:conanfile

* test action

* 修复错别字

* add test on windows action

* fix test on windows action

* fix action on windows

* fix

* fix 内存分配错误

* fix msvc 不支持中文注释,删除中文注释

* test on windows and test  chinese char

* ersion 0.2.4

* feature:根据文件大小分割日志

* fix:内存泄露

* fix:使用char偏移单位

* add english brief

* 修改api函数名,加入Default关键字,减除歧义

* Multiple substring interceptors

* 更新版本号
2024-11-21 15:17:16 +08:00
youmetme
a4b4ad7452 Multiple substring interceptors (#13)
* #feat 增强Fatal级别的底色,修改logging类的方法

* 更新版本号

* 加入test脚本

* fix:conanfile

* test action

* 修复错别字

* add test on windows action

* fix test on windows action

* fix action on windows

* fix

* fix 内存分配错误

* fix msvc 不支持中文注释,删除中文注释

* test on windows and test  chinese char

* ersion 0.2.4

* feature:根据文件大小分割日志

* fix:内存泄露

* fix:使用char偏移单位

* add english brief

* 修改api函数名,加入Default关键字,减除歧义

* Multiple substring interceptors
2024-11-21 15:13:24 +08:00
youmetme
f4494515ca Add English brief (#12)
* #feat 增强Fatal级别的底色,修改logging类的方法

* 更新版本号

* 加入test脚本

* fix:conanfile

* test action

* 修复错别字

* add test on windows action

* fix test on windows action

* fix action on windows

* fix

* fix 内存分配错误

* fix msvc 不支持中文注释,删除中文注释

* test on windows and test  chinese char

* ersion 0.2.4

* feature:根据文件大小分割日志

* fix:内存泄露

* fix:使用char偏移单位

* add english brief
2024-11-21 12:13:57 +08:00
youmetme
d0bfc31563 Dev (#11)
* #feat 增强Fatal级别的底色,修改logging类的方法

* 更新版本号

* 加入test脚本

* fix:conanfile

* test action

* 修复错别字

* add test on windows action

* fix test on windows action

* fix action on windows

* fix

* fix 内存分配错误

* fix msvc 不支持中文注释,删除中文注释

* test on windows and test  chinese char

* ersion 0.2.4

* feature:根据文件大小分割日志

* fix:内存泄露

* fix:使用char偏移单位
2024-11-20 11:56:47 +08:00
youmetme
650ce0dc3f release v0.2.4 (#10)
* #feat 增强Fatal级别的底色,修改logging类的方法

* 更新版本号

* 加入test脚本

* fix:conanfile

* test action

* 修复错别字

* add test on windows action

* fix test on windows action

* fix action on windows

* fix

* fix 内存分配错误

* fix msvc 不支持中文注释,删除中文注释

* test on windows and test  chinese char

* ersion 0.2.4
2024-11-03 11:51:08 +08:00
youmetme
3ef229ed65 Dev test on windows action (#9)
* #feat 增强Fatal级别的底色,修改logging类的方法

* 更新版本号

* 加入test脚本

* fix:conanfile

* test action

* 修复错别字

* add test on windows action

* fix test on windows action

* fix action on windows

* fix

* fix 内存分配错误

* fix msvc 不支持中文注释,删除中文注释
2024-11-02 20:26:04 +08:00
youmetme
b7f49188ad Dev fix (#7)
* #feat 增强Fatal级别的底色,修改logging类的方法

* 更新版本号

* 加入test脚本

* fix:conanfile

* test action

* 修复错别字
2024-11-02 12:56:51 +08:00
youmetme
d61f7b6fde Dev test (#6)
* #feat 增强Fatal级别的底色,修改logging类的方法

* 更新版本号

* 加入test脚本

* fix:conanfile

* test action
2024-11-02 12:38:36 +08:00
youmetme
86cf4c4526 V0.2.3 (#5)
* #feat 增强Fatal级别的底色,修改logging类的方法

* 更新版本号
2024-09-21 15:21:53 +08:00
youmetme
6765dd1214 Merge pull request #4 from WangZhongDian/dev
Dev 0.2.2
2024-09-19 13:20:31 +08:00
b852bdceb8 日志操作器默认使用控制台处理器 2024-09-19 13:14:22 +08:00
556595be2b 补丁 2024-09-18 15:04:57 +08:00
e39dcdcde9 修改项目结构以便于扩展,引入clang-format格式化 2024-09-18 14:48:55 +08:00
youmetme
0c96f67b47 Merge pull request #3 from WangZhongDian/dev
Dev
2024-08-16 18:15:48 +08:00
96a417ba93 调整处理器结构体 2024-08-14 13:02:27 +08:00
d2a2b933a5 修复资源未释放bug,简化释放流程 2024-08-13 22:43:54 +08:00
youmetme
3bc9704258 Merge pull request #2 from WangZhongDian/dev
完成拦截器设计
2024-08-13 22:24:33 +08:00
2608dfa078 完成拦截器设计 2024-08-13 22:23:34 +08:00
youmetme
9448a294a2 Merge pull request #1 from WangZhongDian/dev
Dev
2024-08-12 20:31:00 +08:00
a823bab944 修复测试功能 2024-08-12 20:17:31 +08:00
03e05051ea 修复了日志处理器的设计问题 2024-08-12 19:41:47 +08:00
d22ce6956e 修改了不合理设计 2024-08-12 19:39:12 +08:00
dcd6ba0106 修复错误描述信息 2024-08-12 18:18:30 +08:00
20a1ec9033 添加开源协议 2024-08-12 18:13:42 +08:00
ad0fa169ef 添加conan相关文件 2024-08-12 18:06:41 +08:00
addf2cb751 V0.1.0版本 2024-08-12 14:16:11 +08:00
32 changed files with 1524 additions and 175 deletions

246
.clang-format Normal file
View File

@@ -0,0 +1,246 @@
---
Language: Cpp
# BasedOnStyle: LLVM
AccessModifierOffset: -2
AlignAfterOpenBracket: Align
AlignArrayOfStructures: None
AlignConsecutiveAssignments:
Enabled: true
AcrossEmptyLines: true
AcrossComments: true
AlignCompound: false
AlignFunctionPointers: false
PadOperators: true
AlignConsecutiveBitFields:
Enabled: true
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveDeclarations:
Enabled: true
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveMacros:
Enabled: true
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveShortCaseStatements:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCaseColons: false
AlignEscapedNewlines: Right
AlignOperands: Align
AlignTrailingComments:
Kind: Always
OverEmptyLines: 0
AllowAllArgumentsOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowBreakBeforeNoexceptSpecifier: Never
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortCompoundRequirementOnASingleLine: true
AllowShortEnumsOnASingleLine: true
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Never
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: MultiLine
AttributeMacros:
- __capability
BinPackArguments: false
BinPackParameters: false
BitFieldColonSpacing: Both
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: Never
AfterEnum: false
AfterExternBlock: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
BeforeCatch: false
BeforeElse: false
BeforeLambdaBody: false
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakAdjacentStringLiterals: true
BreakAfterAttributes: Leave
BreakAfterJavaFieldAnnotations: false
BreakArrays: true
BreakBeforeBinaryOperators: None
BreakBeforeConceptDeclarations: Always
BreakBeforeBraces: Attach
BreakBeforeInlineASMColon: OnlyMultiline
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon
BreakInheritanceList: BeforeColon
BreakStringLiterals: true
ColumnLimit: 80
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
EmptyLineAfterAccessModifier: Never
EmptyLineBeforeAccessModifier: LogicalBlock
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IfMacros:
- KJ_IF_MAYBE
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
Priority: 3
SortPriority: 0
CaseSensitive: false
- Regex: '.*'
Priority: 1
SortPriority: 0
CaseSensitive: false
IncludeIsMainRegex: '(Test)?$'
IncludeIsMainSourceRegex: ''
IndentAccessModifiers: false
IndentCaseBlocks: false
IndentCaseLabels: false
IndentExternBlock: AfterExternBlock
IndentGotoLabels: true
IndentPPDirectives: None
IndentRequiresClause: true
IndentWidth: 4
IndentWrappedFunctionNames: false
InsertBraces: false
InsertNewlineAtEOF: false
InsertTrailingCommas: None
IntegerLiteralSeparator:
Binary: 0
BinaryMinDigits: 0
Decimal: 0
DecimalMinDigits: 0
Hex: 0
HexMinDigits: 0
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
KeepEmptyLinesAtEOF: false
LambdaBodyIndentation: Signature
LineEnding: DeriveLF
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 2
ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PackConstructorInitializers: BinPack
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakOpenParenthesis: 0
PenaltyBreakScopeResolution: 500
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyIndentedWhitespace: 0
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right
PPIndentWidth: -1
QualifierAlignment: Leave
ReferenceAlignment: Pointer
ReflowComments: true
RemoveBracesLLVM: false
RemoveParentheses: Leave
RemoveSemicolon: false
RequiresClausePosition: OwnLine
RequiresExpressionIndentation: OuterScope
SeparateDefinitionBlocks: Leave
ShortNamespaceLines: 1
SkipMacroDefinitionBody: false
SortIncludes: CaseSensitive
SortJavaStaticImport: Before
SortUsingDeclarations: LexicographicNumeric
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceAroundPointerQualifiers: Default
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeJsonColon: false
SpaceBeforeParens: ControlStatements
SpaceBeforeParensOptions:
AfterControlStatements: true
AfterForeachMacros: true
AfterFunctionDefinitionName: false
AfterFunctionDeclarationName: false
AfterIfMacros: true
AfterOverloadedOperator: false
AfterPlacementOperator: true
AfterRequiresInClause: false
AfterRequiresInExpression: false
BeforeNonEmptyParentheses: false
SpaceBeforeRangeBasedForLoopColon: true
SpaceBeforeSquareBrackets: false
SpaceInEmptyBlock: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: Never
SpacesInContainerLiterals: true
SpacesInLineCommentPrefix:
Minimum: 1
Maximum: -1
SpacesInParens: Never
SpacesInParensOptions:
InCStyleCasts: false
InConditionalStatements: false
InEmptyParentheses: false
Other: false
SpacesInSquareBrackets: false
Standard: Latest
StatementAttributeLikeMacros:
- Q_EMIT
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 8
UseTab: Never
VerilogBreakBetweenInstancePorts: true
WhitespaceSensitiveMacros:
- BOOST_PP_STRINGIZE
- CF_SWIFT_NAME
- NS_SWIFT_NAME
- PP_STRINGIZE
- STRINGIZE
...

16
.github/workflows/linux_test.yml vendored Normal file
View File

@@ -0,0 +1,16 @@
name: test on Linux
on:
push:
branches: ["main"]
pull_request:
branches: ["main"]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: checkout code
uses: actions/checkout@v4
- name: test
run: bash ./script/test.sh

17
.github/workflows/windows_test.yml vendored Normal file
View File

@@ -0,0 +1,17 @@
name: test on Windows
on:
push:
branches: ["main","dev"]
pull_request:
branches: ["main"]
jobs:
test:
runs-on: windows-latest
steps:
- name: checkout code
uses: actions/checkout@v4
- name: test
run: ./script/test_windows.ps1

2
.gitignore vendored
View File

@@ -1,4 +1,4 @@
.cache
.vscode/**
bin
build

View File

@@ -1,21 +1,26 @@
cmake_minimum_required( VERSION 3.28)
cmake_minimum_required(VERSION 3.28...3.30)
project(Logging)
project(logging)
set(CMAKE_C_STANDARD 99)
aux_source_directory(${CMAKE_SOURCE_DIR}/src SRC)
if(MSVC)
add_compile_options("/source-charset:utf-8")
add_compile_options("/execution-charset:utf-8")
endif()
set(LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/lib)
set(ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/lib)
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/bin)
set(CMAKE_EXPORT_COMPILE_COMMANDS yes)
option(TEST "是否启动单元测试" ON)
option(SHARED "是否编译为动态库" OFF)
include_directories(${CMAKE_SOURCE_DIR}/include)
# add_library(${PROJECT_NAME} ${SRC})
# add_library(${PROJECT_NAME} SHARED ${SRC})
add_executable(${PROJECT_NAME}main main.c ${SRC})
#编译库文件
add_subdirectory(src)
#测试单元
if (TEST)
enable_testing()
add_subdirectory(tests)
endif()

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 youmetme
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

163
README.en.md Normal file
View File

@@ -0,0 +1,163 @@
# C language logging library logging
## brief
Logging is a lightweight and easy-to-use C language log library that supports log level, log format, log output, log files, and other functions.
## function
- Support log levels: DEBUG, INFO, Warning, ERROR, FATAL
- Support log formats: timestamp, log level, log content
- Support log output: console, file
- Support log files: automatic creation, automatic scrolling, log segmentation
## install
- Conan
```shell
conan create .
```
- cmake
```shell
git clone https://github.com/WangZhongDian/logging.git
cd logging
cmake build -B build . && cd build && cmake --build .
cmake --install .
```
## usage
![](docs/img/2024-09-21-11-44-25.png)
![](docs/img/2024-09-21-11-44-06.png)
### console log
```c
#include "logging.h"
int main() {
Logger *logger = newDefaultLogger("testLogger", LOG_DEBUG);
log_info("This is an info message");
log_error("This is an error message%s", "123");
log_fatal("This is an fatal message");
log_debug("This is a debug message");
log_warning("This is a warning message%s", "123");
destroyDefaultLogger();
return 0;
}
```
### file log
```c
#include "logging.h"
#include "logging/logging-handler.h"
int main() {
Logger *logger = newDefaultLogger("testLogger", LOG_DEBUG);
logger->addHandler(loggingHandlerFile("test1", 1024*1024));
log_info("This is an info message");
log_error("This is an error message%s", "123");
log_fatal("This is an fatal message");
log_debug("This is a debug message");
log_warning("This is a warning message%s", "123");
destroyDefaultLogger();
return 0;
}
```
### Logging filter
> Support adding custom filters, currently with built-in substring filters
> The function of an filter is to redirect filtered logs to the filter's dedicated processor
#### example
Redirects filtered logs to a dedicated file processor
```c
#include "logging.h"
#include <stdio.h>
int main() {
Logger *logger = newDefaultLogger("testLogger", LOG_DEBUG);
log_info("This is an info message");
log_error("This is an error message%s", "123");
log_fatal("This is an fatal message");
log_debug("This is a debug message");
log_warning("This is a warning message%s", "123");
char *test1[] = {"123", "tt", NULL};
log_filter *tint = loggingFilterSubStr(
test1,
LOG_DEBUG,
loggingHandlerFile("test_interceptor", 1024 * 1024),
true);
logger->addFilter(tint);
printf("\n");
printf("filter added\n");
printf("\n");
log_info("This is an info message");
log_error("This is an error message%s", "123");
log_fatal("This is an fatal message");
log_debug("This is a debug message");
log_warning("This is a warning message%s", "123");
destroyDefaultLogger();
return 0;
}
```
#### Multiple substring filters
```c
#include "logging.h"
#include <stdbool.h>
#include <stdio.h>
#include <time.h>
int main() {
Logger *logger = newDefaultLogger("testLogger", LOG_DEBUG);
log_info("This is an info message");
log_error("This is an error message%s", "123");
log_fatal("This is an fatal message");
log_debug("This is a debug message");
log_warning("This is a warning message%s", "123");
char *test1[] = {"This",NULL};
log_filter *tint = loggingFilterSubStr(
test1,
LOG_DEBUG,
loggingHandlerFile("test_interceptor", 1024 * 1024),
false);
logger->addFilter(tint);
char *test2[] = {"123",NULL};
log_filter *tint1 = loggingFilterSubStr(
test2,
LOG_DEBUG,
loggingHandlerFile("test_interceptor1", 1024 * 1024),
true);
logger->addFilter(tint1);
printf("\n");
printf("filter added\n");
printf("\n");
log_info("This is an info message");
log_error("This is an error message%s", "123");
log_fatal("This is an fatal message");
log_debug("This is a debug message");
log_warning("This is a warning message%s", "123");
destroyDefaultLogger();
return 0;
}
```

179
README.md
View File

@@ -0,0 +1,179 @@
# C语言日志库logging
[English](README.en.md)
## 简介
logging是一个轻量级的简单易用C语言日志库支持日志级别、日志格式、日志输出、日志文件等功能。
## 功能
- 支持日志级别DEBUG、INFO、WARN、ERROR、FATAL
- 支持日志格式:时间戳、日志级别、日志内容
- 支持日志输出:控制台、文件
- 支持日志文件:自动创建、自动滚动、日志分割
## 安装
### conan安装使用
```shell
git clone https://github.com/WangZhongDian/logging.git
cd logging
conan create .
```
在你的项目的conanfile.txt中添加
```txt
[requires]
logging/0.5.0
```
```shell
conan install . --build=missing
```
### cmake安装使用
```shell
git clone https://github.com/WangZhongDian/logging.git
cd logging
cmake build -B build . && cd build && cmake --build .
cmake --install .
```
## 使用方法
![](docs/img/2024-09-21-11-44-25.png)
![](docs/img/2024-09-21-11-44-06.png)
### 控制台日志
```c
#include "logging.h"
int main() {
Logger *logger = newDefaultLogger("testLogger", LOG_DEBUG);
Log_info("This is an info message");
Log_error("This is an error message%s", "123");
Log_fatal("This is an fatal message");
Log_debug("This is a debug message");
Log_warning("This is a warning message%s", "123");
destroyDefaultLogger();
return 0;
}
```
### 文件日志
```c
#include "logging.h"
#include "logging/logging-handler.h"
int main() {
Logger *logger = newDefaultLogger("testLogger", LOG_DEBUG);
logger->addHandler(loggingHandlerFile("test1", 1024*1024));
Log_info("This is an info message");
Log_error("This is an error message%s", "123");
Log_fatal("This is an fatal message");
Log_debug("This is a debug message");
Log_warning("This is a warning message%s", "123");
destroyDefaultLogger();
return 0;
}
```
### 日志过滤器
> 支持添加自定义的过滤器, 目前内置了子串过滤器
> 过滤器的作用:可以将过滤到的日志重定向到过滤器的专属处理器中
#### 单个子串过滤器
将过滤到的日志重定向到专属处理器中
```c
#include "logging.h"
#include <stdio.h>
int main() {
Logger *logger = newDefaultLogger("testLogger", LOG_DEBUG);
Log_info("This is an info message");
Log_error("This is an error message%s", "123");
Log_fatal("This is an fatal message");
Log_debug("This is a debug message");
Log_warning("This is a warning message%s", "123");
char *test1[] = {"123", "tt", NULL};
log_filter *tint = loggingFilterSubStr(
test1,
LOG_DEBUG,
loggingHandlerFile("test_interceptor", 1024 * 1024),
true);
logger->addFilter(tint);
printf("\n");
printf("filter added\n");
printf("\n");
Log_info("This is an info message");
Log_error("This is an error message%s", "123");
Log_fatal("This is an fatal message");
Log_debug("This is a debug message");
Log_warning("This is a warning message%s", "123");
destroyDefaultLogger();
return 0;
}
```
#### 多个子串过滤器
```c
#include "logging.h"
#include <stdbool.h>
#include <stdio.h>
#include <time.h>
int main() {
Logger *logger = newDefaultLogger("testLogger", LOG_DEBUG);
Log_info("This is an info message");
Log_error("This is an error message%s", "123");
Log_fatal("This is an fatal message");
Log_debug("This is a debug message");
Log_warning("This is a warning message%s", "123");
char *test1[] = {"This",NULL};
log_filter *tint = loggingFilterSubStr(
test1,
LOG_DEBUG,
loggingHandlerFile("test_interceptor", 1024 * 1024),
false);
logger->addFilter(tint);
char *test2[] = {"123",NULL};
log_filter *tint1 = loggingFilterSubStr(
test2,
LOG_DEBUG,
loggingHandlerFile("test_interceptor1", 1024 * 1024),
true);
logger->addFilter(tint1);
printf("\n");
printf("filter added\n");
printf("\n");
Log_info("This is an info message");
Log_error("This is an error message%s", "123");
Log_fatal("This is an fatal message");
Log_debug("This is a debug message");
Log_warning("This is a warning message%s", "123");
destroyDefaultLogger();
return 0;
}
```

58
conanfile.py Normal file
View File

@@ -0,0 +1,58 @@
from conan import ConanFile
from conan.tools.cmake import CMake, CMakeToolchain, CMakeDeps, cmake_layout
from conan.tools.files import copy
import os
class loggingRecipe(ConanFile):
name = "logging"
version = "0.5.1"
license = "MIT"
author = "321640253@qq.com"
url = "https://github.com/WangZhongDian/logging.git"
description = "一个纯C的简单易用的日志库"
topics = ("logging", "C", "simple", "easy-to-use", "log","Logging")
settings = "os", "compiler", "build_type", "arch"
options = {"shared": [True, False], "fPIC": [True, False],"test":[True,False]}
default_options = {"shared": False, "fPIC": True,"test":True}
exports_sources = "include/*", "CMakeLists.txt", "src/*", "tests/*"
def config_options(self):
if self.settings.os == "Windows":
del self.options.fPIC
def configure(self):
if self.options.shared:
self.options.rm_safe("fPIC")
def layout(self):
cmake_layout(self)
def generate(self):
deps = CMakeDeps(self)
deps.generate()
tc = CMakeToolchain(self)
tc.variables["TEST"] = True if self.options.test else False
tc.variables["SHARED"] = True if self.options.shared else False
tc.generate()
def build(self):
cmake = CMake(self)
cmake.configure()
cmake.build()
if self.options.test:
cmake.test()
def package(self):
copy(self, "LICENSE", src=self.source_folder, dst=os.path.join(self.package_folder, "licenses"))
copy(self, pattern="*.h", src=os.path.join(self.source_folder, "include"), dst=os.path.join(self.package_folder, "include"))
copy(self, pattern="*.a", src=self.build_folder, dst=os.path.join(self.package_folder, "lib"), keep_path=False)
copy(self, pattern="*.so", src=self.build_folder, dst=os.path.join(self.package_folder, "lib"), keep_path=False)
copy(self, pattern="*.lib", src=self.build_folder, dst=os.path.join(self.package_folder, "lib"), keep_path=False)
copy(self, pattern="*.dll", src=self.build_folder, dst=os.path.join(self.package_folder, "bin"), keep_path=False)
copy(self, pattern="*.dylib", src=self.build_folder, dst=os.path.join(self.package_folder, "lib"), keep_path=False)
def package_info(self):
self.cpp_info.libs = ["logging"]

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

View File

@@ -1,57 +1,65 @@
#ifndef __LOGGING_H__
#define __LOGGING_H__
#ifndef __LOGGING_H
#define __LOGGING_H
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <time.h>
#include <stdbool.h>
typedef enum {
LOG_ERROR,
LOG_WARNING,
LOG_INFO,
LOG_DEBUG,
} log_level;
#include "logging/logging-core.h"
#include "logging/logging-filter.h"
#include "logging/logging-handler.h"
typedef enum {
L_ERROR,
L_OK,
} log_status;
#ifdef __cplusplus
extern "C" {
#endif
typedef struct Handler {
FILE* file;
log_level level;
} log_Handler;
#define Log_fatal(format, ...) \
log_fatal(__FILE__, __LINE__, format, ##__VA_ARGS__)
#define Log_error(format, ...) \
log_error(__FILE__, __LINE__, format, ##__VA_ARGS__)
#define Log_warning(format, ...) \
log_warning(__FILE__, __LINE__, format, ##__VA_ARGS__)
#define Log_info(format, ...) \
log_info(__FILE__, __LINE__, format, ##__VA_ARGS__)
#define Log_debug(format, ...) \
log_debug(__FILE__, __LINE__, format, ##__VA_ARGS__)
//日志操作器
typedef struct Logger
{
log_level level;
log_Handler* handler;
void (*error)(const char* format, ...);
void (*warning)(const char* format, ...);
void (*info)(const char* format, ...);
void (*debug)(const char* format, ...);
typedef struct Logger {
log_level level;
log_Handler *handler;
log_filter *filter;
const char *name;
bool (*addHandler)(log_Handler *handler);
bool (*addFilter)(log_filter *filter);
} Logger;
void log_fatal(const char *file, int line, const char *format, ...);
void log_error(const char *file, int line, const char *format, ...);
void log_warning(const char *file, int line, const char *format, ...);
void log_info(const char *file, int line, const char *format, ...);
void log_debug(const char *file, int line, const char *format, ...);
//日志类对象
typedef struct Logging {
Logger* (*getLogger)(const char* name, log_level level);
log_status (*setLevel)(Logger* logger, log_level level);
// void (*addFormat)(const char* format);
// void (*addFilter)(const char* filter);
void (*addHandler)(log_Handler* handler);
} Logging;
/**
* @brief
创建默认日志对象,日志对象为单例模式后续可通过getDefaultLogger方法获取
重复调用该方法不会创建新的日志对象,只会返回默认日志对象,并且会修改默认日志对象的名称和等级
* @param name 日志名称
* @param level 日志等级
* @return Logger* 日志对象指针
*/
Logger *newDefaultLogger(const char *name, log_level level);
/**
* @brief 获取默认日志对象
*/
Logger *getDefaultLogger(void);
/**
* @brief 销毁日志对象,该方法会销毁默认日志对象
*/
log_status destroyDefaultLogger(void);
Logging* createLogging();
#ifdef __cplusplus
}
#endif
log_Handler* fileHandler(const char* name,log_level level);
#endif // __LOGGING_H
#endif // __LOGGING_H__

View File

@@ -0,0 +1,25 @@
#ifndef __LOGGING_CORE_H__
#define __LOGGING_CORE_H__
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
LOG_FATAL = 0,
LOG_ERROR,
LOG_WARNING,
LOG_INFO,
LOG_DEBUG,
} log_level;
typedef enum {
L_ERROR,
L_OK,
} log_status;
#ifdef __cplusplus
}
#endif
#endif // __LOGGING_CORE_H__

View File

@@ -0,0 +1,41 @@
#ifndef __LOGGING_INTERCEPTOR_H__
#define __LOGGING_INTERCEPTOR_H__
#include "logging-core.h"
#include "logging-handler.h"
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct log_filter {
log_level level;
log_Handler *handler;
bool jump_out;
bool (*_dispose)(struct log_filter *filter,
log_level level,
const char *message,
...);
void (*_free)(struct log_filter *filter);
struct log_filter *next;
} log_filter;
/**
* @brief 子字符串过滤器
* @param keywords: 关键字数组
* @param count: 关键字数组长度
* @param level: 过滤截日志等级
* @param handler: 日志处理器,用于处理过滤下来的日志
* @return log_filter *
*/
log_filter *loggingFilterSubStr(char *keywords[],
log_level level,
log_Handler *handler,
bool jump_out);
#ifdef __cplusplus
}
#endif
#endif // __LOGGING_INTERCEPTOR_H__

View File

@@ -0,0 +1,36 @@
#ifndef __LOGGING_HANDLER_H__
#define __LOGGING_HANDLER_H__
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct log_Handler {
void *stream;
bool apply_color;
void (*_free)(struct log_Handler *handler);
void (*output)(struct log_Handler *handler, const char *message);
} log_Handler;
/**
* @brief 文件处理器
* @param name 文件名
* @param max_size 文件最大大小
* @return
*/
log_Handler *loggingHandlerFile(const char *name, unsigned int max_size);
/**
* @brief 控制台处理器
* @param
* @return
*/
log_Handler *loggingHandlerConsole();
#ifdef __cplusplus
}
#endif
#endif //__LOGGING_HANDLER_H__

14
main.c
View File

@@ -1,14 +0,0 @@
#include "logging.h"
int main() {
// Your code goes here
Logging *log = createLogging();
Logger *logger = log->getLogger("testLogger",LOG_INFO);
logger->info("This is an info message");
logger->error("This is an error message");
logger->debug("This is a debug message");
logger->warning("This is a warning message");
return 0;
}

6
makefile Normal file
View File

@@ -0,0 +1,6 @@
.PHONY:format
format:
bash script/format.sh

3
script/format.sh Normal file
View File

@@ -0,0 +1,3 @@
#!/bin/bash
# Run clang-format on all C/C++ files in this directory and below.
find . -name "*.c" -o -name "*.h" -exec clang-format -style=file -i {} +

2
script/test.sh Executable file
View File

@@ -0,0 +1,2 @@
#!/bin/bash
cmake build -B build . && cd build && cmake --build . && ctest

5
script/test_windows.ps1 Normal file
View File

@@ -0,0 +1,5 @@
cmake build -B build .
Set-Location -Path "./build"
cmake --build .
ctest
Set-Location -Path ".."

13
src/CMakeLists.txt Normal file
View File

@@ -0,0 +1,13 @@
project(logging)
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} SRC)
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/handler SRC)
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/filter SRC)
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/utils SRC)
if (SHARED)
add_library(${PROJECT_NAME} SHARED ${SRC})
else()
add_library(${PROJECT_NAME} ${SRC})
endif()

View File

@@ -0,0 +1,133 @@
#include "logging/logging-filter.h"
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
typedef struct keywords_s {
char *key;
struct keywords_s *next;
} keywords_t;
static void get_next(char *str, int *next) {
next[1] = 0;
int i = 1;
int j = 0;
while (i < strlen(str)) {
if (j == 0 || str[i] == str[j]) {
next[++i] = ++j;
} else {
j = next[j];
}
}
}
static bool kmp_search(char *substr, char *master) {
if (substr == NULL)
return true; // 空串全匹配
if (master == NULL)
return false;
int i = 0;
int j = 0;
int substrlen = strlen(substr);
int masterlen = strlen(master);
int *next = (int *)malloc(sizeof(int) * (substrlen + 1));
get_next(substr, next);
while (i < masterlen && j < substrlen) {
if (master[i] == substr[j]) {
i++;
j++;
} else {
if (j == 0) {
i++;
} else {
j = next[j];
}
}
}
free(next);
if (j == substrlen)
return true;
else
return false;
}
static bool _disposeSubstring(log_filter *filter,
log_level level,
const char *message,
...) {
int count = 0;
keywords_t *keyword = (keywords_t *)(filter + 1);
if (keyword->key == NULL && keyword->next == NULL) {
if (level <= filter->level)
return true;
return false;
}
while (keyword != NULL && level <= filter->level) {
if (kmp_search(keyword->key, (char *)message))
return true;
keyword = keyword->next;
}
return false;
}
static void _freeFilter(log_filter *filter) {
keywords_t *it_keyword =
(keywords_t *)(filter + 1); // it_keyword 不是起始地址请勿free
keywords_t *keyword = it_keyword->next;
keywords_t *next = NULL;
while (keyword != NULL) {
next = keyword->next;
free(keyword->key);
free(keyword);
keyword = next;
}
if (filter->handler != NULL) {
filter->handler->_free(filter->handler);
}
if (it_keyword->key != NULL)
free(it_keyword->key);
free(filter);
}
log_filter *loggingFilterSubStr(char *keywords[],
log_level level,
log_Handler *handler,
bool jump_out) {
// 分配log_filter和keywords_t的连续内存
log_filter *filter =
(log_filter *)malloc(sizeof(log_filter) + sizeof(keywords_t));
filter->_dispose = _disposeSubstring;
filter->handler = handler;
filter->level = level;
filter->jump_out = jump_out;
filter->_free = _freeFilter;
keywords_t *keyword = (keywords_t *)(filter + 1);
keyword->key = NULL;
int count = 0;
if (keywords[count] != NULL) {
keyword->key = strdup(keywords[count]);
count++;
keyword->next = NULL;
}
while (keywords[count] != NULL) {
keyword->next = (keywords_t *)malloc(sizeof(keywords_t));
keyword = keyword->next;
keyword->key = strdup(keywords[count]);
count++;
}
keyword->next = NULL;
return filter;
}

View File

@@ -0,0 +1,20 @@
#include "logging/logging-handler.h"
#include <stdio.h>
#include <stdlib.h>
static void __freeHandlerConsole(log_Handler *handler) { free(handler); }
static void __outputHandlerConsole(log_Handler *handler, const char *message) {
fputs(message, handler->stream);
}
log_Handler *loggingHandlerConsole() {
log_Handler *handler = (log_Handler *)malloc(sizeof(log_Handler));
handler->stream = stdout;
handler->apply_color = true;
handler->_free = __freeHandlerConsole;
handler->output = __outputHandlerConsole;
return handler;
}

View File

@@ -0,0 +1,98 @@
#include "logging/logging-handler.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 根据log_Handler结构体指针起始获取log_Handler_file_ex_t结构体指针
// log_Handler_file_ex_t与log_Handler处于连续内存中
// 使用char*指针进行偏移达到以偏移1个字节为单位的偏移
#define Handler_file_EX_PRT(handler) \
((log_Handler_file_ex_t *)((char *)handler + sizeof(log_Handler)))
#define FILE_NAME_MAX_SIZE 50
// 文件日志处理器的扩展
typedef struct log_Handler_file_ex_s {
unsigned int file_size;
unsigned int file_size_max;
unsigned int suffix;
char *file_name;
} log_Handler_file_ex_t;
static unsigned int getFileSize(FILE *fp) {
fseek(fp, 0L, SEEK_END);
return ftell(fp);
}
static void __freeFileHandler(log_Handler *handler) {
fclose(handler->stream);
free(Handler_file_EX_PRT(handler)->file_name);
free(handler);
}
static void changeFile(log_Handler *handler) {
log_Handler_file_ex_t *handler_ex = Handler_file_EX_PRT(handler);
fclose(handler->stream);
char new_file_name[FILE_NAME_MAX_SIZE];
sprintf(new_file_name,
"%s_%d.log",
handler_ex->file_name,
++handler_ex->suffix);
handler->stream = fopen(new_file_name, "at");
handler_ex->file_size = getFileSize(handler->stream);
}
static void outputFileHandler(log_Handler *handler, const char *message) {
fputs(message, handler->stream);
log_Handler_file_ex_t *handler_ex = Handler_file_EX_PRT(handler);
handler_ex->file_size += strlen(message);
if (handler_ex->file_size > handler_ex->file_size_max)
changeFile(handler);
}
log_Handler *loggingHandlerFile(const char *name, unsigned int max_size) {
char new_file_name[FILE_NAME_MAX_SIZE];
int suffix = 0;
unsigned int file_size;
FILE *fp = NULL;
log_Handler *handler = NULL;
log_Handler_file_ex_t *handler_ex = NULL;
/// 获取未写满于设置最大文件大小的文件名
do {
sprintf(new_file_name, "%s_%d.log", name, suffix++);
fp = fopen(new_file_name, "at");
if (fp == NULL)
goto ERROR;
file_size = getFileSize(fp);
} while (file_size > max_size);
/// 分配log_Handler与记录文件大小的空间
handler = (log_Handler *)malloc(sizeof(log_Handler) +
sizeof(log_Handler_file_ex_t));
if (handler == NULL)
goto ERROR;
handler_ex = Handler_file_EX_PRT(handler);
handler_ex->file_size_max = max_size;
handler_ex->file_size = file_size;
handler_ex->suffix = suffix;
handler_ex->file_name = strdup(name);
if (handler_ex->file_name == NULL)
goto ERROR;
handler->stream = fp;
handler->apply_color = false;
handler->_free = __freeFileHandler;
handler->output = outputFileHandler;
return handler;
ERROR:
if (fp)
fclose(fp);
if (handler) {
free(Handler_file_EX_PRT(handler)->file_name); // 直接释放无需检查NULL
free(handler);
}
return NULL;
}

View File

@@ -1,16 +0,0 @@
#include "logging.h"
log_Handler* fileHandler(const char* name,log_level level){
FILE* fp = fopen(name, "w");
log_Handler* handler = (log_Handler*)malloc(sizeof(log_Handler));
handler->file = fp;
handler->level = level;
return handler;
}

View File

@@ -1,101 +1,255 @@
#include "logging.h"
#include "logging/logging-core.h"
#include "logging/logging-handler.h"
#include "utils/logging-utils.h"
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
Logger* G_LOGGER = NULL;
#define RED "\033[0;31m"
#define GREEN "\033[0;32m"
#define RED "\033[0;31m"
#define RED_B "\033[0;41m"
#define GREEN "\033[0;32m"
#define YELLOW "\033[0;33m"
#define BLUE "\033[0;34m"
#define RESET "\033[0m"
#define CYAN "\033[0;36m"
#define MAGENTA "\033[0;35m"
#define WHITE "\033[0;37m"
#define BLACK "\033[0;30m"
#define BLUE "\033[0;34m"
#define RESET "\033[0m"
#define CYAN "\033[0;36m"
#define LOG_BUFFER_SIZE 4096 // 日志缓冲区大小,单个日志长度不能超过该值
static void getTimeStr(char * timeStr){
time_t t = time(NULL);
struct tm* p = localtime(&t);
char _timeStr[18];
strftime(_timeStr, sizeof(_timeStr), "%Y-%m-%d %H:%M:%S", p);
strcpy(timeStr, _timeStr);
}
static void addHandler(log_Handler* handler){
if (G_LOGGER == NULL){
return;
}
G_LOGGER->handler = handler;
}
//*************************记录日志******************************* */
static void error(const char* format, ...){
if (G_LOGGER->level >= LOG_ERROR){
char timeStr[18];
getTimeStr(timeStr);
printf("%sError%s %s %s\n",RED,RESET, timeStr, format);
}
}
static void warning(const char* format, ...){
if (G_LOGGER->level >= LOG_WARNING){
char timeStr[18];
getTimeStr(timeStr);
printf("%sWarning%s %s %s\n",YELLOW,RESET, timeStr, format);
}
}
static void info(const char* format, ...){
if (G_LOGGER->level >= LOG_INFO){
char timeStr[18];
getTimeStr(timeStr);
printf("%sInfo%s %s %s\n",GREEN,RESET, timeStr, format);
}
}
static void debug(const char* format, ...){
if (G_LOGGER->level >= LOG_DEBUG){
char timeStr[18];
getTimeStr(timeStr);
printf("%sDebug%s %s %s\n",CYAN,RESET, timeStr, format);
}
}
//*************************记录日志******************************* */
static Logger *G_LOGGER = NULL; // 全局日志对象,唯一实例
/**
* @description :获取一个日志操作对象
* @param
* @return
*/
static Logger* getLogger(const char* name, log_level level){
Logger* logger = (Logger*)malloc(sizeof(Logger));
logger->error = error;
logger->warning = warning;
logger->info = info;
logger->debug = debug;
* @brief 为日志添加一个handler
* @param handler 处理器对象
*/
static bool addHandler(log_Handler *handler) {
if (G_LOGGER == NULL || handler == NULL) {
return false;
}
if (G_LOGGER->handler == NULL) {
G_LOGGER->handler = handler;
return true;
}
logger->level = level;
logger->handler = NULL;
G_LOGGER->handler->_free(G_LOGGER->handler);
G_LOGGER->handler = handler;
return true;
}
G_LOGGER = logger;
/**
* @brief 为日志添加一个filter
* @param filter 过滤器对象
*/
static bool addFilter(log_filter *filter) {
if (G_LOGGER == NULL || filter == NULL) {
return false;
}
if (G_LOGGER->filter == NULL) {
G_LOGGER->filter = filter;
G_LOGGER->filter->next = NULL;
return true;
}
log_filter *it = G_LOGGER->filter;
while (it->next != NULL) {
it = it->next;
}
it->next = filter;
filter->next = NULL;
return true;
}
/**
* @brief 输出到handler
* @param handler 处理器对象
* @param level 日志等级
* @param color 应用的颜色
* @param message 日志内容
*/
static void output_to_handler(log_Handler *handler,
char *level,
const char *color,
const char *message) {
char timeStr[20];
getTimeStr(timeStr);
char logStr[LOG_BUFFER_SIZE * 2];
if (handler->apply_color)
snprintf(logStr,
LOG_BUFFER_SIZE * 2,
"[%s]: %s %s%s%s %s\n",
G_LOGGER->name,
timeStr,
color,
level,
RESET,
message);
else
snprintf(logStr,
LOG_BUFFER_SIZE * 2,
"[%s]: %s %s %s\n",
G_LOGGER->name,
timeStr,
level,
message);
handler->output(handler, logStr);
}
/**
* @brief 内部日志打印处理核心函数
* @param level 日志等级
* @param color 应用的颜色
* @param message 日志内容
* @param ... 格式化参数列表
* @return
*/
static void _builtin_cope(log_level level_e,
char *level,
const char *color,
const char *message) {
if (G_LOGGER == NULL) {
return;
}
if (G_LOGGER->handler == NULL) {
return;
}
log_filter *it = G_LOGGER->filter;
log_Handler *handler = G_LOGGER->handler;
while (it != NULL) {
if (it->_dispose(it, level_e, message)) {
output_to_handler(it->handler, level, color, message);
if (it->jump_out)
return;
}
it = it->next;
}
output_to_handler(handler, level, color, message);
}
void log_fatal(const char *file, int line, const char *message, ...) {
if (G_LOGGER->level >= LOG_ERROR) {
char logStr[LOG_BUFFER_SIZE];
char finalLogStr[LOG_BUFFER_SIZE * 2];
va_list args;
va_start(args, message);
vsprintf(logStr, message, args);
va_end(args);
snprintf(
finalLogStr, LOG_BUFFER_SIZE * 2, "[%s:%d] %s", file, line, logStr);
_builtin_cope(LOG_FATAL, "Fatal", RED_B, finalLogStr);
}
}
void log_error(const char *file, int line, const char *message, ...) {
if (G_LOGGER->level >= LOG_ERROR) {
char logStr[LOG_BUFFER_SIZE];
char finalLogStr[LOG_BUFFER_SIZE * 2];
va_list args;
va_start(args, message);
vsprintf(logStr, message, args);
va_end(args);
snprintf(
finalLogStr, LOG_BUFFER_SIZE * 2, "[%s:%d] %s", file, line, logStr);
_builtin_cope(LOG_ERROR, "Error", RED, finalLogStr);
}
}
void log_warning(const char *file, int line, const char *message, ...) {
if (G_LOGGER->level >= LOG_WARNING) {
char logStr[LOG_BUFFER_SIZE];
char finalLogStr[LOG_BUFFER_SIZE * 2];
va_list args;
va_start(args, message);
vsprintf(logStr, message, args);
va_end(args);
snprintf(
finalLogStr, LOG_BUFFER_SIZE * 2, "[%s:%d] %s", file, line, logStr);
_builtin_cope(LOG_WARNING, "Warning", YELLOW, finalLogStr);
}
}
void log_info(const char *file, int line, const char *message, ...) {
if (G_LOGGER->level >= LOG_INFO) {
char logStr[LOG_BUFFER_SIZE];
char finalLogStr[LOG_BUFFER_SIZE * 2];
va_list args;
va_start(args, message);
vsprintf(logStr, message, args);
va_end(args);
snprintf(
finalLogStr, LOG_BUFFER_SIZE * 2, "[%s:%d] %s", file, line, logStr);
_builtin_cope(LOG_INFO, "Info", GREEN, finalLogStr);
}
}
void log_debug(const char *file, int line, const char *message, ...) {
if (G_LOGGER->level >= LOG_DEBUG) {
char logStr[LOG_BUFFER_SIZE];
char finalLogStr[LOG_BUFFER_SIZE * 2];
va_list args;
va_start(args, message);
vsprintf(logStr, message, args);
va_end(args);
snprintf(
finalLogStr, LOG_BUFFER_SIZE * 2, "[%s:%d] %s", file, line, logStr);
_builtin_cope(LOG_DEBUG, "Debug", CYAN, finalLogStr);
}
}
Logger *newDefaultLogger(const char *name, log_level level) {
if (G_LOGGER != NULL) {
G_LOGGER->name = name;
G_LOGGER->level = level;
return G_LOGGER;
}
Logger *logger = (Logger *)malloc(sizeof(Logger));
logger->addHandler = addHandler;
logger->addFilter = addFilter;
logger->level = level;
logger->handler = loggingHandlerConsole();
logger->name = name;
logger->filter = NULL;
G_LOGGER = logger;
return G_LOGGER;
}
/**
* @description :创建一个日志对象
* @return :Logging* 返回一个日志对象
*/
Logging* createLogging(){
Logging* logging = (Logging*)malloc(sizeof(Logging));
logging->getLogger = getLogger;
logging->addHandler = addHandler;
return logging;
* @brief 销毁日志对象
*/
log_status destroyDefaultLogger(void) {
if (G_LOGGER != NULL) {
if (G_LOGGER->handler != NULL) {
G_LOGGER->handler->_free(G_LOGGER->handler);
}
if (G_LOGGER->filter != NULL) {
log_filter *it = G_LOGGER->filter;
log_filter *next = NULL;
while (it != NULL) {
next = it->next;
it->_free(it);
it = next;
}
}
free(G_LOGGER);
G_LOGGER = NULL;
}
return L_OK;
}
Logger *getDefaultLogger(void) {
if (G_LOGGER == NULL) {
return NULL;
}
return G_LOGGER;
}

15
src/utils/logging-utils.c Normal file
View File

@@ -0,0 +1,15 @@
#include "logging-utils.h"
#include <string.h>
#include <time.h>
/**
* @brief 获取当前时间字符串
* @param timeStr 存储时间字符串缓冲区指针
*/
void getTimeStr(char *timeStr) {
time_t t = time(NULL);
struct tm *p = localtime(&t);
char _timeStr[20];
strftime(_timeStr, sizeof(_timeStr), "%Y-%m-%d %H:%M:%S", p);
strcpy(timeStr, _timeStr);
}

View File

@@ -0,0 +1,5 @@
#ifndef __LOGGING_UTILS_H__
#define __LOGGING_UTILS_H__
void getTimeStr(char *timeStr);
#endif // __LOGGING_UTILS_H__

30
tests/CMakeLists.txt Normal file
View File

@@ -0,0 +1,30 @@
project(test)
enable_testing()
#测试简单基本应用
add_executable(${PROJECT_NAME}simple test-simple.c)
target_link_libraries(${PROJECT_NAME}simple logging)
if(UNIX)
add_test(test_simple ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}simple)
elseif(WIN32)
add_test(test_simple ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}simple.exe)
endif()
#测试简单基本应用
add_executable(${PROJECT_NAME}file test-log-file.c)
target_link_libraries(${PROJECT_NAME}file logging)
if(UNIX)
add_test(test_file ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}file)
elseif(WIN32)
add_test(test_file ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}file.exe)
endif()
#测试拦截器
add_executable(${PROJECT_NAME}filter test-filter.c)
target_link_libraries(${PROJECT_NAME}filter logging)
if(UNIX)
add_test(test_filter ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}filter)
elseif(WIN32)
add_test(test_filter ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}filter.exe)
endif()

49
tests/test-filter.c Normal file
View File

@@ -0,0 +1,49 @@
#include "logging.h"
#include "logging/logging-core.h"
#include "logging/logging-filter.h"
#include <stdbool.h>
#include <stdio.h>
#include <time.h>
int main() {
Logger *logger = newDefaultLogger("testLogger", LOG_DEBUG);
Log_info("This is an info message");
Log_error("This is an error message%s", "123");
Log_fatal("This is an fatal message");
Log_debug("This is a debug message");
Log_warning("This is a warning message%s", "123");
char *test1[] = {"This", NULL};
log_filter *tint =
loggingFilterSubStr(test1,
LOG_DEBUG,
loggingHandlerFile("test_interceptor", 1024 * 1024),
false);
logger->addFilter(tint);
char *test2[] = {"123", NULL};
log_filter *tint1 = loggingFilterSubStr(
test2,
LOG_ERROR,
loggingHandlerFile("test_interceptor1", 1024 * 1024),
true);
logger->addFilter(tint1);
printf("\n");
printf("filter added\n");
printf("\n");
Log_info("This is an info message");
Log_error("This is an error message%s", "123");
Log_fatal("This is an fatal message");
Log_debug("This is a debug message");
Log_warning("This is a warning message%s", "123");
destroyDefaultLogger();
return 0;
}

17
tests/test-log-file.c Normal file
View File

@@ -0,0 +1,17 @@
#include "logging.h"
#include "logging/logging-handler.h"
int main() {
Logger *logger = newDefaultLogger("testLogger", LOG_DEBUG);
log_Handler *hander = loggingHandlerFile("test_log", 1024 * 1024 * 10);
logger->addHandler(hander);
Log_info("This is an info message");
Log_error("This is an error message%s", "123");
Log_fatal("This is an fatal message");
Log_debug("This is a debug message");
Log_warning("This is a warning message%s", "123");
destroyDefaultLogger();
return 0;
}

14
tests/test-simple.c Normal file
View File

@@ -0,0 +1,14 @@
#include "logging.h"
int main() {
Logger *logger = newDefaultLogger("testLogger", LOG_DEBUG);
Log_info("This is an info message");
Log_error("This is an error message%s", "123");
Log_fatal("This is an fatal message");
Log_debug("This is a debug message");
Log_warning("This is a warning message%s", "123");
destroyDefaultLogger();
return 0;
}