pax_global_header00006660000000000000000000000064146057000500014507gustar00rootroot0000000000000052 comment=7fe61ca6265516e9327a51fc394b2adb126c2ef3 zlog-1.2.18/000077500000000000000000000000001460570005000125535ustar00rootroot00000000000000zlog-1.2.18/.clang-format000066400000000000000000000005101460570005000151220ustar00rootroot00000000000000# Coding Style Proposal - Verify Language: Cpp BasedOnStyle: Microsoft ################################################ ReflowComments: false SortIncludes: false # can break build; turning off SortUsingDeclarations: false # can break build; turning off FixNamespaceComments: false BreakStringLiterals: false ColumnLimit: 100 zlog-1.2.18/.gitignore000066400000000000000000000116221460570005000145450ustar00rootroot00000000000000# Created by https://www.toptal.com/developers/gitignore/api/c,c++,bazel,cmake,linux,macos,windows,jetbrains+all,visualstudiocode # Edit at https://www.toptal.com/developers/gitignore?templates=c,c++,bazel,cmake,linux,macos,windows,jetbrains+all,visualstudiocode ### Bazel ### # gitignore template for Bazel build system # website: https://bazel.build/ # Ignore all bazel-* symlinks. There is no full list since this can change # based on the name of the directory bazel is cloned into. /bazel-* # Directories for the Bazel IntelliJ plugin containing the generated # IntelliJ project files and plugin configuration. Seperate directories are # for the IntelliJ, Android Studio and CLion versions of the plugin. /.ijwb/ /.aswb/ /.clwb/ ### C ### # Prerequisites *.d # Object files *.o *.ko *.obj *.elf # Linker output *.ilk *.map *.exp # Precompiled Headers *.gch *.pch # Libraries *.lib *.a *.la *.lo # Shared objects (inc. Windows DLLs) *.dll *.so *.so.* *.dylib # Executables *.exe *.out *.app *.i*86 *.x86_64 *.hex # Debug files *.dSYM/ *.su *.idb *.pdb # Kernel Module Compile Results *.mod* *.cmd .tmp_versions/ modules.order Module.symvers Mkfile.old dkms.conf ### C++ ### # Prerequisites # Compiled Object files *.slo # Precompiled Headers # Compiled Dynamic libraries # Fortran module files *.mod *.smod # Compiled Static libraries *.lai # Executables ### CMake ### CMakeLists.txt.user CMakeCache.txt CMakeFiles CMakeScripts Testing Makefile cmake_install.cmake install_manifest.txt compile_commands.json CTestTestfile.cmake _deps ### CMake Patch ### # External projects *-prefix/ ### JetBrains+all ### # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 # User-specific stuff .idea/**/workspace.xml .idea/**/tasks.xml .idea/**/usage.statistics.xml .idea/**/dictionaries .idea/**/shelf # AWS User-specific .idea/**/aws.xml # Generated files .idea/**/contentModel.xml # Sensitive or high-churn files .idea/**/dataSources/ .idea/**/dataSources.ids .idea/**/dataSources.local.xml .idea/**/sqlDataSources.xml .idea/**/dynamic.xml .idea/**/uiDesigner.xml .idea/**/dbnavigator.xml # Gradle .idea/**/gradle.xml .idea/**/libraries # Gradle and Maven with auto-import # When using Gradle or Maven with auto-import, you should exclude module files, # since they will be recreated, and may cause churn. Uncomment if using # auto-import. # .idea/artifacts # .idea/compiler.xml # .idea/jarRepositories.xml # .idea/modules.xml # .idea/*.iml # .idea/modules # *.iml # *.ipr # CMake cmake-build-*/ # Mongo Explorer plugin .idea/**/mongoSettings.xml # File-based project format *.iws # IntelliJ out/ # mpeltonen/sbt-idea plugin .idea_modules/ # JIRA plugin atlassian-ide-plugin.xml # Cursive Clojure plugin .idea/replstate.xml # SonarLint plugin .idea/sonarlint/ # Crashlytics plugin (for Android Studio and IntelliJ) com_crashlytics_export_strings.xml crashlytics.properties crashlytics-build.properties fabric.properties # Editor-based Rest Client .idea/httpRequests # Android studio 3.1+ serialized cache file .idea/caches/build_file_checksums.ser ### JetBrains+all Patch ### # Ignore everything but code style settings and run configurations # that are supposed to be shared within teams. .idea/* !.idea/codeStyles !.idea/runConfigurations ### Linux ### *~ # temporary files which can be created if a process still has a handle open of a deleted file .fuse_hidden* # KDE directory preferences .directory # Linux trash folder which might appear on any partition or disk .Trash-* # .nfs files are created when an open file is removed but is still being accessed .nfs* ### macOS ### # General .DS_Store .AppleDouble .LSOverride # Icon must end with two \r Icon # Thumbnails ._* # Files that might appear in the root of a volume .DocumentRevisions-V100 .fseventsd .Spotlight-V100 .TemporaryItems .Trashes .VolumeIcon.icns .com.apple.timemachine.donotpresent # Directories potentially created on remote AFP share .AppleDB .AppleDesktop Network Trash Folder Temporary Items .apdisk ### macOS Patch ### # iCloud generated files *.icloud ### VisualStudioCode ### .vscode/* !.vscode/settings.json !.vscode/tasks.json !.vscode/launch.json !.vscode/extensions.json !.vscode/*.code-snippets # Local History for Visual Studio Code .history/ # Built Visual Studio Code Extensions *.vsix ### VisualStudioCode Patch ### # Ignore all local history of files .history .ionide ### Windows ### # Windows thumbnail cache files Thumbs.db Thumbs.db:encryptable ehthumbs.db ehthumbs_vista.db # Dump file *.stackdump # Folder config file [Dd]esktop.ini # Recycle Bin used on file shares $RECYCLE.BIN/ # Windows Installer files *.cab *.msi *.msix *.msm *.msp # Windows shortcuts *.lnk # build folder /build/ # End of https://www.toptal.com/developers/gitignore/api/c,c++,bazel,cmake,linux,macos,windows,jetbrains+all,visualstudiocodezlog-1.2.18/BUILD.bazel000066400000000000000000000007031460570005000144310ustar00rootroot00000000000000load("@rules_cc//cc:defs.bzl", "cc_library") cc_library( name = "zlog", srcs = glob( [ "src/*.c", "src/*.h", ], exclude = [ "src/zlog_win.c", "src/zlog_win.h", ], ), hdrs = glob( [ "src/*.h", "*.h", ], exclude = [ "src/zlog_win.h", ], ), visibility = ["//visibility:public"], ) zlog-1.2.18/CMakeLists.txt000066400000000000000000000037321460570005000153200ustar00rootroot00000000000000# LICENSE: Apache License 2.0 # Copyright (c) Hardy Simpson # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #/ cmake_minimum_required(VERSION 2.8.5) message(STATUS "path : ${CMAKE_FIND_ROOT_PATH}") project(zlog) message(STATUS "path : ${CMAKE_FIND_ROOT_PATH}") set(CMAKE_MODULE_PATH ${zlog_SOURCE_DIR}/cmake) SET(CPACK_PACKAGE_VERSION_MAJOR "1") SET(CPACK_PACKAGE_VERSION_MINOR "2") SET(CPACK_PACKAGE_VERSION_PATCH "12") SET(CPACK_RPM_PACKAGE_RELEASE 1) # release version. SET(CPACK_PACKAGE_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}") SET(ZLOG_VERSION ${CPACK_PACKAGE_VERSION}) SET(ZLOG_SO_VERSION ${CPACK_PACKAGE_VERSION_MAJOR}) message(STATUS "platform : ${CMAKE_SYSTEM}") add_definitions("-g -Wall -Wstrict-prototypes") set(CMAKE_C_FLAGS "-std=c99 -pedantic -D_DEFAULT_SOURCE") set(CMAKE_C_FLAGS_DEBUG "-ggdb3 -DDEBUG") set(CMAKE_C_FLAGS_RELEASE "-O2") if (WIN32) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DWINVER=0x0500 -D_WIN32_WINNT=0x0500 ") endif () cmake_policy(SET CMP0015 NEW) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${zlog_BINARY_DIR}/lib") set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${zlog_BINARY_DIR}/lib") set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${zlog_BINARY_DIR}/bin") link_directories(${CMAKE_LIBRARY_OUTPUT_DIRECTORY}) set(Need_THREAD 1) if (WIN32) set(Need_UNIXEM 1) endif () include(cmake/LoadLibraries.cmake) add_subdirectory(src) add_subdirectory(cpack) if (UNIT_TEST) enable_testing() add_subdirectory(test) endif () zlog-1.2.18/Changelog000066400000000000000000000170221460570005000143670ustar00rootroot00000000000000zlog (1.2.17) unstable; urgency=medium * Change License to Apache 2.0, for more people. -- [Hardy Simpson] zlog (1.2.16) unstable; urgency=medium * Minor bug fixes. -- [Hardy Simpson] zlog (1.2.15) unstable; urgency=medium * Added support for %r to insert a carriage return. * Minor bug fixes. -- [Hardy Simpson] zlog (1.2.14) unstable; urgency=medium * Added support for all syslog facilities. * Test: Added long log test. * Added '-' to the list of accepted characters in category name. * Added API to get zlog version. * Added %k formatter with leading 0's formatting. * Added API for category log level checking. * Fixed compile errors on some platforms. * Minor bug fixes. -- [Hardy Simpson] zlog (1.2.12) unstable; urgency=medium * Bugfix to avoid segmentation fault if zlog_init() is called many times. * Static file rule to emulate Python's WatchedFileHandler mode when used with external log rotation. -- [Hardy Simpson] zlog (1.2.10) unstable; urgency=medium * Bugfix: LGPL v3 -> LGPL v2.1. -- [Hardy Simpson] zlog (1.2.8) unstable; urgency=medium * Added gcc __attribute__ macro for checking zlog's format output. * Added support for # comments in configuration file. -- [Hardy Simpson] zlog (1.2.5) unstable; urgency=medium * Ensured automatic deletion of thread on single-thread exit using atexit. * Cached multiple times to ensure every %d is cached every second during strftime. * Removed usage of %D, although library still supports it, documentation and example code use %d. * Fixed makefile for static linking zlog-chk-conf. -- [Hardy Simpson] zlog (1.2.4) unstable; urgency=medium * No longer maintains thread_list; checks configuration for updates during each log write. -- [Hardy Simpson] zlog (1.2.0) unstable; urgency=medium * Introduced rotate and zip scheme. * Globalized some static variables to reduce interface parameters. * Added fstat64. * Implemented pipe, cronolog. * Ignored second parameter in record. * Cross-platform support for __VA_ARGS__. * Added git/version flags in code (redis). * Removed auto tools, using custom makefile (redis). * Lowercased macros. * Used const. * Introduced per-thread rule->file fd write time. * Used time for reload conf period calculation. * Added zlog_set_conf("section", "command"). * Rewrote format, spec, level using direct arrays for improved performance. * Added manpage, df, examples. -- [Hardy Simpson] zlog (1.1.3) unstable; urgency=medium * Dynamically adjusted zlog_record. -- [Hardy Simpson] zlog (1.1.2) unstable; urgency=medium * Corrected rule.c, capped path_buf. -- [Hardy Simpson] zlog (1.1.1) unstable; urgency=medium * Corrected rule.c, avoided using FILE* to prevent interleaved logs. -- [Hardy Simpson] zlog (1.1.0) unstable; urgency=medium * Switched spec to switch loop, introduced bit-wise lock-free checks. * Established bit flags for us, srcline. * Added pthread_key_t for thread-private storage. * Added %D, cached strftime results. * Cached pid, tid as strings. * Removed rc usage (redis). * Cleaned up ZLOG_FMT and ZLOG_HEX. -- [Hardy Simpson] zlog (1.0.7) unstable; urgency=medium * Partial implementation of custom strftime for efficiency. * %t displayed in hexadecimal. * Various minor performance optimizations. -- [Hardy Simpson] zlog (1.0.6) unstable; urgency=medium * Obtained localtime_r every second for over 1x acceleration. -- [Hardy Simpson] zlog (1.0.5) unstable; urgency=medium * Fixed issue from 1.0.4; used read-write lock for reopening files. -- [Hardy Simpson] zlog (1.0.4) unstable; urgency=medium * Tested multi-threading efficiency. * Cached static file's FILE* and fd for 3x optimization. -- [Hardy Simpson] zlog (1.0.3) unstable; urgency=medium * Automatic configuration refresh based on log count. * Automatic sync to disk based on log count. -- [Hardy Simpson] zlog (1.0.2) unstable; urgency=medium * Added synchronous write to disk option (time-consuming when open). -- [Hardy Simpson] zlog (1.0.1) unstable; urgency=medium * Used configuration file as lock file. -- [Hardy Simpson] zlog (1.0.0) unstable; urgency=medium * Changed configuration file layout. * Made reload atomic, falling back to old on failure. * Optimized spec_gen_msg using custom buf_append alignment. * Introduced __func__. * Set file permissions. * Customizable output function. -- [Hardy Simpson] zlog (0.9) unstable; urgency=medium * Shifted towards an object-oriented approach. * Used arraylist instead of linklist as an internal data structure. * Improved configuration file format. * Created a thread-safe logging function library. * Resolved microsecond/millisecond representation in configuration. * Merged event into thread to avoid memory allocation. * Added init API for reading configuration files. * Parsed default format when initialized without configuration, wrote zlog_chk_conf. * Isolated category. * Added update API. * Adjusted buf size dynamically through zlog_init, rebuilt all caches using zlog_update. * Aligned file creation permissions similar to fopen (0666 & (~umask)). * Handled syslog output, though syslog was found slow. * Traditional mode for rotation, polling, etc. * Added zlog_profile API for diagnosing configuration memory. * Added MDC with configurable additional fields. * Tested buffer rewriting to ensure trailing \0. * Renamed xlog to zlog. * Retrieved ZLOG_ERROR_LOG and ZLOG_DEBUG_LOG once to avoid multiple getenv calls. * Closed zc_assert by default, opened for debugging. * Added match for !.*, matching unmatched categories. * Changed priority to level. * Customizable level. * Syslog open in rule. * Moved default format to global configuration. * Reset level using arraylist. * Introduced ylog, simplified interface, renamed to dzlog. * Added zlog-gen-conf. * Controlled rotate count. * Abandoned ideas: - [x] Used writev to write logs, improve efficiency, achieve one-time formation, multiple outputs -- writev internally also uses buffer memcpy. - [x] Tried semaphore method to make it thread-safe for archiving mutual exclusion problems. - [x] zlog.h variable-length parameter macro problem (refer to glib's log solution). - [x] dynamic] file in rule can be obtained from format. - [x] Modified arraylist read length, loop, etc. - [x] Specified whether to use threads during runtime. - [x] buf_append optimization -- for safety, did not optimize this. - [x] Used some automatic compiler and document generator. - [x] Output to pipes, named pipes same as files. - [x] Print stack... may need language support. - [x] zlog-input -c category -p priority -f conf. - [x] Used fine-grained reo en method to intelligently determine whether to reopen the file with fo en, the operating system itself has done well enough, no need to do. -- [Hardy Simpson] zlog-1.2.18/INSTALL000077700000000000000000000000001460570005000150562README.mdustar00rootroot00000000000000zlog-1.2.18/LICENSE000066400000000000000000000261351460570005000135670ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. zlog-1.2.18/Makefile000066400000000000000000000006451460570005000142200ustar00rootroot00000000000000# Top level makefile, the real shit is at src/makefile TARGETS=noopt 32bit all: cd src && $(MAKE) $@ install: cd src && $(MAKE) $@ $(TARGETS): cd src && $(MAKE) $@ doc: cd doc && $(MAKE) test: cd test && $(MAKE) TAGS: find . -type f -name "*.[ch]" | xargs etags - clean: cd src && $(MAKE) $@ cd test && $(MAKE) $@ cd doc && $(MAKE) $@ rm -f TAGS distclean: clean dummy: .PHONY: doc install test TAGS zlog-1.2.18/README.md000066400000000000000000000104531460570005000140350ustar00rootroot00000000000000# zlog zlog is a reliable, high-performance, thread safe, flexible, clear-model, pure C logging library. Actually, in the C world there was NO good logging library for applications like logback in java or log4cxx in c++. Using printf can work, but can not be redirected or reformatted easily. syslog is slow and is designed for system use. So I wrote zlog. It is faster, safer and more powerful than log4c. So it can be widely used. ## 1. Install Downloads: ```bash tar -zxvf zlog-latest-stable.tar.gz cd zlog-latest-stable/ make sudo make install ``` Or ```bash make PREFIX=/usr/local/ sudo make PREFIX=/usr/local/ install ``` PREFIX indicates the installation destination for zlog. After installation, refresh your dynamic linker to make sure your program can find zlog library. ```bash $ sudo vi /etc/ld.so.conf /usr/local/lib $ sudo ldconfig ``` Before running a real program, make sure libzlog.so is in the directory where the system's dynamic lib loader can find it. The command metioned above are for linux. Other systems will need a similar set of actions. ## 2. Configuration file There are 3 important concepts in zlog: categories, formats and rules. Categories specify different kinds of log entries. In the zlog source code, category is a `zlog_cateogory_t *` variable. In your program, different categories for the log entries will distinguish them from each other. Formats describe log patterns, such as: with or without time stamp, source file, source line. Rules consist of category, level, output file (or other channel) and format. In brief, if the category string in a rule in the configuration file equals the name of a category variable in the source, then they match. Still there is complex match range of category. Rule decouples variable conditions. For example, log4j must specify a level for each logger(or inherit from father logger). That's not convenient when each grade of logger has its own level for output(child logger output at the level of debug, when father logger output at the level of error) Now create a configuration file. The function zlog_init takes the files path as its only argument. ```bash $ cat /etc/zlog.conf [formats] simple = "%m%n" [rules] my_cat.DEBUG >stdout; simple ``` In the configuration file log messages in the category `my_cat` and a level of DEBUG or higher are output to standard output, with the format of simple `%m - usermessage %n - newline`. If you want to direct out to a file and limit the files maximum size, use this configuration ```config my_cat.DEBUG "/var/log/aa.log", 1M; simple ``` ## 3. Using zlog API in C source file ### 3.1 Sample code ```bash $ vi test_hello.c #include #include "zlog.h" int main(int argc, char** argv) { int rc; zlog_category_t *c; rc = zlog_init("/etc/zlog.conf"); if (rc) { printf("init failed\n"); return -1; } c = zlog_get_category("my_cat"); if (!c) { printf("get cat fail\n"); zlog_fini(); return -2; } zlog_info(c, "hello, zlog"); zlog_fini(); return 0; } ``` ### 3.2 Compile, and run it ```bash $ cc -c -o test_hello.o test_hello.c -I/usr/local/include $ cc -o test_hello test_hello.o -L/usr/local/lib -lzlog -lpthread $ ./test_hello hello, zlog ``` ## 4. Advanced Usage * syslog model, better than log4j model * log format customization * multiple output destinations including static file path, dynamic file path, stdout, stderr, syslog, user-defined output * runtime manually or automatically refresh configure(safely) * high-performance, 250'000 logs/second on my laptop, about 1000 times faster than syslog(3) with rsyslogd * user-defined log level * thread-safe and process-safe log file rotation * microsecond accuracy * dzlog, a default category log API for easy use * MDC, a log4j style key-value map * self debuggable, can output zlog's self debug&error log at runtime * No external dependencies, just based on a POSIX system and a C99 compliant vsnprintf. ## 5. Reference * Homepage: * Downloads: * Author's Email: * Auto tools version: * CMake verion: * Windows version: zlog-1.2.18/TODO000066400000000000000000000011331460570005000132410ustar00rootroot00000000000000[p] 使用valgrind测试性能 [ ] 更好的错误展现,当系统出问题的时候直接报错 [ ] hzlog的可定制 [ ] hex那段重写,内置到buf内,参考od的设计 [ ] 分类匹配的可定制化, rcat [ ] 自行管理文件缓存,替代stdio [ ] 减少dynamic文件名open的次数,通过日期改变智能推断, file_table? [ ] async file输出的增加 [ ] 兼容性问题 zlog.h内 [ ] 增加trace级别 [ ] gettid() [ ] 性能对比, log4x, pantheios, glog [ ] perl, python, go, c++支持 [ ] redis对接,协议设计 [ ] 和rsyslog对接的问题 [x] linux fsync->fdatasync, open.. zlog-1.2.18/cmake/000077500000000000000000000000001460570005000136335ustar00rootroot00000000000000zlog-1.2.18/cmake/FindUnixem.cmake000066400000000000000000000013001460570005000166750ustar00rootroot00000000000000# FindUnixem.cmake # author lsm if (NOT UNIXEM_FOUND) if (NOT UNIXEM_INCLUDE_DIR) find_path(UNIXEM_INCLUDE_DIR NAMES unixem/unixem.h ONLY_CMAKE_FIND_ROOT_PATH ) endif () if (NOT UNIXEM_LIBRARY) find_library(UNIXEM_LIBRARY NAMES unixem ONLY_CMAKE_FIND_ROOT_PATH ) endif (NOT UNIXEM_LIBRARY) message(STATUS " UNIXEM_INCLUDE_DIR = ${UNIXEM_INCLUDE_DIR}") message(STATUS " UNIXEM_LIBRARY = ${UNIXEM_LIBRARY}") if (UNIXEM_INCLUDE_DIR AND UNIXEM_LIBRARY) set(UNIXEM_FOUND TRUE) endif (UNIXEM_INCLUDE_DIR AND UNIXEM_LIBRARY) endif () zlog-1.2.18/cmake/LoadLibraries.cmake000066400000000000000000000012631460570005000173530ustar00rootroot00000000000000# ======================================================= # 支持多线程 # 对于需要多线程的库,使用以下命令包含连接库: # target_link_libraries(xxx ${CMAKE_THREAD_PREFER_PTHREAD}) # ======================================================= if (Need_THREAD) find_package(Threads REQUIRED) if (NOT CMAKE_THREAD_PREFER_PTHREAD) set(CMAKE_THREAD_PREFER_PTHREAD ${CMAKE_THREAD_LIBS_INIT}) endif () message(STATUS "thread lib : ${CMAKE_THREAD_PREFER_PTHREAD}") endif (Need_THREAD) if (Need_UNIXEM) find_package(Unixem) if (NOT UNIXEM_FOUND) message(FATAL_ERROR "unixem lib not found!") endif () endif () zlog-1.2.18/cmake/toolchain-mingw64.cmake000066400000000000000000000026411460570005000201110ustar00rootroot00000000000000# this one is important SET(CMAKE_SYSTEM_NAME Windows) # specify the cross compiler find_path(MINGW_BIN_PATH gcc.exe PATHS c:/mingw64 d:/mingw64 c:/mingw-build/mingw64 d:/mingw-build/mingw64 PATH_SUFFIXES bin) if (MINGW_PATH_NOTFOUND) message(FATAL "mingw64 not found!") endif () get_filename_component(MINGW_PATH ${MINGW_BIN_PATH} PATH) SET(CMAKE_GENERATOR "MinGW Makefiles") SET(CMAKE_C_COMPILER gcc) SET(CMAKE_CXX_COMPILER g++) SET(CMAKE_RC_COMPILER windres) set(CMAKE_RC_COMPILE_OBJECT " -O coff -i -o ") # SET(_CMAKE_TOOLCHAIN_PREFIX x86_64-w64-mingw32-) SET(_CMAKE_TOOLCHAIN_LOCATION ${MINGW_BIN_PATH}) # where is the target environment SET(CMAKE_FIND_ROOT_PATH ${MINGW_PATH} ${MINGW_PATH}/x86_64-w64-mingw32) SET(CMAKE_SYSTEM_INCLUDE_PATH "${CMAKE_SYSTEM_INCLUDE_PATH} ${MINGW_PATH}/include ${MINGW_PATH}/x86_64-w64-mingw32/include") SET(CMAKE_SYSTEM_LIBRARY_PATH "${CMAKE_SYSTEM_LIBRARY_PATH} ${MINGW_PATH}/lib ${MINGW_PATH}/x86_64-w64-mingw32/lib") SET(CMAKE_SYSTEM_PROGRAM_PATH "${CMAKE_SYSTEM_PROGRAM_PATH} ${MINGW_PATH}/bin ${MINGW_PATH}/x86_64-w64-mingw32/bin") # printf support %sz format. add_definitions("-D_POSIX") # search for programs in the build host directories SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) # for libraries and headers in the target directories SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) zlog-1.2.18/cpack/000077500000000000000000000000001460570005000136345ustar00rootroot00000000000000zlog-1.2.18/cpack/CMakeLists.txt000066400000000000000000000026561460570005000164050ustar00rootroot00000000000000# create by lsm SET(CPACK_PACKAGE_VENDOR "zlog") SET(CPACK_PACKAGE_CONTACT "HardySimpson1984@gmail.com") SET(CPACK_RPM_PACKAGE_LICENSE "Apache License Version 2.0") SET(CPACK_PACKAGING_INSTALL_PREFIX "/usr/local") SET(CPACK_RPM_PACKAGE_GROUP "System Environment/Base") message(STATUS "system process is ${CMAKE_HOST_SYSTEM_PROCESSOR}") message(STATUS "system process is ${CMAKE_SYSTEM_PROCESSOR}") IF (NOT CMAKE_SYSTEM_PROCESSOR) set(CMAKE_SYSTEM_PROCESSOR ${CMAKE_HOST_SYSTEM_PROCESSOR}) endif () IF (${CMAKE_SYSTEM_PROCESSOR} MATCHES "x86_64|amd64") set(CMAKE_SYSTEM_PROCESSOR x86_64) SET(CPACK_DEBIAN_PACKAGE_ARCHITECTURE amd64) SET(CPACK_RPM_PACKAGE_ARCHITECTURE "x86_64") ELSEIF (${CMAKE_SYSTEM_PROCESSOR} MATCHES "powerpc64|ppc64") SET(CPACK_DEBIAN_PACKAGE_ARCHITECTURE powerpc64) SET(CPACK_RPM_PACKAGE_ARCHITECTURE "ppc64") ELSEIF (${CMAKE_SYSTEM_PROCESSOR} MATCHES "powerpc|ppc") SET(CPACK_DEBIAN_PACKAGE_ARCHITECTURE powerpc) SET(CPACK_RPM_PACKAGE_ARCHITECTURE "ppc") ELSE () SET(CPACK_DEBIAN_PACKAGE_ARCHITECTURE i386) SET(CPACK_RPM_PACKAGE_ARCHITECTURE i386) ENDIF () SET(CPACK_SYSTEM_NAME "${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}") SET(CPACK_TOPLEVEL_TAG "${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}") SET(CPACK_CMAKE_GENERATOR "Unix Makefiles") if (WIN32) SET(CPACK_GENERATOR "NSIS") else () SET(CPACK_GENERATOR "RPM;DEB") endif () add_subdirectory(zlog) zlog-1.2.18/cpack/CPackConfig.cmake000066400000000000000000000044071460570005000167520ustar00rootroot00000000000000SET(CPACK_CMAKE_GENERATOR "${CPACK_CMAKE_GENERATOR}") SET(CPACK_GENERATOR "${CPACK_GENERATOR}") SET(CPACK_OUTPUT_CONFIG_FILE "${CPACK_OUTPUT_CONFIG_FILE}") SET(CPACK_INSTALL_CMAKE_PROJECTS "${CPACK_INSTALL_CMAKE_PROJECTS}") SET(CPACK_PACKAGE_VERSION_MAJOR "${CPACK_PACKAGE_VERSION_MAJOR}") SET(CPACK_PACKAGE_VERSION_MINOR "${CPACK_PACKAGE_VERSION_MINOR}") SET(CPACK_PACKAGE_VERSION_PATCH "${CPACK_PACKAGE_VERSION_PATCH}") SET(CPACK_RPM_PACKAGE_RELEASE "${CPACK_RPM_PACKAGE_RELEASE}") # release version. SET(CPACK_PACKAGE_VERSION "${CPACK_PACKAGE_VERSION}") SET(CPACK_PACKAGE_NAME "${CPACK_PACKAGE_NAME}") SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "${CPACK_PACKAGE_DESCRIPTION_SUMMARY}") SET(CPACK_PACKAGE_DESCRIPTION_FILE "${CPACK_PACKAGE_DESCRIPTION_FILE}") SET(CPACK_PACKAGE_VENDOR "${CPACK_PACKAGE_VENDOR}") SET(CPACK_PACKAGING_INSTALL_PREFIX "${CPACK_PACKAGING_INSTALL_PREFIX}") SET(CPACK_PACKAGE_CONTACT "${CPACK_PACKAGE_CONTACT}") SET(CPACK_PACKAGE_INSTALL_REGISTRY_KEY "${CPACK_PACKAGE_NAME} ${CPACK_PACKAGE_VERSION}") SET(CPACK_SYSTEM_NAME "${CPACK_SYSTEM_NAME}") SET(CPACK_TOPLEVEL_TAG "${CPACK_TOPLEVEL_TAG}") SET(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CPACK_RPM_PACKAGE_RELEASE}-${CPACK_TOPLEVEL_TAG}") # SET(CPACK_PACKAGE_EXECUTABLES "ccmake;CMake") # SET(CPACK_STRIP_FILES "bin/ccmake;bin/cmake;bin/cpack;bin/ctest") set(CPACK_NSIS_MODIFY_PATH, ON) SET(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}") SET(CPACK_RPM_PACKAGE_ARCHITECTURE "${CPACK_RPM_PACKAGE_ARCHITECTURE}") SET(CPACK_RPM_PACKAGE_REQUIRES "${CPACK_RPM_PACKAGE_REQUIRES}") SET(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS}") SET(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_CURRENT_BINARY_DIR}/preinst;${CMAKE_CURRENT_BINARY_DIR}/postinst;${CMAKE_CURRENT_BINARY_DIR}/prerm;${CMAKE_CURRENT_BINARY_DIR}/postrm") SET(CPACK_RPM_PRE_INSTALL_SCRIPT_FILE "${CMAKE_CURRENT_BINARY_DIR}/preinst") SET(CPACK_RPM_POST_INSTALL_SCRIPT_FILE "${CMAKE_CURRENT_BINARY_DIR}/postinst") SET(CPACK_RPM_PRE_UNINSTALL_SCRIPT_FILE "${CMAKE_CURRENT_BINARY_DIR}/prerm") SET(CPACK_RPM_POST_UNINSTALL_SCRIPT_FILE "${CMAKE_CURRENT_BINARY_DIR}/postrm") SET(CPACK_RPM_PACKAGE_LICENSE "${CPACK_RPM_PACKAGE_LICENSE}") SET(CPACK_RPM_PACKAGE_GROUP "${CPACK_RPM_PACKAGE_GROUP}") zlog-1.2.18/cpack/zlog/000077500000000000000000000000001460570005000146075ustar00rootroot00000000000000zlog-1.2.18/cpack/zlog/CMakeLists.txt000066400000000000000000000016211460570005000173470ustar00rootroot00000000000000# create by lsm # =============================== # package info setting # =============================== SET(CPACK_PACKAGE_NAME "zlog") SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "log component for Linux/Unix/AIX") SET(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_HOME_DIRECTORY}/README") SET(CPACK_INSTALL_CMAKE_PROJECTS "${zlog_BINARY_DIR};zlog;zlog;/") # ================================= # dependency setting # ================================= # SET(CPACK_RPM_PACKAGE_REQUIRES "") # SET(CPACK_DEBIAN_PACKAGE_DEPENDS "") # =============================== # copy file to build directory. # =============================== SET(CPACK_OUTPUT_CONFIG_FILE "${CMAKE_CURRENT_BINARY_DIR}/CPackConfig.cmake") configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../CPackConfig.cmake CPackConfig.cmake) file(COPY . DESTINATION ${CMAKE_CURRENT_BINARY_DIR} PATTERN CMakeLists.txt EXCLUDE ) zlog-1.2.18/cpack/zlog/postinst000066400000000000000000000000271460570005000164140ustar00rootroot00000000000000#!/bin/bash ldconfig zlog-1.2.18/cpack/zlog/postrm000066400000000000000000000000301460570005000160470ustar00rootroot00000000000000#!/bin/bash ldconfig zlog-1.2.18/cpack/zlog/preinst000066400000000000000000000000241460570005000162120ustar00rootroot00000000000000#!/usr/bin/env bash zlog-1.2.18/cpack/zlog/prerm000066400000000000000000000000151460570005000156530ustar00rootroot00000000000000#!/bin/bash zlog-1.2.18/doc/000077500000000000000000000000001460570005000133205ustar00rootroot00000000000000zlog-1.2.18/doc/GettingStart-CN.txt000066400000000000000000000111321460570005000167740ustar00rootroot000000000000000. zlog是什么? zlog是一个高可靠性、高性能、线程安全、灵活、概念清晰的纯C日志函数库。 事实上,在C的世界里面没有特别好的日志函数库(就像JAVA里面的的log4j,或者C++的log4cxx)。C程序员都喜欢用自己的轮子。printf就是个挺好的轮子,但没办法通过配置改变日志的格式或者输出文件。syslog是个系统级别的轮子,不过速度慢,而且功能比较单调。 所以我写了zlog。 zlog在效率、功能、安全性上大大超过了log4c,并且是用c写成的,具有比较好的通用性。 1.安装 下载:https://github.com/HardySimpson/zlog/releases $ tar -zxvf zlog-latest-stable.tar.gz $ cd zlog-latest-stable/ $ make $ sudo make install or $ make PREFIX=/usr/local/ $ sudo make PREFIX=/usr/local/ install PREFIX指明了安装的路径,安转完之后为了让你的程序能找到zlog动态库 $ sudo vi /etc/ld.so.conf /usr/local/lib $ sudo ldconfig 在你的程序运行之前,保证libzlog.so在系统的动态链接库加载器可以找到的目录下。上面的命令适用于linux,别的系统自己想办法。 2.介绍一下配置文件 zlog里面有三个重要的概念,category,format,rule 分类(Category)用于区分不同的输入,代码中的分类变量的名字是一个字符串,在一个程序里面可以通过获取不同的分类名的category用来后面输出不同分类的日志,用于不同的目的。 格式(Format)是用来描述输出日志的格式,比如是否有带有时间戳, 是否包含文件位置信息等,上面的例子里面的格式simple就配置成简单的用户输入的信息+换行符。 规则(Rule)则是把分类、级别、输出文件、格式组合起来,决定一条代码中的日志是否输出,输出到哪里,以什么格式输出。简单而言,规则里面的分类字符串和代码里面的分类变量的名字一样就匹配,当然还有更高级的纲目分类匹配。规则彻底解耦了各个元素之间的强绑定,例如log4j就必须为每个分类指定一个级别(或者从父分类那里继承),这在多层系统需要每一层都有自己的级别要求的时候非常不方便。 现在试着写配置文件,配置文件名无所谓,放在哪里也无所谓,反正在zlog_init()的时候可以指定 $ cat /etc/zlog.conf [formats] simple = "%m%n" [rules] my_cat.DEBUG >stdout; simple 在目前的配置文件的例子里面,可以看到my_cat分类,>=debug等级的日志会被输出到stdout(标准输出),并且输出的格式是simple这个格式,也就是用户输入信息+换行符。如果要输出到文件并控制文件大小为1兆,规则的配置应该是 my_cat.DEBUG "/var/log/aa.log", 1M; simple 3.在代码中使用 $ vi test_hello.c #include #include "zlog.h" int main(int argc, char** argv) { int rc; zlog_category_t *c; rc = zlog_init("/etc/zlog.conf"); if (rc) { printf("init failed\n"); return -1; } c = zlog_get_category("my_cat"); if (!c) { printf("get cat fail\n"); zlog_fini(); return -2; } zlog_info(c, "hello, zlog"); zlog_fini(); return 0; } 4.编译、然后运行! $ cc -c -o test_hello.o test_hello.c -I/usr/local/include $ cc -o test_hello test_hello.o -L/usr/local/lib -lzlog -lpthread $ ./test_hello hello, zlog 5.高级使用 * syslog分类模型,比log4j模型更加直接了当 * 日志格式定制,类似于log4j的pattern layout * 多种输出,包括动态文件、静态文件、stdout、stderr、syslog、用户自定义输出函数 * 运行时手动、自动刷新配置文件(同时保证安全) * 高性能,在我的笔记本上达到25万条日志每秒, 大概是syslog(3)配合rsyslogd的1000倍速度 * 用户自定义等级 * 多线程和多进程环境下保证安全转档 * 精确到微秒 * 简单调用包装dzlog(一个程序默认只用一个分类) * MDC,线程键-值对的表,可以扩展用户自定义的字段 * 自诊断,可以在运行时输出zlog自己的日志和配置状态 * 不依赖其他库,只要是个POSIX系统就成(当然还要一个C99兼容的vsnprintf) 6.相关链接 主页:http://hardysimpson.github.com/zlog/ 软件下载:https://github.com/HardySimpson/zlog/releases 作者邮箱:HardySimpson1984@gmail.com auto tools版本: https://github.com/bmanojlovic/zlog cmake版本: https://github.com/lisongmin/zlog windows版本: https://github.com/lopsd07/WinZlog zlog-1.2.18/doc/GettingStart-EN.txt000066400000000000000000000102411460570005000167760ustar00rootroot000000000000000. What is zlog? zlog is a reliable, high-performance, thread safe, flexible, clear-model, pure C logging library. Actually, in the C world there was NO good logging library for applications like logback in java or log4cxx in c++. Using printf can work, but can not be redirected or reformatted easily. syslog is slow and is designed for system use. So I wrote zlog. It is faster, safer and more powerful than log4c. So it can be widely used. 1. Install Downloads: https://github.com/HardySimpson/zlog/releases $ tar -zxvf zlog-latest-stable.tar.gz $ cd zlog-latest-stable/ $ make $ sudo make install or $ make PREFIX=/usr/local/ $ sudo make PREFIX=/usr/local/ install PREFIX indicates the installation destination for zlog. After installation, refresh your dynamic linker to make sure your program can find zlog library. $ sudo vi /etc/ld.so.conf /usr/local/lib $ sudo ldconfig Before running a real program, make sure libzlog.so is in the directory where the system's dynamic lib loader can find it. The command metioned above are for linux. Other systems will need a similar set of actions. 2. Introduce configure file There are 3 important concepts in zlog: categories, formats and rules. Categories specify different kinds of log entries. In the zlog source code, category is a (zlog_cateogory_t *) variable. In your program, different categories for the log entries will distinguish them from each other. Formats describe log patterns, such as: with or without time stamp, source file, source line. Rules consist of category, level, output file (or other channel) and format. In brief, if the category string in a rule in the configuration file equals the name of a category variable in the source, then they match. Still there is complex match range of category. Rule decouples variable conditions. For example, log4j must specify a level for each logger(or inherit from father logger). That's not convenient when each grade of logger has its own level for output(child logger output at the level of debug, when father logger output at the level of error) Now create a configuration file. The function zlog_init takes the files path as its only argument. $ cat /etc/zlog.conf [formats] simple = "%m%n" [rules] my_cat.DEBUG >stdout; simple In the configuration file log messages in the category "my_cat" and a level of DEBUG or higher are output to standard output, with the format of simple(%m - usermessage %n - newline). If you want to direct out to a file and limit the files maximum size, use this configuration my_cat.DEBUG "/var/log/aa.log", 1M; simple 3. Using zlog API in C source file $ vi test_hello.c #include #include "zlog.h" int main(int argc, char** argv) { int rc; zlog_category_t *c; rc = zlog_init("/etc/zlog.conf"); if (rc) { printf("init failed\n"); return -1; } c = zlog_get_category("my_cat"); if (!c) { printf("get cat fail\n"); zlog_fini(); return -2; } zlog_info(c, "hello, zlog"); zlog_fini(); return 0; } 4. Complie, and run it!s $ cc -c -o test_hello.o test_hello.c -I/usr/local/include $ cc -o test_hello test_hello.o -L/usr/local/lib -lzlog -lpthread $ ./test_hello hello, zlog 5. Advanced Usage * syslog model, better than log4j model * log format customization * multiple output destinations including static file path, dynamic file path, stdout, stderr, syslog, user-defined ouput * runtime manually or automatically refresh configure(safely) * high-performance, 250'000 logs/second on my laptop, about 1000 times faster than syslog(3) with rsyslogd * user-defined log level * thread-safe and process-safe log file rotation * microsecond accuracy * dzlog, a default category log API for easy use * MDC, a log4j style key-value map * self debuggable, can output zlog's self debug&error log at runtime * No external dependencies, just based on a POSIX system and a C99 compliant vsnprintf. 6. Links: Homepage: http://hardysimpson.github.com/zlog Downloads: https://github.com/HardySimpson/zlog/releases Author's Email: HardySimpson1984@gmail.com auto tools version: https://github.com/bmanojlovic/zlog cmake verion: https://github.com/lisongmin/zlog windows version: https://github.com/lopsd07/WinZlog zlog-1.2.18/doc/Makefile000066400000000000000000000007231460570005000147620ustar00rootroot00000000000000doc = \ UsersGuide-EN.pdf \ UsersGuide-CN.pdf \ UsersGuide-EN.tex \ UsersGuide-CN.tex \ UsersGuide-EN.html \ UsersGuide-CN.html \ GettingStart-EN.txt \ GettingStart-CN.txt all : $(doc) UsersGuide-EN.pdf : UsersGuide-EN.lyx lyx -f -e pdf2 $^ UsersGuide-CN.pdf : UsersGuide-CN.lyx lyx -f -e pdf4 $^ %.tex : %.lyx lyx -f -e pdflatex $< %.html : %.tex hevea book.hva -s $< hevea book.hva -s $< clean : -rm -f *.pdf *.haux *.html *.htoc *.tex *.lyx~ zlog-1.2.18/doc/UsersGuide-CN.lyx000066400000000000000000004627031460570005000164470ustar00rootroot00000000000000#LyX 2.0 created this file. For more info see http://www.lyx.org/ \lyxformat 413 \begin_document \begin_header \textclass extbook \begin_preamble \usepackage[cm-default]{fontspec} \usepackage{xunicode} \usepackage[utf8]{inputenc} \usepackage{xltxtra} \setmainfont[ItalicFont=KaiTi]{SimSun} %\setmainfont[ItalicFont=KaiTi]{YaHei Consolas Hybrid} %\setmainfont[BoldFont=KaiTi,ItalicFont=KaiTi]{SimSun} %\setsansfont[BoldFont=KaiTi]{KaiTi} %\setmonofont{KaiTi} %其他中文设置% \XeTeXlinebreaklocale “zh”%中文断行 \XeTeXlinebreakskip = 0pt plus 1pt minus 0.1pt%左右弹性间距 \usepackage{indentfirst}%段落首行缩进 \setlength{\parindent}{2em}%缩进两个字符 %编号语言、样式设置% \numberwithin{equation}{section}%设置公式按章节进行编号 \numberwithin{figure}{section}% 按章节编号 \end_preamble \use_default_options true \begin_modules theorems-ams eqs-within-sections figs-within-sections \end_modules \maintain_unincluded_children false \language chinese-simplified \language_package default \inputencoding utf8-plain \fontencoding global \font_roman default \font_sans default \font_typewriter default \font_default_family default \use_non_tex_fonts false \font_sc false \font_osf false \font_sf_scale 100 \font_tt_scale 100 \graphics default \default_output_format default \output_sync 0 \bibtex_command default \index_command default \paperfontsize 12 \spacing single \use_hyperref true \pdf_title "zlog Users Guide CN" \pdf_author "Hardy Simpson" \pdf_bookmarks true \pdf_bookmarksnumbered true \pdf_bookmarksopen true \pdf_bookmarksopenlevel 1 \pdf_breaklinks true \pdf_pdfborder true \pdf_colorlinks true \pdf_backref false \pdf_pdfusetitle true \pdf_quoted_options "unicode=false" \papersize a4paper \use_geometry true \use_amsmath 1 \use_esint 1 \use_mhchem 1 \use_mathdots 1 \cite_engine basic \use_bibtopic false \use_indices false \paperorientation portrait \suppress_date false \use_refstyle 0 \index 索引 \shortcut idx \color #008000 \end_index \rightmargin 2cm \secnumdepth 2 \tocdepth 2 \paragraph_separation indent \paragraph_indentation default \quotes_language english \papercolumns 1 \papersides 2 \paperpagestyle default \tracking_changes false \output_changes false \html_math_output 0 \html_css_as_file 0 \html_be_strict false \end_header \begin_body \begin_layout Title zlog \begin_inset Foot status collapsed \begin_layout Plain Layout 星星之火,可以燎原——毛泽东 \end_layout \end_inset 使用手册 \end_layout \begin_layout Author 难易 著 \begin_inset Foot status collapsed \begin_layout Plain Layout 适用于 zlog v1.2.* \end_layout \end_inset \begin_inset Foot status collapsed \begin_layout Plain Layout 有问题,在github上 \begin_inset CommandInset href LatexCommand href name "开个issue" target "https://github.com/HardySimpson/zlog/issues/new" type "mailto:" \end_inset ,或者写邮件到 \begin_inset CommandInset href LatexCommand href name "HardySimpson1984@gmail.com" target "HardySimpson1984@gmail.com" type "mailto:" \end_inset \end_layout \end_inset \end_layout \begin_layout Standard \begin_inset CommandInset toc LatexCommand tableofcontents \end_inset \end_layout \begin_layout Chapter zlog是什么? \end_layout \begin_layout Standard zlog是一个高可靠性、高性能、线程安全、灵活、概念清晰的纯C日志函数库。 \end_layout \begin_layout Standard 事实上,在C的世界里面没有特别好的日志函数库(就像JAVA里面的的log4j,或者C++的log4cxx)。C程序员都喜欢用自己的轮子。printf就是个挺好的 轮子,但没办法通过配置改变日志的格式或者输出文件。syslog是个系统级别的轮子,不过速度慢,而且功能比较单调。 \end_layout \begin_layout Standard 所以我写了zlog。 \end_layout \begin_layout Standard zlog在效率、功能、安全性上大大超过了log4c,并且是用c写成的,具有比较好的通用性。 \end_layout \begin_layout Standard zlog有这些特性: \end_layout \begin_layout Itemize syslog分类模型,比log4j模型更加直接了当 \end_layout \begin_layout Itemize 日志格式定制,类似于log4j的pattern layout \end_layout \begin_layout Itemize 多种输出,包括动态文件、静态文件、stdout、stderr、syslog、用户自定义输出函数 \end_layout \begin_layout Itemize 运行时手动、自动刷新配置文件(同时保证安全) \end_layout \begin_layout Itemize 高性能,在我的笔记本上达到25万条日志每秒, 大概是syslog(3)配合rsyslogd的1000倍速度 \end_layout \begin_layout Itemize 用户自定义等级 \end_layout \begin_layout Itemize 多线程和多进程环境下保证安全转档 \end_layout \begin_layout Itemize 精确到微秒 \end_layout \begin_layout Itemize 简单调用包装dzlog(一个程序默认只用一个分类) \end_layout \begin_layout Itemize MDC,线程键-值对的表,可以扩展用户自定义的字段 \end_layout \begin_layout Itemize 自诊断,可以在运行时输出zlog自己的日志和配置状态 \end_layout \begin_layout Itemize 不依赖其他库,只要是个POSIX系统就成(当然还要一个C99兼容的vsnprintf) \end_layout \begin_layout Standard 相关链接: \end_layout \begin_layout Standard 主页: \begin_inset CommandInset href LatexCommand href name "http://hardysimpson.github.com/zlog/" target "http://hardysimpson.github.com/zlog/" \end_inset \end_layout \begin_layout Standard 下载: \begin_inset CommandInset href LatexCommand href target "https://github.com/HardySimpson/zlog/releases" \end_inset \end_layout \begin_layout Standard 邮箱: \begin_inset CommandInset href LatexCommand href name "HardySimpson1984@gmail.com" target "HardySimpson1984@gmail.com" type "mailto:" \end_inset \end_layout \begin_layout Section 兼容性说明 \end_layout \begin_layout Enumerate zlog是基于POSIX的。目前我手上有的环境只有AIX和linux。在其他的系统下(FreeBSD, NetBSD, OpenBSD, OpenSolaris , Mac OS X...)估计也能行,有问题欢迎探讨。 \end_layout \begin_layout Enumerate zlog使用了一个C99兼容的vsnprintf。也就是说如果缓存大小不足,vsnprintf将会返回目标字符串应有的长度(不包括' \backslash 0')。如果在你的系统上vsnprintf不是这么运作的,zlog就不知道怎么扩大缓存。如果在目标缓存不够的时候vsnprintf返回-1,zlog就会认为这次 写入失败。幸运的是目前大多数c标准库符合C99标准。glibc 2.1,libc on AIX, libc on freebsd...都是好的,不过glibc2.0不是。 在这种情况下,用户需要自己来装一个C99兼容的vsnprintf,来crack这个函数库。我推荐 \begin_inset CommandInset href LatexCommand href name "ctrio" target "http://sourceforge.net/projects/ctrio/" \end_inset , 或者 \begin_inset CommandInset href LatexCommand href name "C99-snprintf" target "http://www.jhweiss.de/software/snprintf.html" \end_inset 。只要改buf.c就行,祝好运! \end_layout \begin_layout Enumerate 有网友提供了如下版本,方便其他平台上安装编译,非常感谢! \end_layout \begin_deeper \begin_layout Standard auto tools版本: \begin_inset CommandInset href LatexCommand href target "https://github.com/bmanojlovic/zlog" \end_inset \end_layout \begin_layout Standard cmake版本: \begin_inset CommandInset href LatexCommand href target "https://github.com/lisongmin/zlog" \end_inset \end_layout \begin_layout Standard windows版本: \begin_inset CommandInset href LatexCommand href target "https://github.com/lopsd07/WinZlog" \end_inset \end_layout \end_deeper \begin_layout Section zlog 1.2 发布说明 \end_layout \begin_layout Enumerate zlog 1.2 新增了这些功能 \end_layout \begin_deeper \begin_layout Enumerate 对管道的支持,从此zlog可以外接cronolog这样的日志过滤程序来输出 \end_layout \begin_layout Enumerate 全面的日志转档支持,详见 \begin_inset CommandInset ref LatexCommand ref reference "sec:文件转档" \end_inset \end_layout \begin_layout Enumerate 其他兼容性的代码改动 \end_layout \end_deeper \begin_layout Enumerate zlog 1.2 在库方面是和zlog 1.0/1.1二进制兼容的,区别在于: \end_layout \begin_deeper \begin_layout Enumerate 所有的宏改为小写,ZLOG_INFO->zlog_info,方便开发者手工输入。这是一个巨大的改变,如果zlog1.1/1.0的用户要用zlog 1.2的话,需要写一 个脚本,把源代码中的大写批量替换为小写,然后重新编译你的程序。我提供了一个脚本: \end_layout \begin_deeper \begin_layout LyX-Code sed -i -e 's/ \backslash b \backslash w*ZLOG \backslash w* \backslash b/ \backslash L& \backslash E/g' aa.c \end_layout \end_deeper \begin_layout Enumerate 取消了auto tools的使用,也就是说,不论你在任何平台,都需要gcc和gnu make才能编译安装zlog。主流的操作系统(Aix, OpenSolari s..)都能安装gcc和gnu make。当然也可以自行修改makefile来完成编译,对于平台稍有经验的Geek都可以自行完成! \end_layout \end_deeper \begin_layout Chapter zlog不是什么? \end_layout \begin_layout Standard zlog的目标是成为一个简而精的日志函数库,不会直接支持网络输出或者写入数据库,不会直接支持日志内容的过滤和解析。 \end_layout \begin_layout Standard 原因很明显,日志库是被应用程序调用的,所有花在日志库上的时间都是应用程序运行时间的一部分,而上面说的这些操作都很费时间,会拖慢应用程序的速度。这些事儿应该在别的 进程或者别的机器上做。 \end_layout \begin_layout Standard 如果你需要这些特性,我建议使用rsyslog、zLogFabric、Logstash,这些日志搜集、过滤、存储软件,当然这是单独的进程,不是应用程序的一部分。 \end_layout \begin_layout Standard 目前zlog已经支持 \begin_inset CommandInset ref LatexCommand ref reference "sec:用户自定义输出" \end_inset ,可以自己实现一个输出函数,自由的把日志输出到其他进程或者其他机器。而把日志的分类匹配、日志格式成型的工作交给zlog。 \end_layout \begin_layout Standard 目前我的想法是实现一个zlog-redis客户端,用自定义输出功能,把日志存储到本机或者远程的redis服务器内,然后用其他进程(也使用zlog库)来把日志写到 文件里面,不知大家以为这个想法如何?欢迎和我联系探讨。 \end_layout \begin_layout Chapter Hello World \end_layout \begin_layout Section 编译和安装zlog \end_layout \begin_layout Standard 下载 \begin_inset CommandInset href LatexCommand href name "zlog-latest-stable.tar.gz" target "https://github.com/HardySimpson/zlog/archive/latest-stable.tar.gz" type "file:" \end_inset \end_layout \begin_layout LyX-Code $ tar -zxvf zlog-latest-stable.tar.gz \end_layout \begin_layout LyX-Code $ cd zlog-latest-stable/ \end_layout \begin_layout LyX-Code $ make \end_layout \begin_layout LyX-Code $ sudo make install \end_layout \begin_layout LyX-Code or \end_layout \begin_layout LyX-Code $ sudo make PREFIX=/usr/local/ install \end_layout \begin_layout Standard PREFIX指明了安装的路径,安转完之后为了让你的程序能找到zlog动态库 \end_layout \begin_layout LyX-Code $ sudo vi /etc/ld.so.conf \end_layout \begin_layout LyX-Code /usr/local/lib \end_layout \begin_layout LyX-Code $ sudo ldconfig \end_layout \begin_layout Standard 在你的程序运行之前,保证libzlog.so在系统的动态链接库加载器可以找到的目录下。上面的命令适用于linux,别的系统自己想办法。 \end_layout \begin_layout Itemize 除了一般的make以外,还可以 \end_layout \begin_layout LyX-Code $ make 32bit # 32bit version on 64bit machine, libc6-dev-i386 is needed \end_layout \begin_layout LyX-Code $ make noopt # without gcc optimization \end_layout \begin_layout LyX-Code $ make doc # lyx and hevea is needed \end_layout \begin_layout LyX-Code $ make test # test code, which is also good example for zlog \end_layout \begin_layout Itemize makefile是用GNU make的格式写的,所以在你的平台上需要预装gnu make和gcc。或者,手工修改一个自己平台的makefile也行。 \end_layout \begin_layout Section 应用程序调用和链接zlog \end_layout \begin_layout Standard 应用程序使用zlog很简单,只要在C文件里面加一行。 \end_layout \begin_layout LyX-Code #include "zlog.h" \end_layout \begin_layout Standard 链接zlog需要pthread库,命令是: \end_layout \begin_layout LyX-Code $ cc -c -o app.o app.c -I/usr/local/include \end_layout \begin_layout LyX-Code # -I[where zlog.h is put] \end_layout \begin_layout LyX-Code $ cc -o app app.o -L/usr/local/lib -lzlog -lpthread \end_layout \begin_layout LyX-Code # -L[where libzlog.so is put] \end_layout \begin_layout Section Hello World 代码 \begin_inset CommandInset label LatexCommand label name "sec:Hello-World-代码" \end_inset \end_layout \begin_layout Standard 这些代码在$(top_builddir)/test/test_hello.c, test_hello.conf \end_layout \begin_layout Enumerate 写一个C文件: \end_layout \begin_deeper \begin_layout LyX-Code $ vi test_hello.c \end_layout \begin_layout LyX-Code #include \end_layout \begin_layout LyX-Code #include "zlog.h" \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code int main(int argc, char** argv) \end_layout \begin_layout LyX-Code { \end_layout \begin_deeper \begin_layout LyX-Code int rc; \end_layout \begin_layout LyX-Code zlog_category_t *c; \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code rc = zlog_init("test_hello.conf"); \end_layout \begin_layout LyX-Code if (rc) { \end_layout \begin_deeper \begin_layout LyX-Code printf("init failed \backslash n"); \end_layout \begin_layout LyX-Code return -1; \end_layout \end_deeper \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code c = zlog_get_category("my_cat"); \end_layout \begin_layout LyX-Code if (!c) { \end_layout \begin_layout LyX-Code printf("get cat fail \backslash n"); \end_layout \begin_deeper \begin_layout LyX-Code zlog_fini(); \end_layout \begin_layout LyX-Code return -2; \end_layout \end_deeper \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code zlog_info(c, "hello, zlog"); \end_layout \begin_layout LyX-Code zlog_fini(); \end_layout \begin_layout LyX-Code return 0; \end_layout \end_deeper \begin_layout LyX-Code } \end_layout \end_deeper \begin_layout Enumerate 写一个配置文件,放在和test_hello.c同样的目录下: \end_layout \begin_deeper \begin_layout LyX-Code $ vi test_hello.conf \end_layout \begin_layout LyX-Code [formats] \end_layout \begin_layout LyX-Code simple = "%m%n" \end_layout \begin_layout LyX-Code [rules] \end_layout \begin_layout LyX-Code my_cat.DEBUG >stdout; simple \end_layout \end_deeper \begin_layout Enumerate 编译、然后运行! \end_layout \begin_deeper \begin_layout LyX-Code $ cc -c -o test_hello.o test_hello.c -I/usr/local/include \end_layout \begin_layout LyX-Code $ cc -o test_hello test_hello.o -L/usr/local/lib -lzlog \end_layout \begin_layout LyX-Code $ ./test_hello \end_layout \begin_layout LyX-Code hello, zlog \end_layout \end_deeper \begin_layout Section 更简单的Hello World \end_layout \begin_layout Standard 这个例子在$(top_builddir)/test/test_default.c, test_default.conf. 源代码是: \end_layout \begin_layout LyX-Code #include \end_layout \begin_layout LyX-Code #include "zlog.h" \end_layout \begin_layout LyX-Code int main(int argc, char** argv) \end_layout \begin_layout LyX-Code { \end_layout \begin_deeper \begin_layout LyX-Code int rc; \end_layout \begin_layout LyX-Code rc = dzlog_init("test_default.conf", "my_cat"); \end_layout \begin_layout LyX-Code if (rc) { \end_layout \begin_deeper \begin_layout LyX-Code printf("init failed \backslash n"); \end_layout \begin_layout LyX-Code return -1; \end_layout \end_deeper \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code dzlog_info("hello, zlog"); \end_layout \begin_layout LyX-Code zlog_fini(); \end_layout \begin_layout LyX-Code return 0; \end_layout \end_deeper \begin_layout LyX-Code } \end_layout \begin_layout Standard 配置文件是test_default.conf,和test_hello.conf一模一样,最后执行程序的输出也一样。区别在于这里用了dzlog API,内含一个默认的 zlog_category_t。详见 \begin_inset CommandInset ref LatexCommand ref reference "sec:dzlog接口" \end_inset 。 \end_layout \begin_layout Chapter Syslog 模型 \end_layout \begin_layout Section 分类(Category)、规则(Rule)和格式(Format) \end_layout \begin_layout Standard zlog有3个重要的概念:分类(Category)、规则(Rule)和格式(Format)。 \end_layout \begin_layout Standard 分类(Category)用于区分不同的输入。代码中的分类变量的名字是一个字符串,在一个程序里面可以通过获取不同的分类名的category用来后面输出不同分类的日 志,用于不同的目的。 \end_layout \begin_layout Standard 格式(Format)是用来描述输出日志的格式,比如是否有带有时间戳,是否包含文件位置信息等,上面的例子里面的格式simple就是简单的用户输入的信息+换行符。 \end_layout \begin_layout Standard 规则(Rule)则是把分类、级别、输出文件、格式组合起来,决定一条代码中的日志是否输出,输出到哪里,以什么格式输出。 \end_layout \begin_layout Standard 所以,当程序执行下面的语句的时候 \end_layout \begin_layout LyX-Code zlog_category_t *c; \end_layout \begin_layout LyX-Code c = zlog_get_category("my_cat"); \end_layout \begin_layout LyX-Code zlog_info(c, "hello, zlog"); \end_layout \begin_layout Standard zlog会找到c的名字是"my_cat",对应的配置文件中的规则是 \end_layout \begin_layout LyX-Code [rules] \end_layout \begin_layout LyX-Code my_cat.DEBUG >stdout; simple \end_layout \begin_layout Standard 然后库会检查,目前这条日志的级别是否符合规则中的级别来决定是否输出。因为INFO>=DEBUG,所以这条日志会被输出。并且根据这条规则,会被输出到stdout( 标准输出) ,输出的格式是simple,在配置文件中定义是 \end_layout \begin_layout LyX-Code [formats] \end_layout \begin_layout LyX-Code simple = "%m%n" \end_layout \begin_layout Standard 最后在屏幕上打印 \end_layout \begin_layout LyX-Code hello, zlog \end_layout \begin_layout Standard 这就是整个过程。用户要做就是写自己的信息。日志往哪里输出,以什么格式输出,都是库和配置文件来完成的。 \end_layout \begin_layout Section syslog模型和log4j模型的区别 \end_layout \begin_layout Standard 好,那么目前这个模型和syslog有什么关系呢?至今为止,这个模型还是比较像log4j。log4j的模型里面有logger, appender和layout。区 别在于,在log4j里面,代码中的logger和配置中的logger是一一对应的,并且一个logger有唯一的级别。一对一关系是log4j, log4cxx, log4cpp, log4cplus, log4net的唯一选择。 \end_layout \begin_layout Standard 但这种模型是不灵活的,他们发明了过滤器(filters)来弥补,但这只能把事情弄得更加混乱。所以让我们把目光转回syslog的模型,这是一个设计的很简易正确的模 型。 \end_layout \begin_layout Standard 继续上一节的例子,如果在zlog的配置文件中有这么2行规则: \end_layout \begin_layout LyX-Code [rules] \end_layout \begin_layout LyX-Code my_cat.DEBUG >stdout; simple \end_layout \begin_layout LyX-Code my_cat.INFO >stdout; \end_layout \begin_layout Standard 然后,一行代码会产生两行输出: \end_layout \begin_layout LyX-Code hello, zlog \end_layout \begin_layout LyX-Code 2012-05-29 10:41:36 INFO [11288:test_hello.c:41] hello, zlog \end_layout \begin_layout Standard 现在一个代码中的分类对应配置文件中的两条规则。log4j的用户可能会说:"这很好,但是只要在log4j里面放两个appender也能做的一样。"所以继续看下一个 例子: \end_layout \begin_layout LyX-Code [rules] \end_layout \begin_layout LyX-Code my_cat.WARN "/var/log/aa.log" \end_layout \begin_layout LyX-Code my_cat.DEBUG "/var/log/bb.log" \end_layout \begin_layout Standard 代码是: \end_layout \begin_layout LyX-Code zlog_info(c, "info, zlog"); \end_layout \begin_layout LyX-Code zlog_debug(c, "debug, zlog"); \end_layout \begin_layout Standard 最后,在aa.log中只有一条日志 \end_layout \begin_layout LyX-Code 2012-05-29 10:41:36 INFO [11288:test_hello.c:41] info, zlog \end_layout \begin_layout Standard 但在bb.log里面有两条 \end_layout \begin_layout LyX-Code 2012-05-29 10:41:36 INFO [11288:test_hello.c:41] info, zlog \end_layout \begin_layout LyX-Code 2012-05-29 10:41:36 DEBUG [11288:test_hello.c:42] debug, zlog \end_layout \begin_layout Standard 从这个例子能看出来区别。log4j无法轻易的做到这一点。在zlog里面,一个分类可以对应多个规则,每个规则有自己的级别、输出和格式。这就让用户能按照需求过滤、多 渠道输出自己的所有日志。 \end_layout \begin_layout Section 扩展syslog模型 \end_layout \begin_layout Standard 所以到现在你能看出来zlog的模型更像syslog的模型。不幸的是,在syslog里面,设施(facility)是个int型,而且必须从系统定义的那几种里面选择 。zlog走的远一点,用一个字符串来标识分类。 \end_layout \begin_layout Standard syslog有一个通配符"*",匹配所有的设施(facility)。zlog里面也一样,"*"匹配所有分类。这提供了一个很方便的办法来重定向你的系统中各个组件的 错误。只要这么写: \end_layout \begin_layout LyX-Code [rules] \end_layout \begin_layout LyX-Code *.error "/var/log/error.log" \end_layout \begin_layout Standard zlog强大而独有的特性是上下级分类匹配。如果你的分类是这样的: \end_layout \begin_layout LyX-Code c = zlog_get_category("my_cat"); \end_layout \begin_layout Standard 然后配置文件是这样的 \end_layout \begin_layout LyX-Code [rules] \end_layout \begin_layout LyX-Code my_cat.* "/var/log/my_cat.log" \end_layout \begin_layout LyX-Code my_.NOTICE "/var/log/my.log" \end_layout \begin_layout Standard 这两条规则都匹配c分类"my_cat"。通配符"_" 表示上级分类。 "my_"是"my_cat"和"my_dog"的上级分类。还有一个通配符是"!",详见 \begin_inset CommandInset ref LatexCommand ref reference "sub:分类匹配" \end_inset \end_layout \begin_layout Chapter 配置文件 \end_layout \begin_layout Standard 大部分的zlog的行为取决于配置文件:把日志打到哪里去,用什么格式,怎么转档。配置文件是zlog的黑话,我尽量把这个黑话设计的简单明了。这是个配置文件例子: \end_layout \begin_layout LyX-Code # comments \end_layout \begin_layout LyX-Code [global] \end_layout \begin_layout LyX-Code strict init = true \end_layout \begin_layout LyX-Code buffer min = 1024 \end_layout \begin_layout LyX-Code buffer max = 2MB \end_layout \begin_layout LyX-Code rotate lock file = /tmp/zlog.lock \end_layout \begin_layout LyX-Code default format = "%d.%us %-6V (%c:%F:%L) - %m%n" \end_layout \begin_layout LyX-Code file perms = 600 \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code [levels] \end_layout \begin_layout LyX-Code TRACE = 10 \end_layout \begin_layout LyX-Code CRIT = 130, LOG_CRIT \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code [formats] \end_layout \begin_layout LyX-Code simple = "%m%n" \end_layout \begin_layout LyX-Code normal = "%d %m%n" \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code [rules] \end_layout \begin_layout LyX-Code default.* >stdout; simple \end_layout \begin_layout LyX-Code *.* "%12.2E(HOME)/log/%c.log", 1MB*12; simple \end_layout \begin_layout LyX-Code my_.INFO >stderr; \end_layout \begin_layout LyX-Code my_cat.!ERROR "/var/log/aa.log" \end_layout \begin_layout LyX-Code my_dog.=DEBUG >syslog, LOG_LOCAL0; simple \end_layout \begin_layout LyX-Code my_mice.* $user_define; \end_layout \begin_layout Standard 有关单位:当设置内存大小或者大数字时,可以设置1k 5GB 4M这样的单位: \end_layout \begin_layout LyX-Code # 1k => 1000 bytes \end_layout \begin_layout LyX-Code # 1kb => 1024 bytes \end_layout \begin_layout LyX-Code # 1m => 1000000 bytes \end_layout \begin_layout LyX-Code # 1mb => 1024*1024 bytes \end_layout \begin_layout LyX-Code # 1g => 1000000000 bytes \end_layout \begin_layout LyX-Code # 1gb => 1024*1024*1024 byte \end_layout \begin_layout Standard 单位是大小写不敏感的,所以1GB 1Gb 1gB是等效的。 \end_layout \begin_layout Section 全局参数 \end_layout \begin_layout Standard 全局参数以[global]开头。[]代表一个节的开始,四个小节的顺序不能变,依次为global-levels-formats-rules。这一节可以忽略不写。语 法为 \end_layout \begin_layout LyX-Code (key) = (value) \end_layout \begin_layout Itemize strict init \end_layout \begin_deeper \begin_layout Standard 如果"strict init"是true,zlog_init()将会严格检查所有的格式和规则,任何错误都会导致zlog_init() 失败并且返回-1。当"st rict init"是false的时候,zlog_init()会忽略错误的格式和规则。 这个参数默认为true。 \end_layout \end_deeper \begin_layout Itemize reload conf period \end_layout \begin_deeper \begin_layout Standard 这个选项让zlog能在一段时间间隔后自动重载配置文件。重载的间隔以每进程写日志的次数来定义。当写日志次数到了一定值后,内部将会调用zlog_reload()进行 重载。每次zlog_reload()或者zlog_init()之后重新计数累加。因为zlog_reload()是原子性的,重载失败继续用当前的配置信息,所以自动 重载是安全的。默认值是0,自动重载是关闭的。 \end_layout \end_deeper \begin_layout Itemize buffer min \end_layout \begin_layout Itemize buffer max \end_layout \begin_deeper \begin_layout Standard zlog在堆上为每个线程申请缓存。"buffer min"是单个缓存的最小值,zlog_init()的时候申请这个长度的内存。写日志的时候,如果单条日志长度大于 缓存,缓存会自动扩充,直到到"buffer max"。 单条日志再长超过"buffer max"就会被截断。如果 "buffer max" 是 0,意味着不限制缓存,每次扩充为原先的2倍,直到这个进程用完所有内存为止。缓存大小可以加上 KB, MB 或 GB这些单位。默认来说"buffer min"是 1K , "buffer max" 是2MB。 \end_layout \end_deeper \begin_layout Itemize rotate lock file \end_layout \begin_deeper \begin_layout Standard 这个选项指定了一个锁文件,用来保证多进程情况下日志安全转档。zlog会在zlog_init()时候以读写权限打开这个文件。确认你执行程序的用户有权限创建和读写这 个文件。转档日志的伪代码是: \end_layout \begin_layout LyX-Code write(log_file, a_log) \end_layout \begin_layout LyX-Code if (log_file > 1M) \end_layout \begin_deeper \begin_layout LyX-Code if (pthread_mutex_lock succ && fcntl_lock(lock_file) succ) \end_layout \begin_deeper \begin_layout LyX-Code if (log_file > 1M) rotate(log_file); \end_layout \begin_layout LyX-Code fcntl_unlock(lock_file); \end_layout \begin_layout LyX-Code pthread_mutex_unlock; \end_layout \end_deeper \end_deeper \begin_layout Standard mutex_lock用于多线程, fcntl_lock用于多进程。fcntl_lock是POSIX建议锁。详见man 3 fcntl。这个锁是全系统有效的。在某 个进程意外死亡后,操作系统会释放此进程持有的锁。这就是我为什么用fcntl锁来保证安全转档。进程需要对锁文件有读写权限。 \end_layout \begin_layout Standard 默认来说,rotate lock file = self。在这种情况下,zlog不会创建任何锁文件,用配置文件作为锁文件。fcntl是建议锁,所以用户可以自由的 修改存储他们的配置文件。一般来说,单个日志文件不会被不同操作系统用户的进程转档,所以用配置文件作为锁文件是安全的。 \end_layout \begin_layout Standard 如果你设置其他路径作为锁文件,例如/tmp/zlog.lock,zlog会在zlog_init()的时候创建这个文件。如果有多个操作系统用户的进程需要转档同一个日 志文件,确认这个锁文件对于多个用户都可读写。默认值是/tmp/zlog.lock。 \end_layout \end_deeper \begin_layout Itemize default format \end_layout \begin_deeper \begin_layout Standard 这个参数是缺省的日志格式,默认值为: \end_layout \begin_layout LyX-Code "%d %V [%p:%F:%L] %m%n" \end_layout \begin_layout Standard 这种格式产生的输出类似这样: \end_layout \begin_layout LyX-Code 2012-02-14 17:03:12 INFO [3758:test_hello.c:39] hello, zlog \end_layout \end_deeper \begin_layout Itemize file perms \end_layout \begin_deeper \begin_layout Standard 这个指定了创建日志文件的缺省访问权限。必须注意的是最后的产生的日志文件的权限为"file perms"& ~umask。默认为600,只允许当前用户读写。 \end_layout \end_deeper \begin_layout Itemize fsync period \end_layout \begin_deeper \begin_layout Standard 在每条规则写了一定次数的日志到文件后,zlog会调用fsync(3)来让操作系统马上把数据写到硬盘。次数是每条规则单独统计的,并且在zlog_reload()后 会被清0。必须指出的是,在日志文件名是动态生成或者被转档的情况下,zlog不能保证把所有文件都搞定,zlog只fsync()那个时候刚刚write()的文件描述 符。这提供了写日志速度和数据安全性之间的平衡。例子: \end_layout \begin_layout LyX-Code $ time ./test_press_zlog 1 10 100000 \end_layout \begin_layout LyX-Code real 0m1.806s \end_layout \begin_layout LyX-Code user 0m3.060s \end_layout \begin_layout LyX-Code sys 0m0.270s \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code $ wc -l press.log \end_layout \begin_layout LyX-Code 1000000 press.log \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code $ time ./test_press_zlog 1 10 100000 #fsync period = 1K \end_layout \begin_layout LyX-Code real 0m41.995s \end_layout \begin_layout LyX-Code user 0m7.920s \end_layout \begin_layout LyX-Code sys 0m0.990s \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code $ time ./test_press_zlog 1 10 100000 #fsync period = 10K \end_layout \begin_layout LyX-Code real 0m6.856s \end_layout \begin_layout LyX-Code user 0m4.360s \end_layout \begin_layout LyX-Code sys 0m0.550s \end_layout \begin_layout Standard 如果你极度在乎安全而不是速度的话,用同步IO文件,见 \begin_inset CommandInset ref LatexCommand ref reference "ite:同步IO文件" \end_inset 。默认值是0,由操作系统来决定什么时候刷缓存到文件。 \end_layout \end_deeper \begin_layout Section 日志等级自定义 \end_layout \begin_layout Standard 这一节以[levels]开始。用于定义用户自己的日志等级,建议和用户自定义的日志记录宏一起使用。这一节可以忽略不写。语法为: \end_layout \begin_layout LyX-Code (level string) = (level int), (syslog level, optional) \end_layout \begin_layout Standard (level int)必须在[1,253]这个范围内,越大越重要。(syslog level)是可选的,如果不设默认为LOG_DEBUG。 \end_layout \begin_layout Standard 详见 \begin_inset CommandInset ref LatexCommand ref reference "sec:用户自定义等级" \end_inset 。 \end_layout \begin_layout Section 格式(Formats) \end_layout \begin_layout Standard 这一节以[formats]开始。用来定义日志的格式。语法为: \end_layout \begin_layout LyX-Code (name) = "(actual formats)" \end_layout \begin_layout Standard 很好理解,(name)被后面的规则使用。(name)必须由数字和字母组成,下划线"_"也算字母。(actual format)前后需要有双引号。 (actual formats)可以由转换字符组成,见下一节。 \end_layout \begin_layout Section 转换格式串 \begin_inset CommandInset label LatexCommand label name "sec:转换格式串" \end_inset \end_layout \begin_layout Standard 转换格式串的设计是从C的printf函数里面抄来的。一个转换格式串由文本字符和转换说明组成。 \end_layout \begin_layout Standard 转换格式串用在规则的日志文件路径和输出格式(format)中。 \end_layout \begin_layout Standard 你可以把任意的文本字符放到转换格式串里面。 \end_layout \begin_layout Standard 每个转换说明都是以百分号(%)打头的,后面跟可选的宽度修饰符,最后以转换字符结尾。转换字符决定了输出什么数据,例如分类名、级别、时间日期、进程号等等。宽度修饰符 控制了这个字段的最大最小宽度、左右对齐。下面是简单的例子。 \end_layout \begin_layout Standard 如果转换格式串是: \end_layout \begin_layout LyX-Code "%d(%m-%d %T) %-5V [%p:%F:%L] %m%n". \end_layout \begin_layout Standard 源代码中的写日志语句是: \end_layout \begin_layout LyX-Code zlog_info(c, "hello, zlog"); \end_layout \begin_layout Standard 将会输出: \end_layout \begin_layout LyX-Code 02-14 17:17:42 INFO [4935:test_hello.c:39] hello, zlog \end_layout \begin_layout Standard 可以注意到,在文本字符和转换说明之间没有显式的分隔符。zlog解析的时候知道哪里是转换说明的开头和结尾。在这个例子里面%-5p这个转换说明决定了日志级别要被左对 齐,占5个字符宽。 \end_layout \begin_layout Subsection 转换字符 \end_layout \begin_layout Standard 可以被辨认的转换字符是 \end_layout \begin_layout Standard \begin_inset Tabular \begin_inset Text \begin_layout Plain Layout 字符 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 效果 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 例子 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %c \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 分类名 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout aa_bb \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %d() \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 打日志的时间。这个后面要跟一对小括号()内含说明具体的日期格式。就像%d(%F)或者%d(%m-%d %T)。如果不跟小括号,默认是%d(%F %T)。括号内的格式和 strftime(2)的格式一致。详见 \begin_inset CommandInset ref LatexCommand ref reference "sub:时间字符" \end_inset \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %d(%F) 2011-12-01 \end_layout \begin_layout Plain Layout %d(%m-%d %T) 12-01 17:17:42 \end_layout \begin_layout Plain Layout %d(%T) 17:17:42.035 \end_layout \begin_layout Plain Layout %d 2012-02-14 17:03:12 \end_layout \begin_layout Plain Layout %d() \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %E() \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 获取环境变量的值 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %E(USER) simpson \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %ms \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 毫秒,3位数字字符串 \end_layout \begin_layout Plain Layout 取自gettimeofday(2) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 013 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %us \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 微秒,6位数字字符串 \end_layout \begin_layout Plain Layout 取自gettimeofday(2) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 002323 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %F \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 源代码文件名,来源于__FILE__宏。在某些编译器下 __FILE__是绝对路径。用%f来去掉目录只保留文件名,或者编译器有选项可以调节 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout test_hello.c \end_layout \begin_layout Plain Layout 或者在某些编译器下 \end_layout \begin_layout Plain Layout /home/zlog/src/test/test_hello.c \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %f \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 源代码文件名,输出$F最后一个'/'后面的部分。当然这会有一定的性能损失 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout test_hello.c \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %H \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 主机名,来源于 gethostname(2) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout zlog-dev \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %L \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 源代码行数,来源于__LINE__宏 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 135 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %m \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 用户日志,用户从zlog函数输入的日志。 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout hello, zlog \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %M \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout MDC (mapped diagnostic context),每个线程一张键值对表,输出键相对应的值。后面必需跟跟一对小括号()内含键。例如 %M(clientNumber) ,clientNumbe是键。 详见 \begin_inset CommandInset ref LatexCommand ref reference "sec:MDC" \end_inset \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %M(clientNumber) 12345 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %n \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 换行符,目前还不支持windows换行符 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout \backslash n \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %p \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 进程ID,来源于getpid() \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 2134 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %U \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 调用函数名,来自于__func__(C99)或者__FUNCTION__(gcc),如果编译器支持的话。 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout main \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %V \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 日志级别,大写 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout INFO \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %v \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 日志级别,小写 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout info \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %t \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 16进制表示的线程ID,来源于pthread_self() \end_layout \begin_layout Plain Layout "0x%x",(unsigned int) pthread_t \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 0xba01e700 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %T \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 相当于%t,不过是以长整型表示的 \end_layout \begin_layout Plain Layout "%lu", (unsigned long) pthread_t \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 140633234859776 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %% \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 一个百分号 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout % \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %[其他字符] \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 解析为错误,zlog_init()将会失败 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout \end_layout \end_inset \end_inset \end_layout \begin_layout Subsection 宽度修饰符 \end_layout \begin_layout Standard 一般来说数据按原样输出。不过,有了宽度修饰符,就能够控制最小字段宽度、最大字段宽度和左右对齐。当然这要付出一定的性能代价。 \end_layout \begin_layout Standard 可选的宽度修饰符放在百分号和转换字符之间。 \end_layout \begin_layout Standard 第一个可选的宽度修饰符是左对齐标识,减号(-)。然后是可选的最小字段宽度,这是一个十进制数字常量,表示最少有几个字符会被输出。如果数据本来没有那么多字符,将会填 充空格(左对齐或者右对齐)直到最小字段宽度为止。默认是填充在左边也就是右对齐。当然你也可以使用左对齐标志,指定为填充在右边来左对齐。填充字符为空格(space) 。如果数据的宽度超过最小字段宽度,则按照数据的宽度输出,永远不会截断数据。 \end_layout \begin_layout Standard 这种行为可以用最大字段宽度来改变。最大字段宽度是放在一个句点号(.)后面的十进制数字常量。如果数据的宽度超过了最大字段宽度,则尾部多余的字符(超过最大字段宽度的部 分)将会被截去。 最大字段宽度是8,数据的宽度是10,则最后两个字符会被丢弃。这种行为和C的printf是一样的,把后面的部分截断。 \end_layout \begin_layout Standard 下面是各种宽度修饰符和分类转换字符配合一起用的例子。 \end_layout \begin_layout Standard \begin_inset Tabular \begin_inset Text \begin_layout Plain Layout 宽度修饰符 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 左对齐 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 最小字段宽度 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 最大字段宽度 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 附注 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %20c \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 否 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 20 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 无 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 左补充空格,如果分类名小于20个字符长。 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %-20c \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 是 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 20 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 无 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 右补充空格,如果分类名小于20个字符长。 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %.30c \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 无 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 无 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 30 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 如果分类名大于30个字符长,取前30个字符,去掉后面的。 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %20.30c \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 否 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 20 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 30 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 如果分类名小于20个字符长,左补充空格。如果在20-30之间,按照原样输出。如果大于30个字符长,取前30个字符,去掉后面的。 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %-20.30c \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 是 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 20 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 30 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 如果分类名小于20个字符长,右补充空格。如果在20-30之间,按照原样输出。如果大于30个字符长,取前30个字符,去掉后面的。 \end_layout \end_inset \end_inset \end_layout \begin_layout Subsection 时间字符 \begin_inset CommandInset label LatexCommand label name "sub:时间字符" \end_inset \end_layout \begin_layout Standard 这里是转换字符d支持的时间字符。 \end_layout \begin_layout Standard 所有字符都是由strftime(2)生成的,在我的linux操作系统上支持的是: \end_layout \begin_layout Standard \begin_inset Tabular \begin_inset Text \begin_layout Plain Layout 字符 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 效果 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 例子 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %a \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 一星期中各天的缩写名,根据locale显示 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout Wed \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %A \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 一星期中各天的全名,根据locale显示 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout Wednesday \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %b \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 缩写的月份名,根据locale显示 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout Mar \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %B \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 月份全名,根据locale显示 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout March \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %c \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 当地时间和日期的全表示, 根据locale显示 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout Thu Feb 16 14:16:35 2012 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %C \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 世纪 (年/100),2位的数字(SU) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 20 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %d \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 一个月中的某一天 (01-31) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 06 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %D \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 相当于%m/%d/%y. (呃,美国人专用,美国人要知道在别的国家%d/%m/%y 才是主流。也就是说在国际环境下这个格式容易造成误解,要少用) (SU) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 02/16/12 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %e \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 就像%d,一个月中的某一天,但是头上的0被替换成空格(SU) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 6 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %F \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 相当于%Y-%m-%d (ISO 8601日期格式)(C99) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 2012-02-16 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %G \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout The ISO 8601 week-based year (see NOTES) with century as a decimal number. The 4-digit year corre‐ sponding to the ISO week number (see %V). This has the same format and value as %Y, except that if the ISO week number belongs to the previous or next year, that year is used instead. (TZ) \end_layout \begin_layout Plain Layout 大意是采用%V定义的年,如果那年的前几天不算新年的第一周,就算上一年 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 2012 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %g \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 相当于%G,就是不带世纪 (00-99). (TZ) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 12 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %h \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 相当于%b(SU) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout Feb \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %H \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 小时,24小时表示(00-23) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 14 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %I \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 小时,12小时表示(01-12) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 02 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %j \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 一年中的各天(001-366) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 047 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %k \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 小时,24小时表示( 0-23); 一位的前面为空格 (可和%H比较) (TZ) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 15 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %l \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 小时,12小时表示( 0-12); 一位的前面为空格 (可和%比较)(TZ) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 3 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %m \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 月份(01-12) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 02 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %M \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 分钟(00-59) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 11 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %n \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 换行符 (SU) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout \backslash n \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %p \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout "AM" 或 "PM",根据当时的时间,根据locale显示相应的值,例如"上午"、"下午" 。 中午是"PM",凌晨是"AM" \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout PM \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %P \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 相当于%p不过是小写,根据locale显示相应的值 (GNU) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout pm \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %r \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 时间+后缀AM或PM。在POSIX locale下相当于%I:%M:%S %p. (SU) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 03:11:54 PM \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %R \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 小时(24小时制):分钟 (%H:%M) (SU) 如果要带秒的,见%T \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 15:11 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %s \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout Epoch以来的秒数,也就是从1970-01-01 00:00:00 UTC. (TZ) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 1329376487 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %S \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 秒(00-60). (允许60是为了闰秒) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 54 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %t \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 制表符tab(SU) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %T \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 小时(24小时制):分钟:秒 (%H:%M:%S) (SU) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 15:14:47 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %u \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 一周的天序号(1-7),周一是1,另见%w (SU) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 4 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %U \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 一年中的星期序号(00-53),周日是一周的开始,一年中第一个周日所在的周是第01周。另见%V和%W \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 07 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %V \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout ISO 8601星期序号(01-53),01周是第一个至少有4天在新年的周。另见%U 和%W(SU) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 07 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %w \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 一周的天序号(0-6),周日是0。另见%u \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 4 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %W \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 一年中的星期序号(00-53),周一是一周的开始,一年中第一个周一所在的周是第01周。另见%V和%W \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 07 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %x \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 当前locale下的偏好日期 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 02/16/12 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %X \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 当前locale下的偏好时间 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 15:14:47 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %y \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 不带世纪数目的年份(00-99) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 12 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %Y \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 带世纪数目的年份 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 2012 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %z \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 当前时区相对于GMT时间的偏移量。采用RFC 822-conformant来计算(话说我也不知道是啥) (using "%a, %d %b %Y %H:%M:%S %z"). (GNU) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout +0800 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %Z \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 时区名(如果有的话) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout CST \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %% \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 一个百分号 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout % \end_layout \end_inset \end_inset \end_layout \begin_layout Section 规则(Rules) \end_layout \begin_layout Standard 这一节以[rules]开头。这个描述了日志是怎么被过滤、格式化以及被输出的。这节可以忽略不写,不过这样就没有日志输出了,嘿嘿。语法是: \end_layout \begin_layout LyX-Code (category).(level) (output), (options, optional); (format name, optional) \end_layout \begin_layout Standard 当zlog_init()被调用的时候,所有规则都会被读到内存中。当zlog_get_category()被调用,规则就被被分配给分类( \begin_inset CommandInset ref LatexCommand ref reference "sub:分类匹配" \end_inset )。在实际写日志的时候,例如zlog_info()被调用的时候,就会比较这个INFO和各条规则的等级,来决定这条日志会不会通过这条规则输出。当zlog_relo ad()被调用的时候,配置文件会被重新读入,包括所有的规则,并且重新计算分类对应的规则。 \end_layout \begin_layout Subsection 级别匹配 \end_layout \begin_layout Standard zlog有6个默认的级别:"DEBUG", "INFO", "NOTICE", "WARN", "ERROR"和"FATAL"。就像其他的日志函数库那样, aa.DEBUG意味着任何大于等于DEBUG级别的日志会被输出。当然还有其他的表达式。配置文件中的级别是大小写不敏感的。 \end_layout \begin_layout Standard \begin_inset Tabular \begin_inset Text \begin_layout Plain Layout 表达式 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 含义 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout * \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 所有等级 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout aa.debug \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 代码内等级>=debug \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout aa.=debug \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 代码内等级==debug \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout aa.!debug \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 代码内等级!=debug \end_layout \end_inset \end_inset \end_layout \begin_layout Standard 用户可以自定义等级,详见 \begin_inset CommandInset ref LatexCommand ref reference "sec:用户自定义等级" \end_inset 。 \end_layout \begin_layout Subsection 分类匹配 \begin_inset CommandInset label LatexCommand label name "sub:分类匹配" \end_inset \end_layout \begin_layout Standard \end_layout \begin_layout Standard 分类必须由数字和字母组成,下划线"_"也算字母。 \end_layout \begin_layout Standard \begin_inset Tabular \begin_inset Text \begin_layout Plain Layout 总结 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 配置文件规则分类 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 匹配的代码分类 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 不匹配的代码分类 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout *匹配所有 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout *.* \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout aa, aa_bb, aa_cc, xx, yy ... \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout NONE \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 以_结尾的分类匹配本级及下级分类 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout aa_.* \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout aa, aa_bb, aa_cc, aa_bb_cc \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout xx, yy \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 不以_结尾的精确匹配分类名 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout aa.* \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout aa \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout aa_bb, aa_cc, aa_bb_cc \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout !匹配那些没有找到规则的分类 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout !.* \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout xx \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout aa(as it matches rules above) \end_layout \end_inset \end_inset \end_layout \begin_layout Subsection 输出动作 \end_layout \begin_layout Standard 目前zlog支持若干种输出,语法是: \end_layout \begin_layout Standard [输出], [附加选项, 可选]; [format(格式)名, 可选] \end_layout \begin_layout Standard \begin_inset Tabular \begin_inset Text \begin_layout Plain Layout 动作 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 输出字段 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 附加选项 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 标准输出 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout >stdout \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 无意义 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 标准错误输出 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout >stderr \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 无意义 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 输出到syslog \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout >syslog \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout syslog设施(facilitiy): \end_layout \begin_layout Plain Layout LOG_USER(default), LOG_LOCAL[0-7] \end_layout \begin_layout Plain Layout 必填 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 管道输出 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout |cat \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 无意义 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 文件 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout "文件路径" \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 文件转档,详见 \begin_inset CommandInset ref LatexCommand ref reference "sec:文件转档" \end_inset \end_layout \begin_layout Plain Layout 10M * 3 ~ "press.#r.log" \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 同步IO文件 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout -"文件路径" \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 用户自定义输出 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout $name \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout "path" 动态或者静态的用于record输出 \end_layout \end_inset \end_inset \end_layout \begin_layout Itemize stdout, stderr, syslog \end_layout \begin_deeper \begin_layout Standard 如表格描述,其中只有sylog的附加选项是有意义并必须写的。 \end_layout \begin_layout Standard 值得注意的是,zlog在写日志的时候会用这样的语句 \end_layout \begin_layout LyX-Code write(STDOUT_FILENO, zlog_buf_str(a_thread->msg_buf), zlog_buf_len(a_thread->msg _buf)) \end_layout \begin_layout Standard 而如果你的程序是个守护进程,在启动的时候把STDOUT_FILENO,也就是1的文件描述符关掉的话,会发生什么结果呢? \end_layout \begin_layout Standard 日志会被写到新的1的文件描述符所代表的文件里面!我收到过邮件,说zlog把日志写到自己的配置文件里面去了! \end_layout \begin_layout Standard 所以,千万不要在守护进程的规则里面加上>stdout或>stderr。这会产生不可预料的结果……如果一定要输出到终端,用"/dev/tty"代替。 \end_layout \end_deeper \begin_layout Itemize 管道输出 \end_layout \begin_deeper \begin_layout LyX-Code *.* | /usr/bin/cronolog /www/logs/example_%Y%m%d.log ; normal \end_layout \begin_layout Standard 这是一个将zlog的输出到管道后接cronolog的例子。实现的原理很简单,在zlog_init的时候调用popen("/usr/bin/cronolog /www/logs/example_%Y%m%d.log", "w"),后面往这个文件描述符里面写指定格式的日志。使用cronolog来生成按天分割的日志效率比 zlog自己的动态路径的效率要高,因为通过管道,无须每次打开关闭动态路径的文件描述符。 \end_layout \begin_layout LyX-Code [rules] \end_layout \begin_layout LyX-Code *.* "press%d(%Y%m%d).log" \end_layout \begin_layout LyX-Code $ time ./test_press_zlog 1 10 100000 \end_layout \begin_layout LyX-Code real 0m4.240s \end_layout \begin_layout LyX-Code user 0m2.500s \end_layout \begin_layout LyX-Code sys 0m5.460s \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code [rules] \end_layout \begin_layout LyX-Code *.* | /usr/bin/cronolog press%Y%m%d.log \end_layout \begin_layout LyX-Code $ time ./test_press_zlog 1 10 100000 \end_layout \begin_layout LyX-Code real 0m1.911s \end_layout \begin_layout LyX-Code user 0m1.980s \end_layout \begin_layout LyX-Code sys 0m1.470s \end_layout \begin_layout Standard 不过,使用管道也是有限制的: \end_layout \begin_layout Itemize POSIX.1-2001保证读写不大于PIPE_BUF大小的内容是原子的。linux上PIPE_BUF为4096。 \end_layout \begin_layout Itemize 单条日志的长度超过PIPE_BUF的时候并且有多个有父子关系的进程写通过zlog写同一个管道,也就是在zlog_init之后fork多个子进程,此时只有一个cr onolog的进程监听一个管道描述符,日志内容可能会交错。 \end_layout \begin_layout Itemize 多个进程分别zlog_init,启动多个cronolog进程,写拥有同一个文件路径的日志文件,即使单条日志长度不超过PIPE_BUF,也有可能导致日志交错,因为 cronolog读到的文件流是连续的,它不知道单条日志的边界在哪里。 \end_layout \begin_layout Standard 所以,总结一下,使用管道来输出到单个日志文件的情况是: \end_layout \begin_layout Itemize 单进程写,单条日志长度不限制。单进程内内的多线程写日志的原子性已经由zlog保证了。 \end_layout \begin_layout Itemize 有父子关系的多进程,单条日志长度不能超过PIPE_BUF(4096) \end_layout \begin_layout Itemize 无父子关系的多进程使用管道同时写一个日志,无论单条日志长度是多少,都有可能导致日志交错。 \end_layout \begin_layout Standard zlog本身的直接文件输出能保证即使是多进程,同时调用zlog写一个日志文件也不会产生交错,见下。 \end_layout \end_deeper \begin_layout Itemize 文件 \end_layout \begin_deeper \begin_layout Itemize 文件路径 \end_layout \begin_deeper \begin_layout Standard 可以是相对路径或者绝对路径,被双引号"包含。转换格式串可以用在文件路径上。例如文件路径是 "%E(HOME)/log/out.log",环境变量$HOME是/ho me/harry,那最后的输出文件是/home/harry/log/output.log。转换格式串详见 \begin_inset CommandInset ref LatexCommand ref reference "sec:转换格式串" \end_inset 。 \end_layout \begin_layout Standard zlog的文件功能极为强大,例如 \end_layout \begin_layout Enumerate 输出到命名管道(FIFO),必须在调用前由mkfifo(1)创建 \end_layout \begin_deeper \begin_layout LyX-Code *.* "/tmp/pipefile" \end_layout \end_deeper \begin_layout Enumerate 输出到NULL,也就是不输出 \end_layout \begin_deeper \begin_layout LyX-Code *.* "/dev/null" \end_layout \end_deeper \begin_layout Enumerate 在任何情况下输出到终端 \end_layout \begin_deeper \begin_layout LyX-Code *.* "/dev/tty" \end_layout \end_deeper \begin_layout Enumerate 每线程一个日志,在程序运行的目录下 \end_layout \begin_deeper \begin_layout LyX-Code *.* "%T.log" \end_layout \end_deeper \begin_layout Enumerate 输出到有进程号区分的日志,每天,在$HOME/log目录,每1GB转档一次,保持5个日志文件。 \end_layout \begin_deeper \begin_layout LyX-Code *.* "%E(HOME)/log/aa.%p.%d(%F).log",1GB * 5 \end_layout \end_deeper \begin_layout Enumerate aa_及下级分类,每个分类一个日志 \end_layout \begin_deeper \begin_layout LyX-Code aa_.* "/var/log/%c.log" \end_layout \end_deeper \end_deeper \begin_layout Itemize 文件转档 \end_layout \begin_deeper \begin_layout Standard 控制文件的大小和个数。zlog根据这个字段来转档,当日志文件太大的时候。例如 \end_layout \begin_layout LyX-Code "%E(HOME)/log/out.log", 1M * 3 ~ "%E(HOME)/log/out.log.#r" \end_layout \begin_layout Standard 这三个参数都不是必填项,zlog的转档功能详见 \begin_inset CommandInset ref LatexCommand ref reference "sec:文件转档" \end_inset \end_layout \end_deeper \begin_layout Itemize 同步IO文件 \begin_inset CommandInset label LatexCommand label name "ite:同步IO文件" \end_inset \end_layout \begin_deeper \begin_layout Standard 在文件路径前加上一个"-"就打开了同步IO选项。在打开文件(open)的时候,会以O_SYNC选项打开,这时候每次写日志操作都会等操作系统把数据写到硬盘后才返回 。这个选项极为耗时: \end_layout \begin_layout LyX-Code $ time ./test_press_zlog 100 1000 \end_layout \begin_layout LyX-Code real 0m0.732s \end_layout \begin_layout LyX-Code user 0m1.030s \end_layout \begin_layout LyX-Code sys 0m1.080s \end_layout \begin_layout LyX-Code $ time ./test_press_zlog 100 1000 # synchronous I/O open \end_layout \begin_layout LyX-Code real 0m20.646s \end_layout \begin_layout LyX-Code user 0m2.570s \end_layout \begin_layout LyX-Code sys 0m6.950s \end_layout \end_deeper \end_deeper \begin_layout Itemize 格式名 \end_layout \begin_deeper \begin_layout Standard 是可选的,如果不写,用全局配置里面的默认格式: \end_layout \begin_layout LyX-Code [global] \end_layout \begin_layout LyX-Code default format = "%d(%F %T) %V [%p:%F:%L] %m%n" \end_layout \end_deeper \begin_layout Itemize 用户自定义输出详见 \begin_inset CommandInset ref LatexCommand ref reference "sec:用户自定义输出" \end_inset \end_layout \begin_layout Section 文件转档 \begin_inset CommandInset label LatexCommand label name "sec:文件转档" \end_inset \end_layout \begin_layout Standard 为什么需要将日志文件转档?我已经在实际的运行环境中不止一次的看到过,因为日志文件过大,导致系统硬盘被撑爆,或者单个日志文件过大而即使用grep也要花费很多时间来 寻找匹配的日志。对于日志转档,我总结了如下几种范式: \end_layout \begin_layout Enumerate 按固定时间段来切分日志。 \end_layout \begin_deeper \begin_layout Standard 例如,每天生成一个日志 \end_layout \begin_layout LyX-Code aa.2012-08-02.log \end_layout \begin_layout LyX-Code aa.2012-08-03.log \end_layout \begin_layout LyX-Code aa.2012-08-04.log \end_layout \begin_layout Standard 这种日志适合的场景是,管理员大概知道每天生成的日志量,然后希望在n个月之后能精确的找出某天的所有日志。这种日志切分最好由日志库来完成,其次的方法是用cronos plit这种软件来分析日志内容的时间字符串来进行后期的切分,较差的办法是用crontab+logrotate或mv来定期移动(但这并不精确,会造成若干条当天的日 志被放到上一天的文件里面去)。 \end_layout \begin_layout Standard 在zlog里面,这种需求不需要用日志转档功能来完成,简单的在日志文件名里面设置时间日期字符串就能解决问题: \end_layout \begin_layout LyX-Code *.* "aa.%d(%F).log" \end_layout \begin_layout Standard 或者用cronolog来完成,速度会更快一点 \end_layout \begin_layout LyX-Code *.* | cronolog aa.%F.log \end_layout \end_deeper \begin_layout Enumerate 按照日志大小切分 \end_layout \begin_deeper \begin_layout Standard 多用于开发环境,适合的场景是,程序在短时间内生成大量的日志,而用编辑器vi,ue等能快速打开的日志大小是有限的,或者大的日志打开来极慢。同样的,这种日志的切分可 以在事后用split等工具来完成,但对于开发而言会增加步骤,所以最好也是由日志库来完成。值得一提的是存档有两种模式,nlog里面称之为Sequence和Roll ing,在Sequence情况下 \end_layout \begin_layout LyX-Code aa.log (new) \end_layout \begin_layout LyX-Code aa.log.2 (less new) \end_layout \begin_layout LyX-Code aa.log.1 \end_layout \begin_layout LyX-Code aa.log.0 (old) \end_layout \begin_layout Standard 而在Rolling的情况下 \end_layout \begin_layout LyX-Code aa.log (new) \end_layout \begin_layout LyX-Code aa.log.0 (less new) \end_layout \begin_layout LyX-Code aa.log.1 \end_layout \begin_layout LyX-Code aa.log.2 (old) \end_layout \begin_layout Standard 很难说哪种更加符合人的直觉。 \end_layout \begin_layout Standard 如果只有若干个最新的文件是有意义的,需要日志库来做主动的删除旧的工作。由外部程序是很难判定哪些日志是旧的。 \end_layout \begin_layout Standard 最简单的zlog的转档配置为 \end_layout \begin_layout LyX-Code *.* "aa.log", 10MB \end_layout \begin_layout Standard 这个配置是Rolling的情况,每次aa.log超过10MB的时候,会做这样的重命名 \end_layout \begin_layout LyX-Code aa.log.2 -> aa.log.3 \end_layout \begin_layout LyX-Code aa.log.1 -> aa.log.2 \end_layout \begin_layout LyX-Code aa.log.0 -> aa.log.1 \end_layout \begin_layout LyX-Code aa.log -> aa.log.0 \end_layout \begin_layout Standard 上面的配置可以写的更加罗嗦一点 \end_layout \begin_layout LyX-Code *.* "aa.log", 10MB * 0 ~ "aa.log.#r" \end_layout \begin_layout Standard 逗号后第一个参数表示文件达到多大后开始进行转档。 \end_layout \begin_layout Standard 第二个参数表示保留多少个存档文件(0代表不删除任何存档文件)。 \end_layout \begin_layout Standard 第三个参数表示转档的文件名,其中#r表示存档文件的序号,r是rolling的缩写。还可以放#s,是sequence的缩写。转档文件名必须包含#r或者#s。 \end_layout \end_deeper \begin_layout Enumerate 按照日志大小切分,但同时加上时间标签 \end_layout \begin_deeper \begin_layout LyX-Code aa.log \end_layout \begin_layout LyX-Code aa.log-20070305.00.log \end_layout \begin_layout LyX-Code aa.log-20070501.00.log \end_layout \begin_layout LyX-Code aa.log-20070501.01.log \end_layout \begin_layout LyX-Code aa.log-20071008.00.log \end_layout \begin_layout Standard 这种情况适合于程序本身的日志一般不是很受关注,但是又在某一天想要找出来看的情况。当然,在这种情况下,万一在20070501这一天日志的量超过了指定值,例如100 MB,就又要退回到第二种状态,在文件名中加后缀。 \end_layout \begin_layout Standard zlog对应的配置是 \end_layout \begin_layout LyX-Code *.* "aa.log", 100MB ~ "aa-%d(%Y%m%d).#2s.log" \end_layout \begin_layout Standard 每到100MB的时候转档,转档文件名也支持转换字符,可以把转档当时的时间串作为转档文件名的一部分。#2s的意思是序号的长度最少为2位,从00开始编号,Seque nce转档。这是zlog对转档最复杂的支持了! \end_layout \end_deeper \begin_layout Enumerate 压缩、移动、删除旧的日志 \end_layout \begin_deeper \begin_layout Standard 首先,压缩不应该由日志库来完成,因为压缩消耗时间和CPU。日志库的任务是配合压缩。 \end_layout \begin_layout Standard 对于第一种和第三种,管理较为简单,只要符合某些文件名规则或修改日期的,可以用shell脚本+crontab轻易的压缩、移动和删除。 \end_layout \begin_layout Standard 对于第二种,其实不是非常需要压缩,只需要删除就可以了。 \end_layout \begin_layout Standard 如果一定需要转档的同时进行压缩,只有logrotate能干这活儿,毕竟他是独立的程序,能在转档同时搞压缩,不会有混淆的问题。 \end_layout \end_deeper \begin_layout Enumerate zlog对外部转档工具,例如logrotate的支持 \end_layout \begin_deeper \begin_layout Standard zlog的转档功能已经极为强大,当然也有几种情况是zlog无法处理的,例如按时间条件进行转档,转档前后调用一些自制的shell脚本……这会把zlog的配置和表达 弄得过于复杂而缺乏美感。 \end_layout \begin_layout Standard 这时候你也许喜欢用一些外部转档工具,例如logrotate来完成工作。问题是,在linux操作系统下,转档工具重命名日志文件名后,应用进程还是往原来的文件描述符 写日志,没办法重新打开日志文件写新的日志。标准的做法是给应用程序一个信号,让他重新打开日志文件,对于syslogd是 \end_layout \begin_layout LyX-Code kill -SIGHUP `cat /var/run/syslogd.pid` \end_layout \begin_layout Standard 对于zlog,因为是个函数库,不适合接受信号。zlog提供了函数接口zlog_reload(),这个函数会重载配置文件,重新打开所有的日志文件。应用程序在log rotate的信号,或者其他途径,例如客户端的命令后,可以调用这个函数,来重新打开所有的日志文件。 \end_layout \end_deeper \begin_layout Section 配置文件工具 \end_layout \begin_layout LyX-Code $ zlog-chk-conf -h \end_layout \begin_layout LyX-Code Useage: zlog-chk-conf [conf files]... \end_layout \begin_layout LyX-Code -q, suppress non-error message \end_layout \begin_layout LyX-Code -h, show help message \end_layout \begin_layout Standard zlog-chk-conf 尝试读取配置文件,检查语法,然后往屏幕上输出这些配置文件是否正确。我建议每次创建或者改动一个配置文件之后都用一下这个工具。输出可能是 这样: \end_layout \begin_layout LyX-Code $ ./zlog-chk-conf zlog.conf \end_layout \begin_layout LyX-Code 03-08 15:35:44 ERROR (10595:rule.c:391) sscanf [aaa] fail, category or level is null \end_layout \begin_layout LyX-Code 03-08 15:35:44 ERROR (10595:conf.c:155) zlog_rule_new fail [aaa] \end_layout \begin_layout LyX-Code 03-08 15:35:44 ERROR (10595:conf.c:258) parse configure file[zlog.conf] line[126] fail \end_layout \begin_layout LyX-Code 03-08 15:35:44 ERROR (10595:conf.c:306) zlog_conf_read_config fail \end_layout \begin_layout LyX-Code 03-08 15:35:44 ERROR (10595:conf.c:366) zlog_conf_build fail \end_layout \begin_layout LyX-Code 03-08 15:35:44 ERROR (10595:zlog.c:66) conf_file[zlog.conf], init conf fail \end_layout \begin_layout LyX-Code 03-08 15:35:44 ERROR (10595:zlog.c:131) zlog_init_inner[zlog.conf] fail \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code ---[zlog.conf] syntax error, see error message above \end_layout \begin_layout Standard 这个告诉你配置文件zlog.conf的126行,是错的。第一行进一步告诉你[aaa]不是一条正确的规则。 \end_layout \begin_layout Standard zlog-chk-conf可以同时分析多个配置文件,举例: \end_layout \begin_layout LyX-Code $ zlog-chk-conf zlog.conf ylog.conf \end_layout \begin_layout LyX-Code --[zlog.conf] syntax right \end_layout \begin_layout LyX-Code --[ylog.conf] syntax right \end_layout \begin_layout LyX-Code \end_layout \begin_layout Chapter zlog接口(API) \end_layout \begin_layout Standard zlog的所有函数都是线程安全的,使用的时候只需要 \end_layout \begin_layout LyX-Code #include "zlog.h" \end_layout \begin_layout Section 初始化和清理 \end_layout \begin_layout Labeling \labelwidthstring 00.00.0000 总览 \end_layout \begin_deeper \begin_layout LyX-Code int zlog_init(const char * \bar under confpath \bar default ); \end_layout \begin_layout LyX-Code int zlog_reload(const char * \bar under confpath \bar default ); \end_layout \begin_layout LyX-Code void zlog_fini(void); \end_layout \end_deeper \begin_layout Labeling \labelwidthstring 00.00.0000 描述 \end_layout \begin_deeper \begin_layout Standard zlog_init()从配置文件 \bar under confpath \bar default 中读取配置信息到内存。如果 \bar under confpath \bar default 为NULL,会寻找环境变量ZLOG_CONF_PATH的值作为配置文件名。如果环境变量ZLOG_CONF_PATH也没有,所有日志以内置格式写到标准输出上。每个 进程只有第一次调用zlog_init()是有效的,后面的多余调用都会失败并不做任何事情。 \end_layout \begin_layout Standard zlog_reload()从 \bar under confpath \bar default 重载配置,并根据这个配置文件来重计算内部的分类规则匹配、重建每个线程的缓存、并设置原有的用户自定义输出函数。可以在配置文件发生改变后调用这个函数。这个函数使用次 数不限。如果 \bar under confpath \bar default 为NULL,会重载上一次zlog_init()或者zlog_reload()使用的配置文件。如果zlog_reload()失败,上一次的配置依然有效。所以zlo g_reload()具有原子性。 \end_layout \begin_layout Standard zlog_fini()清理所有zlog API申请的内存,关闭它们打开的文件。使用次数不限。 \end_layout \end_deeper \begin_layout Labeling \labelwidthstring 00.00.0000 返回值 \end_layout \begin_deeper \begin_layout Standard 如果成功,zlog_init()和zlog_reload()返回0。失败的话,zlog_init()和zlog_reload()返回-1。详细错误会被写在由环境 变量ZLOG_PROFILE_ERROR指定的错误日志里面。 \end_layout \end_deeper \begin_layout Section 分类(Category)操作 \end_layout \begin_layout Labeling \labelwidthstring 00.00.0000 总览 \end_layout \begin_deeper \begin_layout LyX-Code typedef struct zlog_category_s zlog_category_t; \end_layout \begin_layout LyX-Code zlog_category_t *zlog_get_category(const char * \bar under cname \bar default ); \end_layout \end_deeper \begin_layout Labeling \labelwidthstring 00.00.0000 描述 \end_layout \begin_deeper \begin_layout Standard zlog_get_category()从zlog的全局分类表里面找到分类,用于以后输出日志。如果没有的话,就建一个。然后它会遍历所有的规则,寻找和 \bar under cname \bar default 匹配的规则并绑定。 \end_layout \begin_layout Standard 配置文件规则中的分类名匹配 \bar under cname \bar default 的规律描述如下: \end_layout \begin_layout Enumerate * 匹配任意 \bar under cname \bar default 。 \end_layout \begin_layout Enumerate 以下划线_结尾的分类名同时匹配本级分类和下级分类。例如aa_匹配aa, aa_, aa_bb, aa_bb_cc这几个 \bar under cname \bar default 。 \end_layout \begin_layout Enumerate 不以下划线_结尾的分类名精确匹配 \bar under cname \bar default 。例如aa_bb匹配aa_bb这个 \bar under cname \bar default 。 \end_layout \begin_layout Enumerate ! 匹配目前还没有规则的 \bar under cname \bar default 。 \end_layout \begin_layout Standard 每个zlog_category_t *对应的规则,在zlog_reload()的时候会被自动重新计算。不用担心内存释放,zlog_fini() 最后会清理一切。 \end_layout \end_deeper \begin_layout Labeling \labelwidthstring 00.00.0000 返回值 \end_layout \begin_deeper \begin_layout Standard 如果成功,返回zlog_category_t的指针。如果失败,返回NULL。详细错误会被写在由环境变量ZLOG_PROFILE_ERROR指定的错误日志里面。 \end_layout \end_deeper \begin_layout Section 写日志函数及宏 \end_layout \begin_layout Labeling \labelwidthstring 00.00.0000 总览 \end_layout \begin_deeper \begin_layout LyX-Code void zlog(zlog_category_t * \bar under category \bar default , \end_layout \begin_layout LyX-Code const char * \bar under file \bar default , size_t \bar under filelen \bar default , \end_layout \begin_layout LyX-Code const char * \bar under func \bar default , size_t \bar under funclen \bar default , \end_layout \begin_layout LyX-Code long \bar under line \bar default , int \bar under level \bar default , \end_layout \begin_layout LyX-Code const char * \bar under format \bar default , ...); \end_layout \begin_layout LyX-Code void vzlog(zlog_category_t * \bar under category \bar default , \end_layout \begin_layout LyX-Code const char * \bar under file \bar default , size_t \bar under filelen \bar default , \end_layout \begin_layout LyX-Code const char * \bar under func \bar default , size_t \bar under funclen \bar default , \end_layout \begin_layout LyX-Code long \bar under line \bar default , int \bar under level \bar default , \end_layout \begin_layout LyX-Code const char * \bar under format \bar default , va_list \bar under args \bar default ); \end_layout \begin_layout LyX-Code void hzlog(zlog_category_t * \bar under category \bar default , \end_layout \begin_layout LyX-Code const char * \bar under file \bar default , size_t \bar under filelen \bar default , \end_layout \begin_layout LyX-Code const char * \bar under func \bar default , size_t \bar under funclen \bar default , \end_layout \begin_layout LyX-Code long \bar under line \bar default , int \bar under level \bar default , \end_layout \begin_layout LyX-Code const void * \bar under buf \bar default , size_t \bar under buflen \bar default ); \end_layout \begin_layout LyX-Code \end_layout \end_deeper \begin_layout Labeling \labelwidthstring 00.00.0000 描述 \end_layout \begin_deeper \begin_layout Standard 这3个函数是实际写日志的函数,输入的数据对应于配置文件中的%m。 \bar under category \bar default 来自于调用zlog_get_category()。 \end_layout \begin_layout Standard zlog()和vzlog()根据format输出,就像printf(3)和vprintf(3)。 \end_layout \begin_layout Standard vzlog()相当于zlog(),只是它用一个va_list类型的参数args,而不是一堆类型不同的参数。vzlog() 内部使用了 va_copy 宏,args的内容在vzlog()后保持不变,可以参考stdarg(3)。 \end_layout \begin_layout Standard hzlog()有点不一样,它产生下面这样的输出,长度为 \bar under buf_len \bar default 的内存 \bar under buf \bar default 以16进制的形式表示出来 \emph on 。 \end_layout \begin_layout LyX-Code hex_buf_len=[5365] \end_layout \begin_layout LyX-Code 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDE F \end_layout \begin_layout LyX-Code 0000000001 23 21 20 2f 62 69 6e 2f 62 61 73 68 0a 0a 23 20 #! /bin/bash..# \end_layout \begin_layout LyX-Code 0000000002 74 65 73 74 5f 68 65 78 20 2d 20 74 65 6d 70 6f test_hex - tempo \end_layout \begin_layout LyX-Code 0000000003 72 61 72 79 20 77 72 61 70 70 65 72 20 73 63 72 rary wrapper scr \end_layout \begin_layout Standard 参数 \bar under file \bar default 和 \bar under line \bar default 填写为__FILE__和__LINE__这两个宏。这两个宏标识日志是在哪里发生的。参数 \bar under func \bar default 填写为__func__或者__FUNCTION__,如果编译器支持的话,如果不支持,就填写为""。 \end_layout \begin_layout Standard \bar under level \bar default 是一个整数,应该是在下面几个里面取值。 \end_layout \begin_layout LyX-Code typedef enum { \end_layout \begin_deeper \begin_layout LyX-Code ZLOG_LEVEL_DEBUG = 20, \end_layout \begin_layout LyX-Code ZLOG_LEVEL_INFO = 40, \end_layout \begin_layout LyX-Code ZLOG_LEVEL_NOTICE = 60, \end_layout \begin_layout LyX-Code ZLOG_LEVEL_WARN = 80, \end_layout \begin_layout LyX-Code ZLOG_LEVEL_ERROR = 100, \end_layout \begin_layout LyX-Code ZLOG_LEVEL_FATAL = 120 \end_layout \end_deeper \begin_layout LyX-Code } zlog_level; \end_layout \begin_layout Standard 每个函数都有对应的宏,简单使用。例如: \end_layout \begin_layout LyX-Code #define zlog_fatal(cat, format, args...) \backslash \end_layout \begin_layout LyX-Code zlog(cat, __FILE__, sizeof(__FILE__)-1, \backslash \end_layout \begin_layout LyX-Code __func__, sizeof(__func__)-1, __LINE__, \backslash \end_layout \begin_layout LyX-Code ZLOG_LEVEL_FATAL, format, ##args) \end_layout \begin_layout Standard 所有的宏列表: \end_layout \begin_layout LyX-Code /* zlog macros */ \end_layout \begin_layout LyX-Code zlog_fatal(cat, format, ...) \end_layout \begin_layout LyX-Code zlog_error(cat, format, ...) \end_layout \begin_layout LyX-Code zlog_warn(cat, format, ...) \end_layout \begin_layout LyX-Code zlog_notice(cat, format, ...) \end_layout \begin_layout LyX-Code zlog_info(cat, format, ...) \end_layout \begin_layout LyX-Code zlog_debug(cat, format, ...) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code /* vzlog macros */ \end_layout \begin_layout LyX-Code vzlog_fatal(cat, format, args) \end_layout \begin_layout LyX-Code vzlog_error(cat, format, args) \end_layout \begin_layout LyX-Code vzlog_warn(cat, format, args) \end_layout \begin_layout LyX-Code vzlog_notice(cat, format, args) \end_layout \begin_layout LyX-Code vzlog_info(cat, format, args) \end_layout \begin_layout LyX-Code vzlog_debug(cat, format, args) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code /* hzlog macros */ \end_layout \begin_layout LyX-Code hzlog_fatal(cat, buf, buf_len) \end_layout \begin_layout LyX-Code hzlog_error(cat, buf, buf_len) \end_layout \begin_layout LyX-Code hzlog_warn(cat, buf, buf_len) \end_layout \begin_layout LyX-Code hzlog_notice(cat, buf, buf_len) \end_layout \begin_layout LyX-Code hzlog_info(cat, buf, buf_len) \end_layout \begin_layout LyX-Code hzlog_debug(cat, buf, buf_len) \end_layout \end_deeper \begin_layout Labeling \labelwidthstring 00.00.0000 返回值 \end_layout \begin_deeper \begin_layout Standard 这些函数不返回。如果有错误发生,详细错误会被写在由环境变量ZLOG_PROFILE_ERROR指定的错误日志里面。 \end_layout \end_deeper \begin_layout Section MDC操作 \end_layout \begin_layout Labeling \labelwidthstring 00.00.0000 总览 \end_layout \begin_deeper \begin_layout LyX-Code int zlog_put_mdc(const char * \bar under key \bar default , const char * \bar under value \bar default ); \end_layout \begin_layout LyX-Code char *zlog_get_mdc(const char * \bar under key \bar default ); \end_layout \begin_layout LyX-Code void zlog_remove_mdc(const char * \bar under key \bar default ); \end_layout \begin_layout LyX-Code void zlog_clean_mdc(void); \end_layout \end_deeper \begin_layout Labeling \labelwidthstring 00.00.0000 描述 \end_layout \begin_deeper \begin_layout Standard MDC(Mapped Diagnostic Context)是一个每线程拥有的键-值表,所以和分类没什么关系。 \end_layout \begin_layout Standard \bar under key \bar default 和 \bar under value \bar default 是字符串,长度不能超过MAXLEN_PATH(1024)。如果超过MAXLEN_PATH(1024)的话,会被截断。 \end_layout \begin_layout Standard 记住这个表是和线程绑定的,每个线程有自己的表,所以在一个线程内的调用不会影响其他线程。 \end_layout \end_deeper \begin_layout Labeling \labelwidthstring 00.00.0000 返回值 \end_layout \begin_deeper \begin_layout Standard zlog_put_mdc()成功返回0,失败返回-1。zlog_get_mdc()成功返回value的指针,失败或者没有相应的key返回NULL。如果有错误发生 ,详细错误会被写在由环境变量ZLOG_PROFILE_ERROR指定的错误日志里面。 \end_layout \end_deeper \begin_layout Section dzlog接口 \begin_inset CommandInset label LatexCommand label name "sec:dzlog接口" \end_inset \end_layout \begin_layout Labeling \labelwidthstring 00.00.0000 总览 \end_layout \begin_deeper \begin_layout LyX-Code int dzlog_init(const char * \bar under confpath \bar default , const char * \bar under cname \bar default ); \end_layout \begin_layout LyX-Code int dzlog_set_category(const char * \bar under cname \bar default ); \end_layout \begin_layout LyX-Code void dzlog(const char * \bar under file \bar default , size_t \bar under filelen \bar default , \end_layout \begin_layout LyX-Code const char * \bar under func \bar default , size_t \bar under funclen \bar default , \end_layout \begin_layout LyX-Code long \bar under line \bar default , int \bar under level \bar default , \end_layout \begin_layout LyX-Code const char * \bar under format \bar default , ...); \end_layout \begin_layout LyX-Code void vdzlog(const char * \bar under file \bar default , size_t \bar under filelen \bar default , \end_layout \begin_layout LyX-Code const char * \bar under func \bar default , size_t \bar under funclen \bar default , \end_layout \begin_layout LyX-Code long \bar under line \bar default , int \bar under level \bar default , \end_layout \begin_layout LyX-Code const char * \bar under format \bar default , va_list \bar under args \bar default ); \end_layout \begin_layout LyX-Code void hdzlog(const char * \bar under file \bar default , size_t \bar under filelen \bar default , \end_layout \begin_layout LyX-Code const char * \bar under func \bar default , size_t \bar under funclen \bar default , \end_layout \begin_layout LyX-Code long \bar under line \bar default , int \bar under level \bar default , \end_layout \begin_layout LyX-Code const void * \bar under buf \bar default , size_t \bar under buflen \bar default ); \end_layout \end_deeper \begin_layout Labeling \labelwidthstring 00.00.0000 描述 \end_layout \begin_deeper \begin_layout Standard dzlog是忽略分类(zlog_category_t)的一组简单zlog接口。它采用内置的一个默认分类,这个分类置于锁的保护下。这些接口也是线程安全的。忽略了分 类,意味着用户不需要操心创建、存储、传输zlog_category_t类型的变量 \emph on \noun on 。当然也可以在用 \emph default \noun default dzlog接口的同时用一般的zlog接口函数,这样会更爽。 \end_layout \begin_layout Standard dzlog_init()和zlog_init()一样做初始化,就是多需要一个默认分类名 \bar under cname \bar default 的参数。zlog_reload()、 zlog_fini() 可以和以前一样使用,用来刷新配置,或者清理。 \end_layout \begin_layout Standard dzlog_set_category()是用来改变默认分类用的。上一个分类会被替换成新的。同样不用担心内存释放的问题,zlog_fini()最后会清理。 \end_layout \begin_layout Standard dzlog的宏也定义在zlog.h里面。更简单的写法。 \end_layout \begin_layout LyX-Code dzlog_fatal(format, ...) \end_layout \begin_layout LyX-Code dzlog_error(format, ...) \end_layout \begin_layout LyX-Code dzlog_warn(format, ...) \end_layout \begin_layout LyX-Code dzlog_notice(format, ...) \end_layout \begin_layout LyX-Code dzlog_info(format, ...) \end_layout \begin_layout LyX-Code dezlog_debug(format, ...) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code vdzlog_fatal(format, args) \end_layout \begin_layout LyX-Code vdzlog_error(format, args) \end_layout \begin_layout LyX-Code vdzlog_warn(format, args) \end_layout \begin_layout LyX-Code vdzlog_notice(format, args) \end_layout \begin_layout LyX-Code vdzlog_info(format, args) \end_layout \begin_layout LyX-Code vdzlog_debug(format, args) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code hdzlog_fatal(buf, buf_len) \end_layout \begin_layout LyX-Code hdzlog_error(buf, buf_len) \end_layout \begin_layout LyX-Code hdzlog_warn(buf, buf_len) \end_layout \begin_layout LyX-Code hdzlog_noticebuf, buf_len) \end_layout \begin_layout LyX-Code hdzlog_info(buf, buf_len) \end_layout \begin_layout LyX-Code hdzlog_debug(buf, buf_len) \end_layout \end_deeper \begin_layout Labeling \labelwidthstring 00.00.0000 返回值 \end_layout \begin_deeper \begin_layout Standard 成功情况下dzlog_init()和dzlog_set_category()返回0。失败情况下dzlog_init()和 dzlog_set_category( )返回-1。详细错误会被写在由环境变量ZLOG_PROFILE_ERROR指定的错误日志里面。 \end_layout \end_deeper \begin_layout Section 用户自定义输出 \end_layout \begin_layout Labeling \labelwidthstring 00.00.0000 总览 \end_layout \begin_deeper \begin_layout LyX-Code typedef struct zlog_msg_s { \end_layout \begin_layout LyX-Code char *buf; \end_layout \begin_layout LyX-Code size_t len; \end_layout \begin_layout LyX-Code char *path; \end_layout \begin_layout LyX-Code } zlog_msg_t; \end_layout \begin_layout LyX-Code typedef int (*zlog_record_fn)(zlog_msg_t * \bar under msg \bar default ); \end_layout \begin_layout LyX-Code int zlog_set_record(const char * \bar under rname \bar default , zlog_record_fn \bar under record \bar default ); \end_layout \end_deeper \begin_layout Labeling \labelwidthstring 00.00.0000 描述 \end_layout \begin_deeper \begin_layout Standard zlog允许用户自定义输出函数。输出函数需要绑定到某条特殊的规则上。这种规则的例子是: \end_layout \begin_layout LyX-Code *.* $name, "record path %c %d"; simple \end_layout \begin_layout Standard zlog_set_record()做绑定动作。规则中输出段有$ \bar under name \bar default 的,会被用来做用户自定义输出。输出函数为 \bar under record \bar default 。这个函数需要为zlog_record_fn的格式。 \end_layout \begin_layout Standard zlog_msg_t结构的各个成员描述如下: \end_layout \begin_layout Standard \bar under path \bar default 来自规则的逗号后的字符串,这个字符串会被动态的解析,输出当前的 \bar under path \bar default ,就像动态文件路径一样。 \end_layout \begin_layout Standard \bar under buf \bar default 和 \bar under len \bar default 是zlog格式化后的日志信息和长度。 \end_layout \begin_layout Standard 所有zlog_set_record()做的绑定在zlog_reload()使用后继续有效。 \end_layout \end_deeper \begin_layout Labeling \labelwidthstring 00.00.0000 返回值 \end_layout \begin_deeper \begin_layout Standard 成功情况下zlog_set_record()返回0。失败情况下zlog_set_record()返回-1。详细错误会被写在由环境变量ZLOG_PROFILE_E RROR指定的错误日志里面。 \end_layout \end_deeper \begin_layout Section 调试和诊断 \end_layout \begin_layout Labeling \labelwidthstring 00.00.0000 总览 \end_layout \begin_deeper \begin_layout LyX-Code void zlog_profile(void); \end_layout \end_deeper \begin_layout Labeling \labelwidthstring 00.00.0000 描述 \end_layout \begin_deeper \begin_layout Standard 环境变量ZLOG_PROFILE_ERROR指定zlog本身的错误日志。 \end_layout \begin_layout Standard 环境变量ZLOG_PROFILE_DEBUG指定zlog本身的调试日志。 \end_layout \begin_layout Standard zlog_profile()打印所有内存中的配置信息到ZLOG_PROFILE_ERROR,在运行时。可以把这个和配置文件比较,看看有没有问题。 \end_layout \end_deeper \begin_layout Chapter 高阶使用 \end_layout \begin_layout Section MDC \begin_inset CommandInset label LatexCommand label name "sec:MDC" \end_inset \end_layout \begin_layout Standard MDC是什么?在log4j里面解释为Mapped Diagnostic Context。听起来是个很复杂的技术,其实MDC就是一个键-值对表。一旦某次你设置了, 后面库可以帮你自动打印出来,或者成为文件名的一部分。让我们看一个例子,来自于$(top_builddir)/test/test_mdc.c. \end_layout \begin_layout LyX-Code $ cat test_mdc.c \end_layout \begin_layout LyX-Code #include \end_layout \begin_layout LyX-Code #include \end_layout \begin_layout LyX-Code #include \end_layout \begin_layout LyX-Code #include \end_layout \begin_layout LyX-Code #include \end_layout \begin_layout LyX-Code #include "zlog.h" \end_layout \begin_layout LyX-Code int main(int argc, char** argv) \end_layout \begin_layout LyX-Code { \end_layout \begin_deeper \begin_layout LyX-Code int rc; \end_layout \begin_layout LyX-Code zlog_category_t *zc; \end_layout \begin_layout LyX-Code rc = zlog_init("test_mdc.conf"); \end_layout \begin_deeper \begin_layout LyX-Code if (rc) { \end_layout \begin_layout LyX-Code printf("init failed \backslash n"); \end_layout \begin_layout LyX-Code return -1; \end_layout \end_deeper \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code zc = zlog_get_category("my_cat"); \end_layout \begin_layout LyX-Code if (!zc) { \end_layout \begin_deeper \begin_layout LyX-Code printf("get cat fail \backslash n"); \end_layout \begin_layout LyX-Code zlog_fini(); \end_layout \begin_layout LyX-Code return -2; \end_layout \end_deeper \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code zlog_info(zc, "1.hello, zlog"); \end_layout \begin_layout LyX-Code zlog_put_mdc("myname", "Zhang"); \end_layout \begin_layout LyX-Code zlog_info(zc, "2.hello, zlog"); \end_layout \begin_layout LyX-Code zlog_put_mdc("myname", "Li"); \end_layout \begin_layout LyX-Code zlog_info(zc, "3.hello, zlog"); \end_layout \begin_layout LyX-Code zlog_fini(); \end_layout \begin_layout LyX-Code return 0; \end_layout \end_deeper \begin_layout LyX-Code } \end_layout \begin_layout Standard 配置文件 \end_layout \begin_layout LyX-Code $ cat test_mdc.conf \end_layout \begin_layout LyX-Code [formats] \end_layout \begin_layout LyX-Code mdc_format= "%d(%F %X.%ms) %-6V (%c:%F:%L) [%M(myname)] - %m%n" \end_layout \begin_layout LyX-Code [rules] \end_layout \begin_layout LyX-Code *.* >stdout; mdc_format \end_layout \begin_layout Standard 输出 \end_layout \begin_layout LyX-Code $ ./test_mdc \end_layout \begin_layout LyX-Code 2012-03-12 09:26:37.740 INFO (my_cat:test_mdc.c:47) [] - 1.hello, zlog \end_layout \begin_layout LyX-Code 2012-03-12 09:26:37.740 INFO (my_cat:test_mdc.c:51) [Zhang] - 2.hello, zlog \end_layout \begin_layout LyX-Code 2012-03-12 09:26:37.740 INFO (my_cat:test_mdc.c:55) [Li] - 3.hello, zlog \end_layout \begin_layout Standard 你可以看到zlog_put_mdc()在表里面设置键"myname"对应值"Zhang",然后在配置文件里面%M(myname)指出了在日志的哪个位置需要把值打 出来。第二次,键"myname"的值被覆盖写成"Li",然后日志里面也有相应的变化。 \end_layout \begin_layout Standard MDC什么时候有用呢?往往在用户需要在同样的日志行为区分不同的业务数据的时候。比如说,c源代码是 \end_layout \begin_layout LyX-Code zlog_put_mdc("customer_name", get_customer_name_from_db() ); \end_layout \begin_layout LyX-Code zlog_info("get in"); \end_layout \begin_layout LyX-Code zlog_info("pick product"); \end_layout \begin_layout LyX-Code zlog_info("pay"); \end_layout \begin_layout LyX-Code zlog_info("get out"); \end_layout \begin_layout Standard 在配置文件里面是 \end_layout \begin_layout LyX-Code &format "%M(customer_name) %m%n" \end_layout \begin_layout Standard 当程序同时处理两个用户的时候,打出来的日志可能是 \end_layout \begin_layout LyX-Code Zhang get in \end_layout \begin_layout LyX-Code Li get in \end_layout \begin_layout LyX-Code Zhang pick product \end_layout \begin_layout LyX-Code Zhang pay \end_layout \begin_layout LyX-Code Li pick product \end_layout \begin_layout LyX-Code Li pay \end_layout \begin_layout LyX-Code Zhang get out \end_layout \begin_layout LyX-Code Li get out \end_layout \begin_layout Standard 这样,你就可以用grep命令把这两个用户的日志分开来了 \end_layout \begin_layout LyX-Code $ grep Zhang aa.log > Zhang.log \end_layout \begin_layout LyX-Code $ grep Li aa.log >Li.log \end_layout \begin_layout Standard 或者,还有另外一条路,一开始在文件名里面做区分,看配置文件: \end_layout \begin_layout LyX-Code *.* "mdc_%M(customer_name).log"; \end_layout \begin_layout Standard 这就会产生3个日志文件。 \end_layout \begin_layout LyX-Code mdc_.log mdc_Zhang.log mdc_Li.log \end_layout \begin_layout Standard 这是一条近路,如果用户知道自己在干什么。 \end_layout \begin_layout Standard MDC是每个线程独有的,所以可以把一些线程专有的变量设置进去。如果单单为了区分线程,可以用转换字符里面的%t来搞定。 \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout Section 诊断zlog本身 \begin_inset CommandInset label LatexCommand label name "sec:诊断zlog本身" \end_inset \end_layout \begin_layout Standard OK,至今为止,我假定zlog库本身是不出毛病的。zlog帮助用户程序写日志,帮助程序员debug程序。但是如果zlog内部出错了呢?怎么知道错在哪里呢?其他的 程序可以用日志库来debug,但日志库自己怎么debug?答案很简单,zlog有自己的日志——诊断日志。这个日志通常是关闭的,可以通过环境变量来打开。 \end_layout \begin_layout LyX-Code $ export ZLOG_PROFILE_DEBUG=/tmp/zlog.debug.log \end_layout \begin_layout LyX-Code $ export ZLOG_PROFILE_ERROR=/tmp/zlog.error.log \end_layout \begin_layout Standard 诊断日志只有两个级别debug和error。设置好环境变量后. 再跑test_hello程序 \begin_inset CommandInset ref LatexCommand ref reference "sec:Hello-World-代码" \end_inset ,然后debug日志为 \end_layout \begin_layout LyX-Code $ more zlog.debug.log \end_layout \begin_layout LyX-Code 03-13 09:46:56 DEBUG (7503:zlog.c:115) ------zlog_init start, compile time[Mar 13 2012 11:28:56]------ \end_layout \begin_layout LyX-Code 03-13 09:46:56 DEBUG (7503:spec.c:825) spec:[0x7fdf96b7c010][%d(%F %T)][%F %T 29][] \end_layout \begin_layout LyX-Code 03-13 09:46:56 DEBUG (7503:spec.c:825) spec:[0x7fdf96b52010][ ][ 0][] \end_layout \begin_layout LyX-Code ...... \end_layout \begin_layout LyX-Code 03-13 09:52:40 DEBUG (8139:zlog.c:291) ------zlog_fini end------ \end_layout \begin_layout Standard zlog.error.log日志没产生,因为没有错误发生。 \end_layout \begin_layout Standard 你可以看出来,debug日志展示了zlog是怎么初始化还有清理的。不过在zlog_info()执行的时候没有日志打出来,这是为了效率。 \end_layout \begin_layout Standard 如果zlog库有任何问题,都会打日志到ZLOG_PROFILE_ERROR所指向的错误日志。比如说,在zlog_info()上用一个错误的printf的语法: \end_layout \begin_layout LyX-Code zlog_info(zc, "%l", 1); \end_layout \begin_layout Standard 然后编译执行程序,ZLOG_PROFILE_ERROR的日志会是 \end_layout \begin_layout LyX-Code $ cat zlog.error.log \end_layout \begin_layout LyX-Code 03-13 10:04:58 ERROR (10102:buf.c:189) vsnprintf fail, errno[0] \end_layout \begin_layout LyX-Code 03-13 10:04:58 ERROR (10102:buf.c:191) nwrite[-1], size_left[1024], format[%l] \end_layout \begin_layout LyX-Code 03-13 10:04:58 ERROR (10102:spec.c:329) zlog_buf_vprintf maybe fail or overflow \end_layout \begin_layout LyX-Code 03-13 10:04:58 ERROR (10102:spec.c:467) a_spec->gen_buf fail \end_layout \begin_layout LyX-Code 03-13 10:04:58 ERROR (10102:format.c:160) zlog_spec_gen_msg fail \end_layout \begin_layout LyX-Code 03-13 10:04:58 ERROR (10102:rule.c:265) zlog_format_gen_msg fail \end_layout \begin_layout LyX-Code 03-13 10:04:58 ERROR (10102:category.c:164) hzb_log_rule_output fail \end_layout \begin_layout LyX-Code 03-13 10:04:58 ERROR (10102:zlog.c:632) zlog_output fail, srcfile[test_hello.c], srcline[41] \end_layout \begin_layout Standard 这样,用户就能知道为啥期待的输出没有产生,然后搞定这个问题。 \end_layout \begin_layout Standard 运行时诊断会带来一定的性能损失。一般来说,我在生产环境把ZLOG_PROFILE_ERROR打开,ZLOG_PROFILE_DEBUG关闭。 \end_layout \begin_layout Standard 还有另外一个办法来诊断zlog。我们都知道,zlog_init()会把配置信息读入内存。在整个写日志的过程中,这块内存保持不变。如果用户程序因为某种原因损坏了这 块内存,那么就会造成问题。还有可能是内存中的信息和配置文件的信息不匹配。所以我设计了一个函数,把内存的信息展现到ZLOG_PROFILE_ERROR指向的错误日 志。 \end_layout \begin_layout Standard 代码见$(top_builddir)/test/test_profile.c \end_layout \begin_layout LyX-Code $ cat test_profile.c \end_layout \begin_layout LyX-Code #include \end_layout \begin_layout LyX-Code #include "zlog.h" \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code int main(int argc, char** argv) \end_layout \begin_layout LyX-Code { \end_layout \begin_deeper \begin_layout LyX-Code int rc; \end_layout \begin_layout LyX-Code rc = dzlog_init("test_profile.conf", "my_cat"); \end_layout \begin_layout LyX-Code if (rc) { \end_layout \begin_deeper \begin_layout LyX-Code printf("init failed \backslash n"); \end_layout \begin_layout LyX-Code return -1; \end_layout \end_deeper \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code dzlog_info("hello, zlog"); \end_layout \begin_layout LyX-Code zlog_profile(); \end_layout \begin_layout LyX-Code zlog_fini(); \end_layout \begin_layout LyX-Code return 0; \end_layout \end_deeper \begin_layout LyX-Code } \end_layout \begin_layout Standard zlog_profile()就是这个函数。配置文件很简单。 \end_layout \begin_layout LyX-Code $ cat test_profile.conf \end_layout \begin_layout LyX-Code [formats] \end_layout \begin_layout LyX-Code simple = "%m%n" \end_layout \begin_layout LyX-Code [rules] \end_layout \begin_layout LyX-Code my_cat.* >stdout; simple \end_layout \begin_layout Standard 然后zlog.error.log会是 \end_layout \begin_layout LyX-Code $ cat /tmp/zlog.error.log \end_layout \begin_layout LyX-Code 06-01 11:21:26 WARN (7063:zlog.c:783) ------zlog_profile start------ \end_layout \begin_layout LyX-Code 06-01 11:21:26 WARN (7063:zlog.c:784) init_flag:[1] \end_layout \begin_layout LyX-Code 06-01 11:21:26 WARN (7063:conf.c:75) -conf[0x2333010]- \end_layout \begin_layout LyX-Code 06-01 11:21:26 WARN (7063:conf.c:76) --global-- \end_layout \begin_layout LyX-Code 06-01 11:21:26 WARN (7063:conf.c:77) ---file[test_profile.conf],mtime[2012-06-01 11:20:44]--- \end_layout \begin_layout LyX-Code 06-01 11:21:26 WARN (7063:conf.c:78) ---strict init[1]--- \end_layout \begin_layout LyX-Code 06-01 11:21:26 WARN (7063:conf.c:79) ---buffer min[1024]--- \end_layout \begin_layout LyX-Code 06-01 11:21:26 WARN (7063:conf.c:80) ---buffer max[2097152]--- \end_layout \begin_layout LyX-Code 06-01 11:21:26 WARN (7063:conf.c:82) ---default_format--- \end_layout \begin_layout LyX-Code 06-01 11:21:26 WARN (7063:format.c:48) ---format[0x235ef60][default = %d(%F %T) %V [%p:%F:%L] %m%n(0x233b810)]--- \end_layout \begin_layout LyX-Code 06-01 11:21:26 WARN (7063:conf.c:85) ---file perms[0600]--- \end_layout \begin_layout LyX-Code 06-01 11:21:26 WARN (7063:conf.c:87) ---rotate lock file[/tmp/zlog.lock]--- \end_layout \begin_layout LyX-Code 06-01 11:21:26 WARN (7063:rotater.c:48) --rotater[0x233b7d0][0x233b7d0,/tmp/zlog. lock,4]-- \end_layout \begin_layout LyX-Code 06-01 11:21:26 WARN (7063:level_list.c:37) --level_list[0x2335490]-- \end_layout \begin_layout LyX-Code 06-01 11:21:26 WARN (7063:level.c:37) ---level[0x23355c0][0,*,*,1,6]--- \end_layout \begin_layout LyX-Code 06-01 11:21:26 WARN (7063:level.c:37) ---level[0x23375e0][20,DEBUG,debug,5,7]--- \end_layout \begin_layout LyX-Code 06-01 11:21:26 WARN (7063:level.c:37) ---level[0x2339600][40,INFO,info,4,6]--- \end_layout \begin_layout LyX-Code 06-01 11:21:26 WARN (7063:level.c:37) ---level[0x233b830][60,NOTICE,notice,6,5]- -- \end_layout \begin_layout LyX-Code 06-01 11:21:26 WARN (7063:level.c:37) ---level[0x233d850][80,WARN,warn,4,4]--- \end_layout \begin_layout LyX-Code 06-01 11:21:26 WARN (7063:level.c:37) ---level[0x233fc80][100,ERROR,error,5,3]-- - \end_layout \begin_layout Section 用户自定义等级 \begin_inset CommandInset label LatexCommand label name "sec:用户自定义等级" \end_inset \end_layout \begin_layout Standard 这里我把用户自定义等级的几个步骤写下来。 \end_layout \begin_layout Enumerate 在配置文件中定义新的等级 \end_layout \begin_deeper \begin_layout LyX-Code $ cat $(top_builddir)/test/test_level.conf \end_layout \begin_layout LyX-Code [global] \end_layout \begin_layout LyX-Code default format = "%V %v %m%n" \end_layout \begin_layout LyX-Code [levels] \end_layout \begin_layout LyX-Code TRACE = 30, LOG_DEBUG \end_layout \begin_layout LyX-Code [rules] \end_layout \begin_layout LyX-Code my_cat.TRACE >stdout; \end_layout \begin_layout Standard 内置的默认等级是(这些不需要写在配置文件里面) \end_layout \begin_layout LyX-Code DEBUG = 20, LOG_DEBUG \end_layout \begin_layout LyX-Code INFO = 40, LOG_INFO \end_layout \begin_layout LyX-Code NOTICE = 60, LOG_NOTICE \end_layout \begin_layout LyX-Code WARN = 80, LOG_WARNING \end_layout \begin_layout LyX-Code ERROR = 100, LOG_ERR \end_layout \begin_layout LyX-Code FATAL = 120, LOG_ALERT \end_layout \begin_layout LyX-Code UNKNOWN = 254, LOG_ERR \end_layout \begin_layout Standard 这样在zlog看来,一个整数(30)还有一个等级字符串(TRACE)代表了等级。这个整数必须位于[1,253]之间,其他数字是非法的。数字越大代表越重要。现在T RACE比DEBUG重要(30>20),比INFO等级低(30<40)。在这样的定义后,TRACE就可以在下面的配置文件里面用了。例如这句话: \end_layout \begin_layout LyX-Code my_cat.TRACE >stdout; \end_layout \begin_layout Standard 意味着等级>=TRACE的,包括INFO, NOTICE, WARN, ERROR, FATAL会被写到标准输出。 \end_layout \begin_layout Standard 格式里面的转换字符%V会产生等级字符串的大写输出,%v会产生小写的等级字符串输出。 \end_layout \begin_layout Standard 另外,在等级的定义里面,LOG_DEBUG是指当需要输出到syslog的时候,自定义的TRACE等级会以LOG_DEBUG输出到syslog。 \end_layout \end_deeper \begin_layout Enumerate 在源代码里面直接用新的等级是这么搞的 \end_layout \begin_deeper \begin_layout LyX-Code zlog(cat, __FILE__, sizeof(__FILE__)-1, \backslash \end_layout \begin_layout LyX-Code __func__, sizeof(__func__)-1,__LINE__, \backslash \end_layout \begin_layout LyX-Code 30, "test %d", 1); \end_layout \begin_layout Standard 为了简单使用,创建一个.h头文件 \end_layout \begin_layout LyX-Code $ cat $(top_builddir)/test/test_level.h \end_layout \begin_layout LyX-Code #ifndef __test_level_h \end_layout \begin_layout LyX-Code #define __test_level_h \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code #include "zlog.h" \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code enum { \end_layout \begin_deeper \begin_layout LyX-Code ZLOG_LEVEL_TRACE = 30, \end_layout \begin_layout LyX-Code /* must equals conf file setting */ \end_layout \end_deeper \begin_layout LyX-Code }; \end_layout \begin_layout LyX-Code #define zlog_trace(cat, format, ...) \backslash \end_layout \begin_layout LyX-Code zlog(cat, __FILE__, sizeof(__FILE__)-1, \backslash \end_layout \begin_layout LyX-Code __func__, sizeof(__func__)-1, __LINE__, \backslash \end_layout \begin_layout LyX-Code ZLOG_LEVEL_TRACE, format, ## __VA_ARGS__) \end_layout \begin_layout LyX-Code #endif \end_layout \end_deeper \begin_layout Enumerate 这样zlog_trace就能在.c文件里面用了 \end_layout \begin_deeper \begin_layout LyX-Code $ cat $(top_builddir)/test/test_level.c \end_layout \begin_layout LyX-Code #include \end_layout \begin_layout LyX-Code #include "test_level.h" \end_layout \begin_layout LyX-Code int main(int argc, char** argv) \end_layout \begin_layout LyX-Code { \end_layout \begin_deeper \begin_layout LyX-Code int rc; \end_layout \begin_layout LyX-Code zlog_category_t *zc; \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code rc = zlog_init("test_level.conf"); \end_layout \begin_layout LyX-Code if (rc) { \end_layout \begin_deeper \begin_layout LyX-Code printf("init failed \backslash n"); \end_layout \begin_layout LyX-Code return -1; \end_layout \end_deeper \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code zc = zlog_get_category("my_cat"); \end_layout \begin_layout LyX-Code if (!zc) { \end_layout \begin_deeper \begin_layout LyX-Code printf("get cat fail \backslash n"); \end_layout \begin_layout LyX-Code zlog_fini(); \end_layout \begin_layout LyX-Code return -2; \end_layout \end_deeper \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code zlog_trace(zc, "hello, zlog - trace"); \end_layout \begin_layout LyX-Code zlog_debug(zc, "hello, zlog - debug"); \end_layout \begin_layout LyX-Code zlog_info(zc, "hello, zlog - info"); \end_layout \begin_layout LyX-Code zlog_fini(); \end_layout \begin_layout LyX-Code return 0; \end_layout \end_deeper \begin_layout LyX-Code } \end_layout \end_deeper \begin_layout Enumerate 最后我们能看到输出 \end_layout \begin_deeper \begin_layout LyX-Code $ ./test_level \end_layout \begin_layout LyX-Code TRACE trace hello, zlog - trace \end_layout \begin_layout LyX-Code INFO info hello, zlog - info \end_layout \begin_layout Standard 正是我们所期待的,配置文件只允许>=TRACE等级的日志输出到屏幕上。%V和%v也显示了正确的结果。 \end_layout \end_deeper \begin_layout Section 用户自定义输出 \begin_inset CommandInset label LatexCommand label name "sec:用户自定义输出" \end_inset \end_layout \begin_layout Standard 用户自定义输出的意义是zlog放弃一些权力。zlog只负责动态生成单条日志和文件路径,但怎么输出、转档、清理等等工作由用户按照自己的需求自行写函数完成。写完函数 只要绑定到某个规则就可以。这里我把用户自定义输出的几个步骤写下来。 \end_layout \begin_layout Enumerate 在配置文件里面定义规则 \end_layout \begin_deeper \begin_layout LyX-Code $ cat test_record.conf \end_layout \begin_layout LyX-Code [formats] \end_layout \begin_layout LyX-Code simple = "%m%n" \end_layout \begin_layout LyX-Code [rules] \end_layout \begin_layout LyX-Code my_cat.* $myoutput, " mypath %c %d";simple \end_layout \end_deeper \begin_layout Enumerate 绑定一个函数到$myoutput,并使用之 \end_layout \begin_deeper \begin_layout LyX-Code #include \end_layout \begin_layout LyX-Code #include "zlog.h" \end_layout \begin_layout LyX-Code int output(zlog_msg_t *msg) \end_layout \begin_layout LyX-Code { \end_layout \begin_deeper \begin_layout LyX-Code printf("[mystd]:[%s][%s][%ld] \backslash n", msg->path, msg->buf, (long)msg->len); \end_layout \begin_layout LyX-Code return 0; \end_layout \end_deeper \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code int main(int argc, char** argv) \end_layout \begin_layout LyX-Code { \end_layout \begin_deeper \begin_layout LyX-Code int rc; \end_layout \begin_layout LyX-Code zlog_category_t *zc; \end_layout \begin_deeper \begin_layout LyX-Code rc = zlog_init("test_record.conf"); \end_layout \begin_layout LyX-Code if (rc) { \end_layout \begin_layout LyX-Code printf("init failed \backslash n"); \end_layout \begin_layout LyX-Code return -1; \end_layout \end_deeper \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code zlog_set_record("myoutput", output); \end_layout \begin_layout LyX-Code zc = zlog_get_category("my_cat"); \end_layout \begin_layout LyX-Code if (!zc) { \end_layout \begin_deeper \begin_layout LyX-Code printf("get cat fail \backslash n"); \end_layout \begin_layout LyX-Code zlog_fini(); \end_layout \begin_layout LyX-Code return -2; \end_layout \end_deeper \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code zlog_info(zc, "hello, zlog"); \end_layout \begin_layout LyX-Code zlog_fini(); \end_layout \begin_layout LyX-Code return 0; \end_layout \end_deeper \begin_layout LyX-Code } \end_layout \end_deeper \begin_layout Enumerate 最后我们发现用户自定义输出的函数能用了! \end_layout \begin_deeper \begin_layout LyX-Code $ ./test_record \end_layout \begin_layout LyX-Code [mystd]:[ mypath my_cat 2012-07-19 12:23:08][hello, zlog \end_layout \begin_layout LyX-Code ][12] \end_layout \begin_layout Standard 正如你所见,msglen是12,zlog生成的msg在最后有一个换行符。 \end_layout \end_deeper \begin_layout Enumerate 用户自定义输出可以干很多神奇的事情,就像某个用户(flw@newsmth.net)的需求 \end_layout \begin_deeper \begin_layout Enumerate 日志文件名为 foo.log \end_layout \begin_layout Enumerate 如果 foo.log 超过 100M,则生成一个新文件,其中包含的就是 foo.log 目前的内容 而 foo.log 则变为空,重新开始增长 \end_layout \begin_layout Enumerate 如果距离上次生成文件时已经超过 5 分钟,则即使不到 100M,也应当生成一个新文件 \end_layout \begin_layout Enumerate 新文件名称可以自定义,比如加上设备名作为前缀、日期时间串作为后缀 \end_layout \begin_layout Enumerate 我希望这个新文件可以被压缩,以节省磁盘空间或者网络带宽。 \end_layout \begin_layout Standard 但愿他能顺利写出这个需求的代码!在多进程或者多线程的情况下!上帝保佑他! \end_layout \end_deeper \begin_layout Chapter 尾声 \end_layout \begin_layout Standard 好酒!所有人生问题的终极起源和终极答案。 \end_layout \begin_layout Right Address 荷马.辛普森 \end_layout \end_body \end_document zlog-1.2.18/doc/UsersGuide-EN.lyx000066400000000000000000005112131460570005000164400ustar00rootroot00000000000000#LyX 2.3 created this file. For more info see http://www.lyx.org/ \lyxformat 544 \begin_document \begin_header \save_transient_properties true \origin unavailable \textclass extbook \use_default_options true \begin_modules theorems-ams eqs-within-sections figs-within-sections \end_modules \maintain_unincluded_children false \language english \language_package default \inputencoding auto \fontencoding global \font_roman "default" "default" \font_sans "default" "default" \font_typewriter "default" "default" \font_math "auto" "auto" \font_default_family default \use_non_tex_fonts false \font_sc false \font_osf false \font_sf_scale 100 100 \font_tt_scale 100 100 \use_microtype false \use_dash_ligatures true \graphics default \default_output_format default \output_sync 0 \bibtex_command default \index_command default \paperfontsize 12 \spacing single \use_hyperref true \pdf_title "zlog Users Guide EN" \pdf_author "Hardy Simpson" \pdf_bookmarks true \pdf_bookmarksnumbered true \pdf_bookmarksopen true \pdf_bookmarksopenlevel 1 \pdf_breaklinks true \pdf_pdfborder true \pdf_colorlinks true \pdf_backref false \pdf_pdfusetitle true \papersize default \use_geometry true \use_package amsmath 1 \use_package amssymb 1 \use_package cancel 1 \use_package esint 1 \use_package mathdots 1 \use_package mathtools 1 \use_package mhchem 1 \use_package stackrel 1 \use_package stmaryrd 1 \use_package undertilde 1 \cite_engine basic \cite_engine_type default \biblio_style plain \use_bibtopic false \use_indices false \paperorientation portrait \suppress_date false \justification true \use_refstyle 0 \use_minted 0 \index 索引 \shortcut idx \color #008000 \end_index \secnumdepth 3 \tocdepth 3 \paragraph_separation indent \paragraph_indentation default \is_math_indent 0 \math_numbering_side default \quotes_style english \dynamic_quotes 0 \papercolumns 1 \papersides 1 \paperpagestyle default \tracking_changes false \output_changes false \html_math_output 0 \html_css_as_file 0 \html_be_strict false \end_header \begin_body \begin_layout Title zlog \begin_inset Foot status collapsed \begin_layout Plain Layout A single spark can start a prairie fire – Mao Zedong \end_layout \end_inset User's Guide \end_layout \begin_layout Author Hardy Simpson \begin_inset Foot status collapsed \begin_layout Plain Layout This Guide is for zlog v1.2.* \end_layout \end_inset \begin_inset Foot status collapsed \begin_layout Plain Layout If you have comments or error corrections, post \begin_inset CommandInset href LatexCommand href name "a issue" target "https://github.com/HardySimpson/zlog/issues/new" literal "false" \end_inset on github, or write email to \begin_inset CommandInset href LatexCommand href name "HardySimpson1984@gmail.com" target "HardySimpson1984@gmail.com" type "mailto:" literal "false" \end_inset \end_layout \end_inset \end_layout \begin_layout Standard \begin_inset CommandInset toc LatexCommand tableofcontents \end_inset \end_layout \begin_layout Chapter What is zlog? \end_layout \begin_layout Standard zlog is a reliable, high-performance, thread safe, flexible, clear-model, pure C logging library. \end_layout \begin_layout Standard Actually, in the C world there was NO good logging library for applications like logback in java or log4cxx in c++. printf can work, but can not be redirected easily nor be reformatted. syslog is slow and is designed for system use. \end_layout \begin_layout Standard So I wrote zlog. \end_layout \begin_layout Standard It is faster, safer and more powerful than log4c. So it can be widely used. \end_layout \begin_layout Standard zlog has these features: \end_layout \begin_layout Itemize syslog model, better than log4j model \end_layout \begin_layout Itemize log format customization \end_layout \begin_layout Itemize multiple output, include static file path, dynamic file path, stdout, stderr, syslog, user-defined ouput \end_layout \begin_layout Itemize runtime with manual or automatic refresh of configuration (done safely) \end_layout \begin_layout Itemize high-performance, 250'000 logs/second on my laptop, about 1000 times faster than syslog(3) with rsyslogd \end_layout \begin_layout Itemize user-defined log level \end_layout \begin_layout Itemize safely rotate log file under multiple-process or multiple-thread conditions \end_layout \begin_layout Itemize accurate to microseconds \end_layout \begin_layout Itemize dzlog, a default category log API for easy use \end_layout \begin_layout Itemize MDC, a log4j style key-value map \end_layout \begin_layout Itemize self debuggable, can output zlog's self debug and error log at runtime \end_layout \begin_layout Itemize Does not depend on any other 3rd party library, just base on POSIX system (including pthread) and a C99 compliant vsnprintf. \end_layout \begin_layout Standard Links: \end_layout \begin_layout Standard Homepage: \begin_inset CommandInset href LatexCommand href name "http://hardysimpson.github.com/zlog" target "http://hardysimpson.github.com/zlog" literal "false" \end_inset \end_layout \begin_layout Standard Downloads: \begin_inset CommandInset href LatexCommand href target "https://github.com/HardySimpson/zlog/releases" literal "false" \end_inset \end_layout \begin_layout Standard Author's Email: \begin_inset CommandInset href LatexCommand href name "HardySimpson1984@gmail.com" target "HardySimpson1984@gmail.com" type "mailto:" literal "false" \end_inset \end_layout \begin_layout Section Compatibility Notes \end_layout \begin_layout Enumerate zlog is based on POSIX-compatible systems. I have just GNU/linux and AIX environments to compile, test and run zlog. Still, I think zlog will work well on FreeBSD, NetBSD, OpenBSD, OpenSolaris, Mac OS X etc. Test runs of zlog on any system are welcome. \end_layout \begin_layout Enumerate zlog uses a feature of C99 compliant vsnprintf. That is, if the buffer size of destination is not long enough, vsnprintf will return the number of characters (not including the trailing ' \backslash 0') which would have been written to the final string if enough space had been available. If the vsnprintf on your system does not work like that, zlog can not know the right buffer size when a single log is longer than the buffer. Fortunately, glibc 2.1, libc on AIX, and libc on freebsd work correctly, while glibc 2.0 does not. In this case, user should crack zlog himself with a C99 compliant vsnprintf. I suggest \begin_inset CommandInset href LatexCommand href name "ctrio" target "http://sourceforge.net/projects/ctrio/" literal "false" \end_inset , or \begin_inset CommandInset href LatexCommand href name "C99-snprintf" target "http://www.jhweiss.de/software/snprintf.html" literal "false" \end_inset . The file buf.c should be cracked, good luck! \end_layout \begin_layout Enumerate Some people offer versions of zlog for other platforms. Thanks! \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout Standard auto tools version: \begin_inset CommandInset href LatexCommand href target "https://github.com/bmanojlovic/zlog" literal "false" \end_inset \end_layout \begin_layout Standard cmake verion: \begin_inset CommandInset href LatexCommand href target "https://github.com/lisongmin/zlog" literal "false" \end_inset \end_layout \begin_layout Standard windows version: \begin_inset CommandInset href LatexCommand href target "https://github.com/lopsd07/WinZlog" literal "false" \end_inset \end_layout \end_deeper \begin_layout Section zlog 1.2 Release Notes \end_layout \begin_layout Enumerate zlog 1.2 provides these features: \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout Enumerate support for pipeline. Now zlog can send ouput log through programs like cronolog \end_layout \begin_layout Enumerate Full rotation support, see \begin_inset CommandInset ref LatexCommand ref reference "sec:Rotation" \end_inset \end_layout \begin_layout Enumerate Other code compatible details, bug fixes. \end_layout \end_deeper \begin_layout Enumerate zlog 1.2 is binary compatible with zlog 1.0. The differences are: \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout Enumerate All zlog macros like ZLOG_INFO are shifted to lowercase versions, zlog_info. This big change is because I think it is easier for people to type. If you are using a previous version of zlog, please use a script to substitute all macros, and re-compile your program. Here is an example: \end_layout \begin_layout LyX-Code sed -i -e 's/ \backslash b \backslash w*ZLOG \backslash w* \backslash b/ \backslash L& \backslash E/g' aa.c \end_layout \begin_layout Enumerate Auto tools compile is abandoned. Auto tools is ugly so I dropped it. A simple makefile is in use, which requires gcc and gnu make. If this makefile does not work in your environment, you will need to write a suitable makefile for yourself. It should be quite easy for a geek. \end_layout \end_deeper \begin_layout Chapter What zlog is not \end_layout \begin_layout Standard The goal of zlog is to be a simple, fast log library for applications. It does not support output like sending the log to another machine through the net or saving it to database. It will not parse content of log and filter them. \end_layout \begin_layout Standard The reason is obvious: the library is called by an application, so all time taken by the log library is part of the application's time. And database inserting or log content parsing takes a long time. These will slow down the application. These operation should be done in a different process or on a different machine. \end_layout \begin_layout Standard If you want all these features, I recommend rsyslog, zLogFabric, Logstash. These have independent processes to receive logs from another process or machine, and to parse and store logs. These functions are separated from the user application. \end_layout \begin_layout Standard Now \begin_inset CommandInset ref LatexCommand ref reference "sec:User-defined-Output" \end_inset is supported by zlog. Just one output function need to be implemented: to transfer the log to the other process or machine. The work of category matching and log generating is left with the zlog library. \end_layout \begin_layout Standard One possibility is to write a zlog-redis client. It send logs to redis on local or remote machines by user defined output. Then other processes can read logs from redis and write to disk. What do you think about this idea? I will be happy to discuss it with you. \end_layout \begin_layout Chapter Hello World \end_layout \begin_layout Section Build and Installation zlog \end_layout \begin_layout Standard Download: \begin_inset CommandInset href LatexCommand href target "https://github.com/HardySimpson/zlog/archive/latest-stable.tar.gz" literal "false" \end_inset \end_layout \begin_layout LyX-Code $ tar -zxvf zlog-latest-stable.tar.gz \end_layout \begin_layout LyX-Code $ cd zlog-latest-stable/ \end_layout \begin_layout LyX-Code $ make \end_layout \begin_layout LyX-Code $ sudo make install \end_layout \begin_layout LyX-Code or \end_layout \begin_layout LyX-Code $ sudo make PREFIX=/usr/local/ install \end_layout \begin_layout Standard PREFIX indicates where zlog is installed. After installation, change system settings to make sure your program can find the zlog library \end_layout \begin_layout LyX-Code $ sudo vi /etc/ld.so.conf \end_layout \begin_layout LyX-Code /usr/local/lib \end_layout \begin_layout LyX-Code $ sudo ldconfig \end_layout \begin_layout Standard Before running a real program, make sure libzlog.so is in the directory where the system's dynamic lib loader can find it. The commands mentioned above are for linux. Other systems will need a similar set of actions. \end_layout \begin_layout Itemize Beside the normal make, these are also available: \end_layout \begin_layout LyX-Code $ make 32bit # 32bit version on 64bit machine, libc6-dev-i386 is needed \end_layout \begin_layout LyX-Code $ make noopt # without gcc optimization \end_layout \begin_layout LyX-Code $ make doc # lyx and hevea is needed \end_layout \begin_layout LyX-Code $ make test # test code, which is also good example for zlog \end_layout \begin_layout Itemize makefile of zlog is written in gnu make style. So if your platform is not linux, install a gnu make and gcc before trying to build zlog. Another way is to write a makefile in your platform's make style. This should be quite easy as zlog is not complicated. \end_layout \begin_layout Section Call and Link zlog in User's application \end_layout \begin_layout Standard To use zlog, add one line to the source c file or cpp file: \end_layout \begin_layout LyX-Code #include "zlog.h" \end_layout \begin_layout Standard zlog needs the pthread library. The link command is: \end_layout \begin_layout LyX-Code $ cc -c -o app.o app.c -I/usr/local/include \end_layout \begin_layout LyX-Code # -I[where zlog.h is put] \end_layout \begin_layout LyX-Code $ cc -o app app.o -L/usr/local/lib -lzlog -lpthread \end_layout \begin_layout LyX-Code # -L[where libzlog.so] \end_layout \begin_layout Section Hello World Example \begin_inset CommandInset label LatexCommand label name "sec:Hello-World-Example" \end_inset \end_layout \begin_layout Standard This example can be found in $(top_builddir)/test/test_hello.c, test_hello.conf \end_layout \begin_layout Enumerate Write a new c source file: \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout LyX-Code $ vi test_hello.c \end_layout \begin_layout LyX-Code #include \end_layout \begin_layout LyX-Code #include "zlog.h" \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code int main(int argc, char** argv) \end_layout \begin_layout LyX-Code { \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout LyX-Code int rc; \end_layout \begin_layout LyX-Code zlog_category_t *c; \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code rc = zlog_init("test_hello.conf"); \end_layout \begin_layout LyX-Code if (rc) { \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout LyX-Code printf("init failed \backslash n"); \end_layout \begin_layout LyX-Code return -1; \end_layout \end_deeper \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code c = zlog_get_category("my_cat"); \end_layout \begin_layout LyX-Code if (!c) { \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout LyX-Code printf("get cat fail \backslash n"); \end_layout \begin_layout LyX-Code zlog_fini(); \end_layout \begin_layout LyX-Code return -2; \end_layout \end_deeper \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code zlog_info(c, "hello, zlog"); \end_layout \begin_layout LyX-Code zlog_fini(); \end_layout \begin_layout LyX-Code return 0; \end_layout \end_deeper \begin_layout LyX-Code } \end_layout \end_deeper \begin_layout Enumerate Write a configuration file in the same path as test_hello.c: \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout LyX-Code $ vi test_hello.conf \end_layout \begin_layout LyX-Code [formats] \end_layout \begin_layout LyX-Code simple = "%m%n" \end_layout \begin_layout LyX-Code [rules] \end_layout \begin_layout LyX-Code my_cat.DEBUG >stdout; simple \end_layout \end_deeper \begin_layout Enumerate Compile and run it: \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout LyX-Code $ cc -c -o test_hello.o test_hello.c -I/usr/local/include \end_layout \begin_layout LyX-Code $ cc -o test_hello test_hello.o -L/usr/local/lib -lzlog \end_layout \begin_layout LyX-Code $ ./test_hello \end_layout \begin_layout LyX-Code hello, zlog \end_layout \end_deeper \begin_layout Section Simpler Hello World Example \end_layout \begin_layout Standard This example can be found in $(top_builddir)/test/test_default.c, test_default.con f. The source code is \end_layout \begin_layout LyX-Code #include \end_layout \begin_layout LyX-Code #include "zlog.h" \end_layout \begin_layout LyX-Code int main(int argc, char** argv) \end_layout \begin_layout LyX-Code { \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout LyX-Code int rc; \end_layout \begin_layout LyX-Code rc = dzlog_init("test_default.conf", "my_cat"); \end_layout \begin_layout LyX-Code if (rc) { \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout LyX-Code printf("init failed \backslash n"); \end_layout \begin_layout LyX-Code return -1; \end_layout \end_deeper \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code dzlog_info("hello, zlog"); \end_layout \begin_layout LyX-Code zlog_fini(); \end_layout \begin_layout LyX-Code return 0; \end_layout \end_deeper \begin_layout LyX-Code } \end_layout \begin_layout Standard The configure file test_default.conf is the same as test_hello.conf, and the output of test_default is the same as that of test_hello. The difference is that test_default uses the dzlog API, which has a default \emph on zlog_category_t \emph default inside and is easier to use. See \begin_inset CommandInset ref LatexCommand ref reference "sec:dzlog-API" \end_inset for more details. \end_layout \begin_layout Chapter Syslog model \end_layout \begin_layout Section Category, Rule and Format \end_layout \begin_layout Standard In zlog, there are 3 important concepts: category, rule and format. \end_layout \begin_layout Standard Category specifies different kinds of log entries. In the zlog source code, category is a (zlog_cateogory_t *) variable. In your program, different categories for the log entries will distinguish them from each other. \end_layout \begin_layout Standard Format describes detail log patterns, such as: with or without time stamp, source file, source line. \end_layout \begin_layout Standard Rule consists of category, level, output file (or other channel) and format. In brief, if the category string in a rule in the configuration file equals the name of a category variable in the source, then they match. \end_layout \begin_layout Standard So when this sentence in the source file is executed: \end_layout \begin_layout LyX-Code zlog_category_t *c; \end_layout \begin_layout LyX-Code c = zlog_get_category("my_cat"); \end_layout \begin_layout LyX-Code zlog_info(c, "hello, zlog"); \end_layout \begin_layout Standard zlog library uses the category name "my_cat" to match one rule in the configurat ion file. That is \end_layout \begin_layout LyX-Code [rules] \end_layout \begin_layout LyX-Code my_cat.DEBUG >stdout; simple \end_layout \begin_layout Standard Then the library will check if level is correct to decide whether the log will be output or not. As INFO>=DEBUG the log will be output, and as the rule says, it will be sent to stdout (standard output) in the format of simple, which is described as \end_layout \begin_layout LyX-Code [formats] \end_layout \begin_layout LyX-Code simple = "%m%n" \end_layout \begin_layout Standard Lastly, zlog will show the zlog_info() content on the screen \end_layout \begin_layout LyX-Code hello, zlog \end_layout \begin_layout Standard That's the whole story. The only thing a user need to do is to write the messages. Where the log will be output, or in which format, is done by zlog library. \end_layout \begin_layout Section Differences between syslog model and log4j model \end_layout \begin_layout Standard Does zLog have anything to do with syslog? Until now, the model is more like log4j. As in log4j, there are concepts of logger, appender and layout. The difference is that in log4j, each logger in source code must correspond to one logger in the configuration file and has just one definite level. One-to-one relationship is the only choice for log4j, log4cxx, log4cpp, log4cplus log4net and etc... \end_layout \begin_layout Standard But the log4j model is NOT flexible, they invent filters to make up for it, and that make things more worse. So let's get back to syslog model, which has an excellent design. \end_layout \begin_layout Standard Continuing our example from the last section, if the zlog configuration file has 2 rules: \end_layout \begin_layout LyX-Code [rules] \end_layout \begin_layout LyX-Code my_cat.DEBUG >stdout; simple \end_layout \begin_layout LyX-Code my_cat.INFO >stdout; \end_layout \begin_layout Standard Then they will generate 2 log outputs to stdout: \end_layout \begin_layout LyX-Code hello, zlog \end_layout \begin_layout LyX-Code 2012-05-29 10:41:36 INFO [11288:test_hello.c:41] hello, zlog \end_layout \begin_layout Standard You see that one category in the source code corresponds to two rules in the configuration file. Maybe log4j's user will say, "That's good, but 2 appender for one logger will do the same thing". So, let's see the next example of configure file: \end_layout \begin_layout LyX-Code [rules] \end_layout \begin_layout LyX-Code my_cat.WARN "/var/log/aa.log" \end_layout \begin_layout LyX-Code my_cat.DEBUG "/var/log/bb.log" \end_layout \begin_layout Standard And the source code is: \end_layout \begin_layout LyX-Code zlog_info(c, "info, zlog"); \end_layout \begin_layout LyX-Code zlog_debug(c, "debug, zlog"); \end_layout \begin_layout Standard Then, in aa.log, there is just one log \end_layout \begin_layout LyX-Code 2012-05-29 10:41:36 INFO [11288:test_hello.c:41] info, zlog \end_layout \begin_layout Standard But in bb.log, there will be two \end_layout \begin_layout LyX-Code 2012-05-29 10:41:36 INFO [11288:test_hello.c:41] info, zlog \end_layout \begin_layout LyX-Code 2012-05-29 10:41:36 DEBUG [11288:test_hello.c:42] debug, zlog \end_layout \begin_layout Standard From this example, you see the difference. Log4j can not do it easily. In zlog, one category may correspond to mutiple rules, and rules can have different level, output, and format combinations. The user has an easy, clear way to filter and multi-ouput all logs on demand. \end_layout \begin_layout Section Expand syslog model \end_layout \begin_layout Standard You can see that category in zlog is more like facility in syslog. Unfortunately, facility in sylog is an int, and the value of facility must be chosen from a limited system-defined range. zlog does better, making it a string variable. \end_layout \begin_layout Standard In syslog, there is a special wildcard "*", which matches all facilities. It does the same thing in zlog. "*" matches all categories. That is a convenient way to make all errors generated by multiple components in your system redirect to one log file. Just write in the configuration file like this: \end_layout \begin_layout LyX-Code [rules] \end_layout \begin_layout LyX-Code *.error "/var/log/error.log" \end_layout \begin_layout Standard A unique feature of zlog is sub-category matching. If your source code has: \end_layout \begin_layout LyX-Code c = zlog_get_category("my_cat"); \end_layout \begin_layout Standard And the configuration file has rules : \end_layout \begin_layout LyX-Code [rules] \end_layout \begin_layout LyX-Code my_cat.* "/var/log/my_cat.log" \end_layout \begin_layout LyX-Code my_.NOTICE "/var/log/my.log" \end_layout \begin_layout Standard These 2 rules match category "c" with the name "my_cat". The wildcard "_" is the way to represent a super category. "my_" is a super category for "my_cat" and "my_dog". There is also another wildcard "!". See \begin_inset CommandInset ref LatexCommand ref reference "subsec:Category-Matching" \end_inset for more detail. \end_layout \begin_layout Chapter Configure File \end_layout \begin_layout Standard Most actions of zlog library are dependent upon the configuration file: where to output the log, how to rotate the log files, how to format the output, etc. The configuration file uses a domain specific language to control the library actions. Here is an example of zlog.conf: \end_layout \begin_layout LyX-Code # comments \end_layout \begin_layout LyX-Code [global] \end_layout \begin_layout LyX-Code strict init = true \end_layout \begin_layout LyX-Code reload conf period = 1M \end_layout \begin_layout LyX-Code buffer min = 1024 \end_layout \begin_layout LyX-Code buffer max = 2MB \end_layout \begin_layout LyX-Code rotate lock file = /tmp/zlog.lock \end_layout \begin_layout LyX-Code default format = "%d.%ms %-6V (%c:%F:%L) - %m%n" \end_layout \begin_layout LyX-Code file perms = 600 \end_layout \begin_layout LyX-Code fsync period = 1K \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code [levels] \end_layout \begin_layout LyX-Code TRACE = 10 \end_layout \begin_layout LyX-Code CRIT = 130, LOG_CRIT \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code [formats] \end_layout \begin_layout LyX-Code simple = "%m%n" \end_layout \begin_layout LyX-Code normal = "%d(%F %T) %m%n" \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code [rules] \end_layout \begin_layout LyX-Code default.* >stdout; simple \end_layout \begin_layout LyX-Code *.* "%12.2E(HOME)/log/%c.log", 1MB*12; simple \end_layout \begin_layout LyX-Code my_.INFO >stderr; \end_layout \begin_layout LyX-Code my_cat.!ERROR "/var/log/aa.log" \end_layout \begin_layout LyX-Code my_dog.=DEBUG >syslog, LOG_LOCAL0; simple \end_layout \begin_layout LyX-Code my_mice.* $user_define; \end_layout \begin_layout Standard [] means a section's beginning, and the order of sections is fixed, using the sequence global-levels-formats-rules. \end_layout \begin_layout Standard Note on units: when memory size or large number is needed, it is possible to specify it in the usual form of 1k 5GB 4M and so forth: \end_layout \begin_layout LyX-Code # 1k => 1000 bytes \end_layout \begin_layout LyX-Code # 1kb => 1024 bytes \end_layout \begin_layout LyX-Code # 1m => 1000000 bytes \end_layout \begin_layout LyX-Code # 1mb => 1024*1024 bytes \end_layout \begin_layout LyX-Code # 1g => 1000000000 bytes \end_layout \begin_layout LyX-Code # 1gb => 1024*1024*1024 byte \end_layout \begin_layout Standard units are case insensitive so 1GB 1Gb 1gB are all the same. \end_layout \begin_layout Section Global \end_layout \begin_layout Standard Global section begins with [global]. This section can be omitted.The syntax is \end_layout \begin_layout LyX-Code (key) = (value) \end_layout \begin_layout Itemize strict init \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout Standard If "strict init = true \begin_inset Quotes erd \end_inset , zlog_init() will check syntax of all formats and rules strictly, and any error will cause zlog_init() to fail and return -1. When "strict init = false \begin_inset Quotes erd \end_inset , zlog_init() will ignore syntax errors for formats and rules. The default is true. \end_layout \end_deeper \begin_layout Itemize reload conf period \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout Standard This parameter causes the zlog library to reload the configuration file automatically after a period, which is measured by number of log times per process. When the number reaches the value, it calls zlog_reload() internally. The number is reset to zero at the last zlog_reload() or zlog_init(). As zlog_reload() is atomic, if zlog_reload() fails, zlog still runs with the current configuration. So reloading automatically the configuration is safe. The default is 0, which means never reload automatically. \end_layout \end_deeper \begin_layout Itemize buffer min \end_layout \begin_layout Itemize buffer max \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout Standard zlog allocates a log buffer in each thread. "buffer min" indicates size of buffer malloc'ed at init time. While logging, if one single log's content is longer than buffer size now, zlog will expand buffer automatically until "buffer max". Then, if the size is still longer than "buffer max", the log content will be truncated. If "buffer max" is 0, it means buffer size is unlimited, and each time zlog will expand buffer by twice its size, until the process uses all available memory. The value of these 2 parameters can appended with unit KB, MB or GB suffix, where 1024 equals 1KB. As default, "buffer min" is 1K and "buffer max" is 2MB. \end_layout \end_deeper \begin_layout Itemize rotate lock file \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout Standard This specifies a lock file for rotating a log safely in multi-process situations. zlog will open the file at zlog_init() with the permission of read-write. The pseudo-code for rotating a log file is: \end_layout \begin_layout LyX-Code write(log_file, a_log) \end_layout \begin_layout LyX-Code if (log_file > 1M) \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout LyX-Code if (pthread_mutex_lock succ && fcntl_lock(lock_file) succ) \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout LyX-Code if (log_file > 1M) rotate(log_file); \end_layout \begin_layout LyX-Code fcntl_unlock(lock_file); \end_layout \begin_layout LyX-Code pthread_mutex_unlock; \end_layout \end_deeper \end_deeper \begin_layout Standard mutex_lock is for multi-thread and fcntl_lock is for multi-process. fcntl_lock is the POSIX advisory record locking. See man 3 fcntl for details. The lock is system-wide, and when a process dies unexpectedly, the operating system releases all locks owned by the process. That's why I chose fcntl lock for rotating log safely. The process needs read-write permisson for lock_file to lock it. \end_layout \begin_layout Standard By default, rotate lock file = self. This way, zlog does not create any lock file and sets the configuration file as the lock file. As fcntl is advisory, it does not really forbid programmers to change and store the configuration file. Generally speaking, one log file will not be rotated by processes run by different operating system users, so using the configuration file as lock file is safe. \end_layout \begin_layout Standard If you choose another path as lock file, for example, /tmp/zlog.lock, zlog will create it at zlog_init(). Make sure your program has permission to create and read-write the file. If processes run by different operating system users need to write and rotate the same log file, make sure that each program has permission to create and read-write the same lock file. \end_layout \end_deeper \begin_layout Itemize default format \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout Standard This parameter is used by rules without format specified. The default is \end_layout \begin_layout LyX-Code "%d %V [%p:%F:%L] %m%n" \end_layout \begin_layout Standard It will yield output like this: \end_layout \begin_layout LyX-Code 2012-02-14 17:03:12 INFO [3758:test_hello.c:39] hello, zlog \end_layout \end_deeper \begin_layout Itemize file perms \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout Standard This specifies all log file permissions when they are created. Note that it is affected by user's umask. The final file permission will be "file perms" & ~umask. The default is 600, which just allows user read and write. \end_layout \end_deeper \begin_layout Itemize fsync period \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout Standard After a number of log times per rule (to file only), zlog will call fsync(3) after write() to tell the Operating System to write data to disk immediately from any internal system buffers. The number is incremented by each rule and will be reset to 0 after zlog_reload (). Note that when the file's path is generated dynamically or is rotated, zlog does not guarantee fsync() touch all files. It just does fsync() against the file descriptors that have have seen write() prior to the boundary time. It offers a balance between speed and data safety. An example: \end_layout \begin_layout LyX-Code $ time ./test_press_zlog 1 10 100000 \end_layout \begin_layout LyX-Code real 0m1.806s \end_layout \begin_layout LyX-Code user 0m3.060s \end_layout \begin_layout LyX-Code sys 0m0.270s \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code $ wc -l press.log \end_layout \begin_layout LyX-Code 1000000 press.log \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code $ time ./test_press_zlog 1 10 100000 #fsync period = 1K \end_layout \begin_layout LyX-Code real 0m41.995s \end_layout \begin_layout LyX-Code user 0m7.920s \end_layout \begin_layout LyX-Code sys 0m0.990s \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code $ time ./test_press_zlog 1 10 100000 #fsync period = 10K \end_layout \begin_layout LyX-Code real 0m6.856s \end_layout \begin_layout LyX-Code user 0m4.360s \end_layout \begin_layout LyX-Code sys 0m0.550s \end_layout \begin_layout Standard If you want extreme safety but do not care about speed, use synchronous file I/O, see \begin_inset CommandInset ref LatexCommand ref reference "ite:synchronous-I/O-file" \end_inset .The defualt is 0, which means let the operating system flush the output buffer when it wants. \end_layout \end_deeper \begin_layout Section Levels \end_layout \begin_layout Standard This section begins with [levels] and allows the user to define application levels. You should match these values with user-defined macros in the source file. This section can be omitted. \end_layout \begin_layout Standard The syntax is \end_layout \begin_layout LyX-Code (level string) = (level int), (syslog level, optional) \end_layout \begin_layout Standard level int should in [1,253], higher numbers mean more important. syslog level is optional, if not set, use LOG_DEBUG \end_layout \begin_layout Standard see \begin_inset CommandInset ref LatexCommand ref reference "sec:User-defined-Level" \end_inset for more details. \end_layout \begin_layout Section Formats \end_layout \begin_layout Standard This section begins with [formats], where the user can define preferred log patterns. The syntax is \end_layout \begin_layout LyX-Code (name) = "(actual formats)" \end_layout \begin_layout Standard It is easy to understand, (name) will be used in the next section [rules]. The format (name) consists of letters and digits plus underscore "_". The (actual format) should be put in double quotes. It can be built up with conversion patterns, as described below. \end_layout \begin_layout Section Conversion pattern \begin_inset CommandInset label LatexCommand label name "sec:Conversion-pattern" \end_inset \end_layout \begin_layout Standard The conversion pattern is closely related to the conversion pattern of the C printf function. A conversion pattern is composed of literal text and format control expressions called conversion specifiers. \end_layout \begin_layout Standard Conversion pattern is used in both filepath of rule and pattern of format. \end_layout \begin_layout Standard You are free to insert any literal text within the conversion pattern. \end_layout \begin_layout Standard Each conversion specifier starts with a percent sign (%) and is followed by optional format modifiers and a conversion character. The conversion character specifies the type of data, e.g. category, level, date, thread id. The format modifiers control such things as field width, padding, left and right justification. The following is a simple example. \end_layout \begin_layout Standard Let the conversion pattern be \end_layout \begin_layout LyX-Code "%d(%m-%d %T) %-5V [%p:%F:%L] %m%n". \end_layout \begin_layout Standard Then the statement \end_layout \begin_layout LyX-Code zlog_info(c, "hello, zlog"); \end_layout \begin_layout Standard would yield the output \end_layout \begin_layout LyX-Code 02-14 17:17:42 INFO [4935:test_hello.c:39] hello, zlog \end_layout \begin_layout Standard Note that there is no explicit separator between text and conversion specifiers. The pattern parser knows when it has reached the end of a conversion specifier when it reads a conversion character. In the example above the conversion specifier %-5p means the level of the logging event should be left justified to a width of five characters. \end_layout \begin_layout Subsection Conversion Characters \end_layout \begin_layout Standard The recognized conversion characters are \end_layout \begin_layout Standard \begin_inset Tabular \begin_inset Text \begin_layout Plain Layout conversion char \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout effect \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout example \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %c \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout Used to output the category of the logging event. \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout aa_bb \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %d() \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout Used to output the date of the logging event. The date conversion specifier may be followed by a date format specifier enclosed between parentheses. For example, %d(%F) or %d(%m-%d %T). If no date format specifier is given then %d(%F %T) format is assumed. The date format specifier permits the same syntax as the strftime(2). see \begin_inset CommandInset ref LatexCommand ref reference "subsec:Time-Character" \end_inset for more detail. \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %d(%F) 2011-12-01 \end_layout \begin_layout Plain Layout %d(%m-%d %T) 12-01 17:17:42 \end_layout \begin_layout Plain Layout %d(%T).%ms 17:17:42.035 \end_layout \begin_layout Plain Layout %d 2012-02-14 17:03:12 \end_layout \begin_layout Plain Layout %d() 2012-02-14 17:03:12 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %E() \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout Value of environment variables \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %E(LOGNAME) simpson \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %ms \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout The millisecond, 3-digit integer string \end_layout \begin_layout Plain Layout comes from gettimeofday(2) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 013 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %us \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout The microsecond, 6-digit integer string \end_layout \begin_layout Plain Layout comes from gettimeofday(2) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 002323 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %F \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout Used to output the file name where the logging request was issued. The file name comes from __FILE__ macro. Some compilers supply __FILE__ as the absolute path. Use %f to strip path and keep the file name. Some compilers have an option to control this feature. \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout test_hello.c \end_layout \begin_layout Plain Layout or, under some compiler \end_layout \begin_layout Plain Layout /home/zlog/src/test/test_hello.c \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %f \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout Used to output the source file name, the string after the last '/' of $F. It will cause a little performance loss in each log event. \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout test_hello.c \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %g() \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout Used to output the date of the logging event in UTC in stead of local time. \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %g(%F) 2011-12-01 \end_layout \begin_layout Plain Layout %g(%m-%d %T) 12-01 15:17:42 \end_layout \begin_layout Plain Layout %g(%T.ms) 15:17:42.035 \end_layout \begin_layout Plain Layout %g 2012-02-14 15:03:12 \end_layout \begin_layout Plain Layout %g() 2012-02-14 15:03:12 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %H \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout Used to output the hostname of system, which is from gethostname(2) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout zlog-dev \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %k \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout Used to output the kernel thread id. On Linux, that's the LWP using syscall(SYS_gettid) and on OSX, pthread_threadid _np. \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 2136 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %L \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout Used to output the line number from where the logging request was issued, which comes from __LINE__ macro \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 135 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %m \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout Used to output the application supplied message associated with the logging event. \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout hello, zlog \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %M \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout Used to output the MDC (mapped diagnostic context) associated with the thread that generated the logging event. The M conversion character must be followed by the key for the map placed between parenthesis, as in %M(clientNumber) where clientNumber is the key. The value in the MDC corresponding to the key will be output.See \begin_inset CommandInset ref LatexCommand ref reference "sec:MDC" \end_inset for more detail. \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %M(clientNumber) 12345 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %n \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout Outputs unix newline character, zLog does not support the MS-Windows line separator at this time. \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout \backslash n \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %p \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout Used to output the id of the process that generated the logging event, which comes from getpid(). \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 2134 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %U \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout Used to output the function name where the logging request was issued. It comes from __func__(C99) or __FUNCTION__(gcc) macro, with the support of the compiler. \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout main \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %V \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout Used to output the level of the logging event, uppercase. \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout INFO \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %v \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout Used to output the level of the logging event, lowercase. \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout info \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %t \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout Used to output the hexadecimal form of the thread id that generated the logging event, which comes from pthread_self(). \end_layout \begin_layout Plain Layout "%x",(unsigned int) pthread_t \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout ba01e700 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %T \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout Equivalent to %t, but the long form \end_layout \begin_layout Plain Layout "%lu", (unsigned long) pthread_t \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 140633234859776 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %% \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout the sequence %% outputs a single percent sign. \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout % \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %[other char] \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout parsed as a wrong syntax, will cause zlog_init() fail \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout \end_layout \end_inset \end_inset \end_layout \begin_layout Subsection Format Modifier \end_layout \begin_layout Standard By default, the relevant information is output as-is. However, with the aid of format modifiers it is possible to change the minimum field width, the maximum field width, and justification. It will cause a little performance loss in each log event. \end_layout \begin_layout Standard The optional format modifier is placed between the percent sign and the conversion character. \end_layout \begin_layout Standard The first optional format modifier is the left justification flag which is just the minus (-) character. Then comes the optional minimum field width modifier. This is a decimal constant that represents the minimum number of characters to output. If the data item requires fewer characters, it is padded on either the left or the right until the minimum width is reached. The default is to pad on the left (right justify) but you can specify right padding with the left justification flag. The padding character is space. If the data item is larger than the minimum field width, the field is expanded to accommodate the data. The value is never truncated. \end_layout \begin_layout Standard This behavior can be changed using the maximum field width modifier which is designated by a period followed by a decimal constant. If the data item is longer than the maximum field, then the extra characters are removed from the beginning of the data item and not from the end. For example, if the maximum field width is eight and the data item is ten characters long, then the last two characters of the data item are dropped. This behavior equals the printf function in C where truncation is done from the end. \end_layout \begin_layout Standard Below are various format modifier examples for the category conversion specifier. \end_layout \begin_layout Standard \begin_inset Tabular \begin_inset Text \begin_layout Plain Layout format modifier \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout left justify \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout minimum width \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout maximum width \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout comment \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %20c \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout false \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 20 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout none \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout Left pad with spaces if the category name is less than 20 characters long. \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %-20c \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout true \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 20 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout none \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout Right pad with spaces if the category name is less than 20 characters long. \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %020c \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout false \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 20 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout none \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout Left pad with 0's if the category name is less than 20 characters long. \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %.30c \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout NA \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout none \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 30 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout Truncate from the end if the category name is longer than 30 characters. \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %20.30c \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout false \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 20 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 30 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout Left pad with spaces if the category name is shorter than 20 characters. However, if category name is longer than 30 characters, then truncate from the end. \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %-20.30c \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout true \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 20 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 30 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout Right pad with spaces if the category name is shorter than 20 characters. However, if category name is longer than 30 characters, then truncate from the end. \end_layout \end_inset \end_inset \end_layout \begin_layout Subsection Time Character \begin_inset CommandInset label LatexCommand label name "subsec:Time-Character" \end_inset \end_layout \begin_layout Standard Here is the Time Character support by Conversion Character \emph on d. \emph default \end_layout \begin_layout Standard All Character is supported by strftime(3) in library. The Character supported on my linux system are \end_layout \begin_layout Standard \begin_inset Tabular \begin_inset Text \begin_layout Plain Layout character \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout effect \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout example \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %a \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout The abbreviated weekday name according to the current locale. \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout Wed \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %A \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout The full weekday name according to the current locale. \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout Wednesday \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %b \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout The abbreviated month name according to the current locale. \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout Mar \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %B \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout The full month name according to the current locale. \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout March \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %c \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout The preferred date and time representation for the current locale. \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout Thu Feb 16 14:16:35 2012 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %C \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout The century number (year/100) as a 2-digit integer. (SU) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 20 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %d \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout The day of the month as a decimal number (range 01 to 31). \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 06 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %D \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout Equivalent to %m/%d/%y. (for Americans only. Americans should note that in other countries %d/%m/%y is more common. This means that in an international context this format is ambiguous and should not be used.) (SU) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 02/16/12 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %e \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout Like %d, the day of the month as a decimal number, but a leading zero is replaced by a space. (SU) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 6 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %F \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout Equivalent to %Y-%m-%d (the ISO 8601 date format). (C99) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 2012-02-16 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %G \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout The ISO 8601 week-based year (see NOTES) with century as a decimal number. The 4-digit year corresponding to the ISO week number (see %V). This has the same format and value as %Y, except that if the ISO week number belongs to the previous or next year, that year is used instead. (TZ) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 2012 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %g \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout Like %G, but without century, that is, with a 2-digit year (00-99). (TZ) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 12 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %h \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout Equivalent to %b. (SU) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout Feb \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %H \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout The hour as a decimal number using a 24-hour clock (range 00 to 23). \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 14 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %I \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout The hour as a decimal number using a 12-hour clock (range 01 to 12). \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 02 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %j \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout The day of the year as a decimal number (range 001 to 366). \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 047 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %k \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout The hour (24-hour clock) as a decimal number (range 0 to 23); single digits are preceded by a blank. (See also %H.) (TZ) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 15 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %l \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout The hour (12-hour clock) as a decimal number (range 1 to 12); single digits are preceded by a blank. (See also %I.) (TZ) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 3 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %m \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout The month as a decimal number (range 01 to 12). \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 02 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %M \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout The minute as a decimal number (range 00 to 59). \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 11 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %n \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout A newline character. (SU) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout \backslash n \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %p \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout Either "AM" or "PM" according to the given time value, or the corresponding strings for the current locale. Noon is treated as "PM" and midnight as "AM". \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout PM \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %P \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout Like %p but in lowercase: "am" or "pm" or a corresponding string for the current locale. (GNU) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout pm \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %r \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout The time in a.m. or p.m. notation. In the POSIX locale this is equivalent to %I:%M:%S %p. (SU) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 03:11:54 PM \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %R \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout The time in 24-hour notation (%H:%M). (SU) For a version including the seconds, see %T below. \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 15:11 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %s \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout The number of seconds since the Epoch, that is, since 1970-01-01 00:00:00 UTC. (TZ) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 1329376487 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %S \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout The second as a decimal number (range 00 to 60). (The range is up to 60 to allow for occasional leap seconds.) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 54 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %t \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout A tab character. (SU) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %T \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout The time in 24-hour notation (%H:%M:%S). (SU) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 15:14:47 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %u \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout The day of the week as a decimal, range 1 to 7, Monday being 1. See also %w. (SU) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 4 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %U \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout The week number of the current year as a decimal number, range 00 to 53, starting with the first Sun‐ day as the first day of week 01. See also %V and %W. \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 07 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %V \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout The ISO 8601 week number (see NOTES) of the current year as a decimal number, range 01 to 53, where week 1 is the first week that has at least 4 days in the new year. See also %U and %W. (SU) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 07 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %w \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout The day of the week as a decimal, range 0 to 6, Sunday being 0. See also %u. \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 4 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %W \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout The week number of the current year as a decimal number, range 00 to 53, starting with the first Mon‐ day as the first day of week 01. \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 07 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %x \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout The preferred date representation for the current locale without the time. \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 02/16/12 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %X \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout The preferred time representation for the current locale without the date. \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 15:14:47 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %y \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout The year as a decimal number without a century (range 00 to 99). \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 12 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %Y \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout The year as a decimal number including the century. \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout 2012 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %z \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout The time-zone as hour offset from GMT. Required to emit RFC 822-conformant dates (using "%a, %d %b %Y %H:%M:%S %z"). (GNU) \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout +0800 \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %Z \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout The timezone or name or abbreviation. \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout CST \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout %% \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout A literal '%' character. \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout % \end_layout \end_inset \end_inset \end_layout \begin_layout Standard \end_layout \begin_layout Section Rules \end_layout \begin_layout Standard This section begins with [rules]. It decides how log actions are filtered, formatted and output. This section can be omitted, but there will result in no log output. The syntax is \end_layout \begin_layout LyX-Code (category).(level) (output), (option,optional); (format name, optional) \end_layout \begin_layout Standard When zlog_init() is called, all rules will be read into memory. When zlog_get_category() is called, mutiple rules will be assigned to each category, in the way \begin_inset CommandInset ref LatexCommand ref reference "subsec:Category-Matching" \end_inset describes. When logging is performed, the level between matched rules and INFO will be checked to decide whether this single log will be output through the rule. When zlog_reload() is called, the configuration file will be re-read into memory, including rules. All category rules will be re-calculated. \end_layout \begin_layout Subsection Level Matching \end_layout \begin_layout Standard There are six default levels in zlog, "DEBUG", "INFO", "NOTICE", "WARN", "ERROR" and "FATAL". As in all other log libraries, aa.DEBUG means all logs of level greater than or equal to DEBUG will be output. Still, there are more expressions. Levels in the configuration file are not case sensitive; both capital or lowercase are accepted. \end_layout \begin_layout Standard \begin_inset Tabular \begin_inset Text \begin_layout Plain Layout example expression \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout meaning \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout * \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout all [source level] \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout aa.debug \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout [source level]>=debug \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout aa.=debug \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout [source level]==debug \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout aa.!debug \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout [source level]!=debug \end_layout \end_inset \end_inset \end_layout \begin_layout Standard The level strings can be defined by the user. See \begin_inset CommandInset ref LatexCommand ref reference "sec:User-defined-Level" \end_inset . \end_layout \begin_layout Subsection Category Matching \begin_inset CommandInset label LatexCommand label name "subsec:Category-Matching" \end_inset \end_layout \begin_layout Standard Category Matching is simple. The name of the category is made up of letters, digits, and/or the underscore "_". \end_layout \begin_layout Standard \begin_inset Tabular \begin_inset Text \begin_layout Plain Layout summarize \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout category string from configure file \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout category matches \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout category not matches \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout * matches all \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout *.* \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout aa, aa_bb, aa_cc, xx, yy ... \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout NONE \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout string end with underline matches super-category and sub-categories \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout aa_.* \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout aa, aa_bb, aa_cc, aa_bb_cc \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout xx, yy \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout string not ending with underline accurately matches category \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout aa.* \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout aa \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout aa_bb, aa_cc, aa_bb_cc \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout ! matches category that has no rule matched \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout !.* \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout xx \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout aa(as it matches rules above) \end_layout \end_inset \end_inset \end_layout \begin_layout Subsection Output Action \end_layout \begin_layout Standard zlog supports various output methods. The syntax is \end_layout \begin_layout LyX-Code (output action), (output option); (format name, optional) \end_layout \begin_layout Standard \begin_inset Tabular \begin_inset Text \begin_layout Plain Layout output \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout output action \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout output option \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout to standard out \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout >stdout \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout no meaning \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout to standard error \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout >stderr \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout no meaning \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout to syslog \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout >syslog \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout syslog facility, can be: LOG_USER(default), LOG_LOCAL[0-7] \end_layout \begin_layout Plain Layout This is required. \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout pipeline output \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout | cat \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout no meaning \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout to file \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout "(file path)" \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout rotation. see \begin_inset CommandInset ref LatexCommand ref reference "sec:Rotation" \end_inset for detail \end_layout \begin_layout Plain Layout 10MB * 3 ~ "press.#r.log" \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout synchronous I/O file \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout -"(file path)" \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout user-defined output \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout $name \end_layout \end_inset \begin_inset Text \begin_layout Plain Layout "path" (dynamic or static) of record function \end_layout \end_inset \end_inset \end_layout \begin_layout Itemize stdout, stderr, syslog \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout Standard As the above table describes, only the syslog action has a meaningful output option and it must be set. \end_layout \begin_layout Standard Warning: NEVER use >stdout or >stderr when your program is a daemon process. A daemon process always closes its first file descriptor, and when >stdout is set, zlog will output a log like this \end_layout \begin_layout LyX-Code write(STDOUT_FILENO, zlog_buf_str(a_thread->msg_buf), zlog_buf_len(a_thread->msg _buf)) \end_layout \begin_layout Standard What will happen then? The log will be written to the file whose fd is now 1. I have received mail from someone who said zlog as a daemon wrote logs to the configuration file. So remember, daemon processes should not set any rule output to stdout, or stderr. It will generate undefined behavior. If you still want output logs to console when stdout is closed, use "/dev/tty" instead. \end_layout \end_deeper \begin_layout Itemize pipeline output \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout LyX-Code *.* | /usr/bin/cronolog /www/logs/example_%Y%m%d.log ; normal \end_layout \begin_layout Standard This is an example of how zlog pipelines its output to cronolog. The implementation is simple. popen("/usr/bin/cronolog /www/logs/example_%Y%m%d.log","w") is called at zlog_init(), and forward logs will be written to the open descriptor in the "normal" format. Writing through pipeline and cronnolog is faster than dynamic file of zlog, as there is no need to open and close file descripter each time when logs are written to a pipe. \end_layout \begin_layout LyX-Code [rules] \end_layout \begin_layout LyX-Code *.* "press%d(%Y%m%d).log" \end_layout \begin_layout LyX-Code $ time ./test_press_zlog 1 10 100000 \end_layout \begin_layout LyX-Code real 0m4.240s \end_layout \begin_layout LyX-Code user 0m2.500s \end_layout \begin_layout LyX-Code sys 0m5.460s \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code [rules] \end_layout \begin_layout LyX-Code *.* | /usr/bin/cronolog press%Y%m%d.log \end_layout \begin_layout LyX-Code $ time ./test_press_zlog 1 10 100000 \end_layout \begin_layout LyX-Code real 0m1.911s \end_layout \begin_layout LyX-Code user 0m1.980s \end_layout \begin_layout LyX-Code sys 0m1.470s \end_layout \begin_layout Standard There are some limitations when using pipeline output: \end_layout \begin_layout Itemize POSIX.1-2001 says that write(2)s of less than PIPE_BUF bytes must be atomic, On Linux, PIPE_BUF is 4096 bytes. \end_layout \begin_layout Itemize When a single log is longer than PIPE_BUF, and multiple processes write logs through one pipe (parent calls zlog_init(), and forks many child processes ), log interlacing will occur. \end_layout \begin_layout Itemize Unrelated multiple processes can start multiple cronolog processes and write to the same log file. Even if a single log is not longer than PIPE_BUF, multiple cronologs will cause log interlace. As cronologs read log continuously, it doesn't know where is the split between log entries. \end_layout \begin_layout Standard In summary, pipeline to a single log file: \end_layout \begin_layout Itemize Single process writing, no limitation for length of one log. Multi-threads in one process, atomic writing is already assured by zlog. \end_layout \begin_layout Itemize Related multiple processes, the length of one log should not longer than PIPE_BUF. \end_layout \begin_layout Itemize Unrelated multiple processes, no matter how long a single log is, will cause interlace and is not safe. \end_layout \end_deeper \begin_layout Itemize file \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout Itemize file path \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout Standard can be absolute file path or relative file path. It is quoted by double quotation marks. \emph on Conversion pattern \emph default can be used in file path. If the file path is "%E(HOME)/log/out.log" and the program environment $HOME is /home/harry, then the log file will be /home/harry/log/output.log. See \begin_inset CommandInset ref LatexCommand ref reference "sec:Conversion-pattern" \end_inset for more details. \end_layout \begin_layout Standard file of zlog is powerful, for example \end_layout \begin_layout Enumerate output to named pipe(FIFO), which must be created by mkfifo(1) before use \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout LyX-Code *.* "/tmp/pipefile" \end_layout \end_deeper \begin_layout Enumerate output to null, do nothing at all \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout LyX-Code *.* "/dev/null" \end_layout \end_deeper \begin_layout Enumerate output to console, in any case \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout LyX-Code *.* "/dev/tty" \end_layout \end_deeper \begin_layout Enumerate output a log to each tid, in the directory where the process running \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout LyX-Code *.* "%T.log" \end_layout \end_deeper \begin_layout Enumerate output to file with pid name, every day, in $HOME/log directory, rotate log at 1GB, keep 5 log files \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout LyX-Code *.* "%E(HOME)/log/aa.%p.%d(%F).log",1GB * 5 \end_layout \end_deeper \begin_layout Enumerate each category of aa_ super category, output log with category name \end_layout \begin_layout LyX-Code aa_.* "/var/log/%c.log" \end_layout \end_deeper \begin_layout Itemize rotate action \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout Standard controls log file size and count. zlog rotates the log file when the file exceeds this value. For example, let the action be \end_layout \begin_layout LyX-Code "%E(HOME)/log/out.log",1M*3 \end_layout \begin_layout Standard and after out.log is filled to 1M, the rotation is \end_layout \begin_layout LyX-Code out.log -> out.log.1 \end_layout \begin_layout LyX-Code out.log(new create) \end_layout \begin_layout Standard If the new log is full again, the rotation is \end_layout \begin_layout LyX-Code out.log.1 -> out.log.2 \end_layout \begin_layout LyX-Code out.log -> out.log.1 \end_layout \begin_layout LyX-Code out.log(new create) \end_layout \begin_layout Standard The next rotation will delete the oldest log, as *3 means just allows 3 file exist \end_layout \begin_layout LyX-Code unlink(out.log.2) \end_layout \begin_layout LyX-Code out.log.1 -> out.log.2 \end_layout \begin_layout LyX-Code out.log -> out.log.1 \end_layout \begin_layout LyX-Code out.log(new create) \end_layout \begin_layout Standard So the oldest log has the biggest serial number. If *3 is not specified, it means rotation will continue and no old log will be deleted. \end_layout \end_deeper \begin_layout Itemize synchronous I/O file \begin_inset CommandInset label LatexCommand label name "ite:synchronous-I/O-file" \end_inset \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout Standard Putting a minus sign '-' sets the synchronous I/O option. log file is opened with O_SYNC and every single log action will wait until the Operating System writes data to disk. It is painfully slow: \end_layout \begin_layout LyX-Code $ time ./test_press_zlog 100 1000 \end_layout \begin_layout LyX-Code real 0m0.732s \end_layout \begin_layout LyX-Code user 0m1.030s \end_layout \begin_layout LyX-Code sys 0m1.080s \end_layout \begin_layout LyX-Code $ time ./test_press_zlog 100 1000 # synchronous I/O open \end_layout \begin_layout LyX-Code real 0m20.646s \end_layout \begin_layout LyX-Code user 0m2.570s \end_layout \begin_layout LyX-Code sys 0m6.950s \end_layout \end_deeper \end_deeper \begin_layout Itemize format name \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout Standard It is optional. If not set, use zlog default format in global setting, which is: \end_layout \begin_layout LyX-Code [global] \end_layout \begin_layout LyX-Code default format = "%d %V [%p:%F:%L] %m%n" \end_layout \end_deeper \begin_layout Itemize see \begin_inset CommandInset ref LatexCommand ref reference "sec:User-defined-Output" \end_inset for more details for $. \end_layout \begin_layout Section Rotation \begin_inset CommandInset label LatexCommand label name "sec:Rotation" \end_inset \end_layout \begin_layout Standard Why rotation? I have see more than once in a production environment, that the hard disk is full of logs and causes the system to stop working, or a single log file is too big to open or grep. Several ways to rotate and archive log files are possible: \end_layout \begin_layout Enumerate Split log by date or time. \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout Standard For example, generate one log file per day. \end_layout \begin_layout LyX-Code aa.2012-08-02.log \end_layout \begin_layout LyX-Code aa.2012-08-03.log \end_layout \begin_layout LyX-Code aa.2012-08-04.log \end_layout \begin_layout Standard In this case, the system administrator knows how much log will be produced one day. The sys admin is able to search log files based on the day. The best way to make this split is to let the zlog library do it. Another choice is using cronosplit to analyse the content of log file and split it. A bad way is using crontab+logrotate to daily move log files, which is not accurate, some logs will be put into the file for the previous day. \end_layout \begin_layout Standard Using zlog, there is no need for external rotate action to complete the job. Setting time in the log file name works: \end_layout \begin_layout LyX-Code *.* "aa.%d(%F).log" \end_layout \begin_layout Standard or using cronolog for faster performace: \end_layout \begin_layout LyX-Code *.* | cronolog aa.%F.log \end_layout \end_deeper \begin_layout Enumerate Split log by size. \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout Standard Always suitable for development use. In this case, the program generates a lot of logs in a short period. But the text editor might not be able to open big files quickly. Although the split can be done using split tools afterwards, this requires extra steps. So a good way is to let the logging library do the rotation. There are two ways of rotation, as nlog describes, Sequence and Rolling. In case of Sequence: \end_layout \begin_layout LyX-Code aa.log (new) \end_layout \begin_layout LyX-Code aa.log.2 (less new) \end_layout \begin_layout LyX-Code aa.log.1 \end_layout \begin_layout LyX-Code aa.log.0 (old) \end_layout \begin_layout Standard And in case of Rolling: \end_layout \begin_layout LyX-Code aa.log (new) \end_layout \begin_layout LyX-Code aa.log.0 (less new) \end_layout \begin_layout LyX-Code aa.log.1 \end_layout \begin_layout LyX-Code aa.log.2 (old) \end_layout \begin_layout Standard It's hard to say which one is most suitable. \end_layout \begin_layout Standard If only some of the newest logs are useful to developers, logging library should do the cleanup work and delete the old log files. Some external tools can't find out which files are older. \end_layout \begin_layout Standard The simplest rotation configuration for zlog is: \end_layout \begin_layout LyX-Code *.* "aa.log", 10MB \end_layout \begin_layout Standard It is Rolling. When aa.log is larger than 10MB, zlog will rename file like this: \end_layout \begin_layout LyX-Code aa.log.2 -> aa.log.3 \end_layout \begin_layout LyX-Code aa.log.1 -> aa.log.2 \end_layout \begin_layout LyX-Code aa.log.0 -> aa.log.1 \end_layout \begin_layout LyX-Code aa.log -> aa.log.0 \end_layout \begin_layout Standard The configuration can be more complex: \end_layout \begin_layout LyX-Code *.* "aa.log", 10MB * 0 ~ "aa.log.#r" \end_layout \begin_layout Standard The 1st argument after the file name says when rotation will be triggered, in size. \end_layout \begin_layout Standard The 2nd argument after the file name says how many archive files will be kept, (0 means keep all). \end_layout \begin_layout Standard The 3rd argument after the file name shows the archive file name. #r is a sequence number for archive files. r is short for Rolling, and #s is short for sequence. Archive file name must contain one of #r or #s. \end_layout \end_deeper \begin_layout Enumerate Split log by size, and add time tag to archive file. \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout LyX-Code aa.log \end_layout \begin_layout LyX-Code aa.log-20070305.00.log \end_layout \begin_layout LyX-Code aa.log-20070501.00.log \end_layout \begin_layout LyX-Code aa.log-20070501.01.log \end_layout \begin_layout LyX-Code aa.log-20071008.00.log \end_layout \begin_layout Standard In this case, the log file is not usually viewed frequently, and is likely checked once a day. Of course, when one day's log is more than 100MB, you should consider storing in two files and add postfix numbers. For example if the date is used as part of the pattern (like 20070501): \end_layout \begin_layout Standard The configuration of zlog is: \end_layout \begin_layout LyX-Code *.* "aa.log", 100MB ~ "aa-%d(%Y%m%d).#2s.log" \end_layout \begin_layout LyX-Code Do rotation every 100MB. The archive file name also supports conversion strings. #2s means the sequence number is at least 2 bytes wide. Sequence from 00. That's the most complex way to archive in zlog. \end_layout \end_deeper \begin_layout Enumerate Compress, move and delete old archive. \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout Standard Compress should not be done by the logging library, because compress need time and CPU. The mission of the logging library is to cooperate with compress programs. \end_layout \begin_layout Standard For the 3 ways to split logs, way 1 and way 3 are easy to manage. It is easy to find old log file by name or by modify time. And then compress, move and delete old log files by crontab and shell. \end_layout \begin_layout Standard For second way, compress is useless, delete is needed and zlog already supports it. \end_layout \begin_layout Standard If you really want to rotate and compress log file at the same time, I suggest logrotate. It is an independent program and will not confuse the situation. \end_layout \end_deeper \begin_layout Enumerate zlog support for external tools like logrotate. \end_layout \begin_layout Standard The rotation support of zlog is very powerful, still there are several cases zlog can not handle. Like rotation by time, before or after rotation call some user-defined shells. That will make zlog too complex. \end_layout \begin_layout Standard Under these circumstances, consider using external tools like logrotate. On linux, the problem is that when a tool renames the log file, the working process which uses an inode to reference the file will not automatically reopen the new file. The standard way is send a signal to the program and let it reopen the file. For syslogd the command is: \end_layout \begin_layout LyX-Code kill -SIGHUP `cat /var/run/syslogd.pid` \end_layout \begin_layout Standard For zlog as a library, it is not good to receive signals. zlog provide zlog_reload(), which reloads the configuration file and reopens all log files. So if you write a program and want to reopen a log file manually, you can write some code to do the job like this: after receiving a signal or command from client, call zlog_reload(). \end_layout \begin_layout Section Configure File Tools \end_layout \begin_layout LyX-Code $ zlog-chk-conf -h \end_layout \begin_layout LyX-Code Useage: zlog-chk-conf [conf files]... \end_layout \begin_layout LyX-Code -q, suppress non-error message \end_layout \begin_layout LyX-Code -h, show help message \end_layout \begin_layout Standard zlog-chk-conf tries to read configuration files, check their syntax, and output to screen whether it is correct or not. I suggest using this tool each time you create or change a configuration file. It will output like this \end_layout \begin_layout LyX-Code $ ./zlog-chk-conf zlog.conf \end_layout \begin_layout LyX-Code 03-08 15:35:44 ERROR (10595:rule.c:391) sscanf [aaa] fail, category or level is null \end_layout \begin_layout LyX-Code 03-08 15:35:44 ERROR (10595:conf.c:155) zlog_rule_new fail [aaa] \end_layout \begin_layout LyX-Code 03-08 15:35:44 ERROR (10595:conf.c:258) parse configure file[zlog.conf] line[126] fail \end_layout \begin_layout LyX-Code 03-08 15:35:44 ERROR (10595:conf.c:306) zlog_conf_read_config fail \end_layout \begin_layout LyX-Code 03-08 15:35:44 ERROR (10595:conf.c:366) zlog_conf_build fail \end_layout \begin_layout LyX-Code 03-08 15:35:44 ERROR (10595:zlog.c:66) conf_file[zlog.conf], init conf fail \end_layout \begin_layout LyX-Code 03-08 15:35:44 ERROR (10595:zlog.c:131) zlog_init_inner[zlog.conf] fail \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code ---[zlog.conf] syntax error, see error message above \end_layout \begin_layout Standard This example tells you that [aaa] is not a correct rule and that line 126 in the configuration file is wrong. Later failure reports result from that fundamental failure. \end_layout \begin_layout LyX-Code \end_layout \begin_layout Chapter zlog API \end_layout \begin_layout Standard All zlog APIs are thread safe. To use them, you just need to \end_layout \begin_layout LyX-Code #include "zlog.h" \end_layout \begin_layout Section initialize and finish \end_layout \begin_layout Labeling \labelwidthstring 00.00.0000 SYNOPSIS \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout LyX-Code int zlog_init(const char * \bar under confpath \bar default ); \end_layout \begin_layout LyX-Code int zlog_reload(const char * \bar under confpath \bar default ); \end_layout \begin_layout LyX-Code void zlog_fini(void); \end_layout \end_deeper \begin_layout Labeling \labelwidthstring 00.00.0000 DESCRIPTION \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout Standard zlog_init() reads configuration from the file \bar under confpath \bar default . If \bar under confpath \bar default is NULL, it looks for the environment variable ZLOG_CONF_PATH to find the configuration file. If $ZLOG_CONF_PATH is NULL also, all logs will be output to stdout with an internal format. Only the first call to zlog_init() per process is effective, subsequent calls will fail and do nothing. \end_layout \begin_layout Standard zlog_reload() is designed to reload the configuration file. From the \bar under confpath \bar default it re-calculates the category-rule relationships, rebuilds thread buffers, and resets user-defined output function rules. It can be called at runtime when the configuration file is changed or you wish to use another configuration file. It can be called any number of times. If \bar under confpath \bar default is NULL, it reloads the last configuration file that zlog_init() or zlog_reload () specified. If zlog_reload() failed, the current configuration in memory will remain unchanged. So zlog_reload() is atomic. \end_layout \begin_layout Standard zlog_fini() releases all zlog API \emph on \emph default memory and closes opened files. It can be called any number of times. \end_layout \end_deeper \begin_layout Labeling \labelwidthstring 00.00.0000 RETURN VALUE \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout Standard On success , zlog_init() and zlog_reload() return zero. On error, zlog_init() and zlog_reload() return -1, and a detailed error log will be recorded to the log file indicated by ZLOG_PROFILE_ERROR. \end_layout \end_deeper \begin_layout Section category operation \end_layout \begin_layout Labeling \labelwidthstring 00.00.0000 SYNOPSIS \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout LyX-Code typedef struct zlog_category_s zlog_category_t; \end_layout \begin_layout LyX-Code zlog_category_t *zlog_get_category(const char * \bar under cname \bar default ); \end_layout \end_deeper \begin_layout Labeling \labelwidthstring 00.00.0000 DESCRIPTION \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout Standard zlog_get_category() gets a category from zlog's category_table for a future log action. If the category cname does not exist it will be created. Then zlog goes through all rules as determined by the configuration. It returns a pointer to matched rules corresponding to \bar under cname \bar default . \end_layout \begin_layout Standard This is how category string in rules matches \bar under cname \bar default : \end_layout \begin_layout Enumerate * matches all \bar under cname \bar default . \end_layout \begin_layout Enumerate category string which ends with underscore "_" matches super-category and sub-categories. For example, "aa_" matches \bar under cname \bar default like "aa", "aa_", "aa_bb", "aa_bb_cc". \end_layout \begin_layout Enumerate category string which does not end with underscore "_"matches \bar under cname \bar default accurately. For example, "aa_bb" matches only a \bar under cname \bar default of "aa_bb". \end_layout \begin_layout Enumerate ! matches \bar under cname \bar default that has no rule matched. \end_layout \begin_layout Standard The rules for each category will be automatically re-calculated when zlog_reload () is called. No need to worry about category's memory release, \emph on \emph default zlog_fini() will clean up at the end. \end_layout \end_deeper \begin_layout Labeling \labelwidthstring 00.00.0000 RETURN VALUE \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout Standard On success, return the address of zlog_category_t. On error, return NULL, and a detailed error log will be recorded to the log file indicated by ZLOG_PROFILE_ERROR. \end_layout \end_deeper \begin_layout Section log functions and macros \end_layout \begin_layout Labeling \labelwidthstring 00.00.0000 SYNOPSIS \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout LyX-Code void zlog(zlog_category_t * \bar under category \bar default , \end_layout \begin_layout LyX-Code const char * \bar under file \bar default , size_t \bar under filelen \bar default , \end_layout \begin_layout LyX-Code const char * \bar under func \bar default , size_t \bar under funclen \bar default , \end_layout \begin_layout LyX-Code long \bar under line \bar default , int \bar under level \bar default , \end_layout \begin_layout LyX-Code const char * \bar under format \bar default , ...); \end_layout \begin_layout LyX-Code void vzlog(zlog_category_t * \bar under category \bar default , \end_layout \begin_layout LyX-Code const char * \bar under file \bar default , size_t \bar under filelen \bar default , \end_layout \begin_layout LyX-Code const char * \bar under func \bar default , size_t \bar under funclen \bar default , \end_layout \begin_layout LyX-Code long \bar under line \bar default , int \bar under level \bar default , \end_layout \begin_layout LyX-Code const char * \bar under format \bar default , va_list \bar under args \bar default ); \end_layout \begin_layout LyX-Code void hzlog(zlog_category_t * \bar under category \bar default , \end_layout \begin_layout LyX-Code const char * \bar under file \bar default , size_t \bar under filelen \bar default , \end_layout \begin_layout LyX-Code const char * \bar under func \bar default , size_t \bar under funclen \bar default , \end_layout \begin_layout LyX-Code long \bar under line \bar default , int \bar under level \bar default , \end_layout \begin_layout LyX-Code const void * \bar under buf \bar default , size_t \bar under buflen \bar default ); \end_layout \begin_layout LyX-Code \end_layout \end_deeper \begin_layout Labeling \labelwidthstring 00.00.0000 DESCRIPTION \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout Standard These 3 functions are the real log functions producing user messages, which correspond to %m is configuration file entries. \bar under category \bar default comes from zlog_get_category() described above. \end_layout \begin_layout Standard zlog() and vzlog() \emph on \emph default produce output according to a \bar under format \bar default like printf(3) and vprintf(3). \end_layout \begin_layout Standard vzlog() \emph on \emph default is equivalent to zlog(), except that it is called with a va_list instead of a variable number of arguments. vzlog() invokes the va_copy macro, the value of \bar under args \bar default remain unchanged after the call. See stdarg(3). \end_layout \begin_layout Standard hzlog() \emph on \emph default is a little different, it produce output like this, the hexadecimal representati on of \bar under buf \emph on \bar default \emph default and output len is \bar under buf_len \end_layout \begin_layout LyX-Code hex_buf_len=[5365] \end_layout \begin_layout LyX-Code 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDE F \end_layout \begin_layout LyX-Code 0000000001 23 21 20 2f 62 69 6e 2f 62 61 73 68 0a 0a 23 20 #! /bin/bash..# \end_layout \begin_layout LyX-Code 0000000002 74 65 73 74 5f 68 65 78 20 2d 20 74 65 6d 70 6f test_hex - tempo \end_layout \begin_layout LyX-Code 0000000003 72 61 72 79 20 77 72 61 70 70 65 72 20 73 63 72 rary wrapper scr \end_layout \begin_layout Standard The parameter \bar under file \bar default and \bar under line \bar default are usually filled by the __FILE__ and __LINE__ macros. These indicate where the log event happened. The parameter \bar under func \bar default is filled with __func__ or __FUNCTION__, if the compiler supports it, otherwise it will be filled with "". \end_layout \begin_layout Standard \bar under level \emph on \bar default \emph default is an int in the current level list, which defaults to: \end_layout \begin_layout LyX-Code typedef enum { \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout LyX-Code ZLOG_LEVEL_DEBUG = 20, \end_layout \begin_layout LyX-Code ZLOG_LEVEL_INFO = 40, \end_layout \begin_layout LyX-Code ZLOG_LEVEL_NOTICE = 60, \end_layout \begin_layout LyX-Code ZLOG_LEVEL_WARN = 80, \end_layout \begin_layout LyX-Code ZLOG_LEVEL_ERROR = 100, \end_layout \begin_layout LyX-Code ZLOG_LEVEL_FATAL = 120 \end_layout \end_deeper \begin_layout LyX-Code } zlog_level; \end_layout \begin_layout Standard Each fuction has its macros for easy use. For example, \end_layout \begin_layout LyX-Code #define zlog_fatal(cat, format, args...) \backslash \end_layout \begin_layout LyX-Code zlog(cat, __FILE__, sizeof(__FILE__)-1, \backslash \end_layout \begin_layout LyX-Code __func__, sizeof(__func__)-1, __LINE__, \backslash \end_layout \begin_layout LyX-Code ZLOG_LEVEL_FATAL, format, ##args) \end_layout \begin_layout Standard The full list of macros is: \end_layout \begin_layout LyX-Code /* zlog macros */ \end_layout \begin_layout LyX-Code /* zlog macros */ \end_layout \begin_layout LyX-Code zlog_fatal(cat, format, ...) \end_layout \begin_layout LyX-Code zlog_error(cat, format, ...) \end_layout \begin_layout LyX-Code zlog_warn(cat, format, ...) \end_layout \begin_layout LyX-Code zlog_notice(cat, format, ...) \end_layout \begin_layout LyX-Code zlog_info(cat, format, ...) \end_layout \begin_layout LyX-Code zlog_debug(cat, format, ...) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code /* vzlog macros */ \end_layout \begin_layout LyX-Code vzlog_fatal(cat, format, args) \end_layout \begin_layout LyX-Code vzlog_error(cat, format, args) \end_layout \begin_layout LyX-Code vzlog_warn(cat, format, args) \end_layout \begin_layout LyX-Code vzlog_notice(cat, format, args) \end_layout \begin_layout LyX-Code vzlog_info(cat, format, args) \end_layout \begin_layout LyX-Code vzlog_debug(cat, format, args) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code /* hzlog macros */ \end_layout \begin_layout LyX-Code hzlog_fatal(cat, buf, buf_len) \end_layout \begin_layout LyX-Code hzlog_error(cat, buf, buf_len) \end_layout \begin_layout LyX-Code hzlog_warn(cat, buf, buf_len) \end_layout \begin_layout LyX-Code hzlog_notice(cat, buf, buf_len) \end_layout \begin_layout LyX-Code hzlog_info(cat, buf, buf_len) \end_layout \begin_layout LyX-Code hzlog_debug(cat, buf, buf_len) \end_layout \end_deeper \begin_layout Labeling \labelwidthstring 00.00.0000 RETURN VALUE \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout Standard These functions return nothing. But if anything wrong happens, a detailed error log will be recorded to the log file indicated by ZLOG_PROFILE_ERROR. \end_layout \end_deeper \begin_layout Section MDC operation \end_layout \begin_layout Labeling \labelwidthstring 00.00.0000 SYNOPSIS \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout LyX-Code int zlog_put_mdc(const char * \bar under key \bar default , const char * \bar under value \bar default ); \end_layout \begin_layout LyX-Code char *zlog_get_mdc(const char * \bar under key \bar default ); \end_layout \begin_layout LyX-Code void zlog_remove_mdc(const char * \bar under key \bar default ); \end_layout \begin_layout LyX-Code void zlog_clean_mdc(void); \end_layout \end_deeper \begin_layout Labeling \labelwidthstring 00.00.0000 DESCRIPTION \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout Standard MDC (Mapped Diagnostic Context) is a thread key-value map, and has nothing to do with category. \end_layout \begin_layout Standard \bar under key \bar default and \bar under value \bar default are all strings, which should be no longer than MAXLEN_PATH(1024). If the input is longer than MAXLEN_PATH(1024), the input will be truncated. \end_layout \begin_layout Standard One thing you should remember is that the map bonds to a thread, thus if you set a key-value pair in one thread, it will not affect other threads. \end_layout \end_deeper \begin_layout Labeling \labelwidthstring 00.00.0000 RETURN VALUE \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout Standard zlog_put_mdc() returns 0 for success, -1 for fail. zlog_get_mdc() returns a pointer to \bar under value \bar default for success, NULL for fail or key not exist. If anything wrong happens, a detailed error log will be recorded to the log file indicated by ZLOG_PROFILE_ERROR. \end_layout \end_deeper \begin_layout Section dzlog API \begin_inset CommandInset label LatexCommand label name "sec:dzlog-API" \end_inset \end_layout \begin_layout Labeling \labelwidthstring 00.00.0000 SYNOPSIS \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout LyX-Code int dzlog_init(const char * \bar under confpath \bar default , const char * \bar under cname \bar default ); \end_layout \begin_layout LyX-Code int dzlog_set_category(const char * \bar under cname \bar default ); \end_layout \begin_layout LyX-Code void dzlog(const char * \bar under file \bar default , size_t \bar under filelen \bar default , \end_layout \begin_layout LyX-Code const char * \bar under func \bar default , size_t \bar under funclen \bar default , \end_layout \begin_layout LyX-Code long \bar under line \bar default , int \bar under level \bar default , \end_layout \begin_layout LyX-Code const char * \bar under format \bar default , ...); \end_layout \begin_layout LyX-Code void vdzlog(const char * \bar under file \bar default , size_t \bar under filelen \bar default , \end_layout \begin_layout LyX-Code const char * \bar under func \bar default , size_t \bar under funclen \bar default , \end_layout \begin_layout LyX-Code long \bar under line \bar default , int \bar under level \bar default , \end_layout \begin_layout LyX-Code const char * \bar under format \bar default , va_list \bar under args \bar default ); \end_layout \begin_layout LyX-Code void hdzlog(const char * \bar under file \bar default , size_t \bar under filelen \bar default , \end_layout \begin_layout LyX-Code const char * \bar under func \bar default , size_t \bar under funclen \bar default , \end_layout \begin_layout LyX-Code long \bar under line \bar default , int \bar under level \bar default , \end_layout \begin_layout LyX-Code const void * \bar under buf \bar default , size_t \bar under buflen \bar default ); \end_layout \end_deeper \begin_layout Labeling \labelwidthstring 00.00.0000 DESCRIPTION \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout Standard dzlog consists of simple functions that omit \emph on zlog_category_t \emph default . It uses a default category internally and puts the category under lock protection. It is thread safe also. Omitting the category means that users need not create, save, or transfer \emph on zlog_category_t \emph default variables. Still, you can get and use other category values at the same time through the normal API for flexibility. \end_layout \begin_layout Standard dzlog_init() is just as zlog_init(), but needs a \bar under cname \bar default for the internal default category. zlog_reload() and zlog_fini() can be used as before, to refresh conf_file, or release all. \end_layout \begin_layout Standard dzlog_set_category() \emph on \emph default is designed for changing the default category. The last default category is replaced by the new one. Don't worry about releasing memory since all category allocations will be cleaned up at zlog_fini(). \end_layout \begin_layout Standard Macros are defined in zlog.h. They are the general way in simple logging. \end_layout \begin_layout LyX-Code dzlog_fatal(format, ...) \end_layout \begin_layout LyX-Code dzlog_error(format, ...) \end_layout \begin_layout LyX-Code dzlog_warn(format, ...) \end_layout \begin_layout LyX-Code dzlog_notice(format, ...) \end_layout \begin_layout LyX-Code dzlog_info(format, ...) \end_layout \begin_layout LyX-Code dezlog_debug(format, ...) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code vdzlog_fatal(format, args) \end_layout \begin_layout LyX-Code vdzlog_error(format, args) \end_layout \begin_layout LyX-Code vdzlog_warn(format, args) \end_layout \begin_layout LyX-Code vdzlog_notice(format, args) \end_layout \begin_layout LyX-Code vdzlog_info(format, args) \end_layout \begin_layout LyX-Code vdzlog_debug(format, args) \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code hdzlog_fatal(buf, buf_len) \end_layout \begin_layout LyX-Code hdzlog_error(buf, buf_len) \end_layout \begin_layout LyX-Code hdzlog_warn(buf, buf_len) \end_layout \begin_layout LyX-Code hdzlog_noticebuf, buf_len) \end_layout \begin_layout LyX-Code hdzlog_info(buf, buf_len) \end_layout \begin_layout LyX-Code hdzlog_debug(buf, buf_len) \end_layout \end_deeper \begin_layout Labeling \labelwidthstring 00.00.0000 RETURN VALUE \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout Standard On success, dzlog_init() and dzlog_set_category() return zero. On error, dzlog_init() and dzlog_set_category() return -1, and a detailed error log will be recorded to the log file indicated by ZLOG_PROFILE_ERROR. \end_layout \end_deeper \begin_layout Section User-defined Output \end_layout \begin_layout Labeling \labelwidthstring 00.00.0000 SYNOPSIS \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout LyX-Code typedef struct zlog_msg_s { \end_layout \begin_layout LyX-Code char *buf; \end_layout \begin_layout LyX-Code size_t len; \end_layout \begin_layout LyX-Code char *path; \end_layout \begin_layout LyX-Code } zlog_msg_t; \end_layout \begin_layout LyX-Code typedef int (*zlog_record_fn)(zlog_msg_t * \bar under msg \bar default ); \end_layout \begin_layout LyX-Code int zlog_set_record(const char * \bar under rname \bar default , zlog_record_fn \bar under record \bar default ); \end_layout \end_deeper \begin_layout Labeling \labelwidthstring 00.00.0000 DESCRIPTION \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout Standard zlog allows a user to define an output function. The output function bonds to a special kind of rule in configuration file. A typical rule is: \end_layout \begin_layout LyX-Code *.* $name, "record path %c %d"; simple \end_layout \begin_layout Standard zlog_set_record() does the bonding operation. Rules with the $ \bar under rname \bar default will be output through user-defined function \bar under record \bar default . The callback function has the type of zlog_record_fn. \end_layout \begin_layout Standard The members of struct zlog_msg_t are decribed below: \end_layout \begin_layout Standard \bar under path \bar default comes from the second parameter of rule after $name, which is generated dynamically like the file path. \end_layout \begin_layout Standard \bar under buf \bar default and \bar under len \bar default are zlog formatted log message and its length. \end_layout \begin_layout Standard All settings of zlog_set_record() are kept available after zlog_reload(). \end_layout \end_deeper \begin_layout Labeling \labelwidthstring 00.00.0000 RETURN VALUE \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout Standard On success, zlog_set_record() returns zero. On error, it returns -1, and a detailed error log will be recorded to the log file indicated by ZLOG_PROFILE_ERROR. \end_layout \end_deeper \begin_layout Section debug and profile \end_layout \begin_layout Labeling \labelwidthstring 00.00.0000 SYNOPSIS \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout LyX-Code void zlog_profile(void); \end_layout \end_deeper \begin_layout Labeling \labelwidthstring 00.00.0000 DESCRIPTION \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout Standard environment variable ZLOG_PROFILE_ERROR indicates zlog's error log path. \end_layout \begin_layout Standard environment variable ZLOG_PROFILE_DEBUG indicates zlog's debug log path. \end_layout \begin_layout Standard zlog_profile() prints all information in memory to zlog's error log file at runtime. You can compare it to the configuration file to find possible errors. \end_layout \end_deeper \begin_layout Chapter Advanced Usage \end_layout \begin_layout Section MDC \begin_inset CommandInset label LatexCommand label name "sec:MDC" \end_inset \end_layout \begin_layout Standard What is MDC? In log4j it is short for Mapped Diagnostic Context. That sounds like a complicated terminology. MDC is just a key-value map. Once you set it by function, the zlog library will print it to file every time a log event happens, or become part of log file path. Let's see an example in $(top_builddir)/test/test_mdc.c. \end_layout \begin_layout LyX-Code $ cat test_mdc.c \end_layout \begin_layout LyX-Code #include \end_layout \begin_layout LyX-Code #include \end_layout \begin_layout LyX-Code #include \end_layout \begin_layout LyX-Code #include \end_layout \begin_layout LyX-Code #include \end_layout \begin_layout LyX-Code #include "zlog.h" \end_layout \begin_layout LyX-Code int main(int argc, char** argv) \end_layout \begin_layout LyX-Code { \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout LyX-Code int rc; \end_layout \begin_layout LyX-Code zlog_category_t *zc; \end_layout \begin_layout LyX-Code rc = zlog_init("test_mdc.conf"); \end_layout \begin_layout LyX-Code if (rc) { \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout LyX-Code printf("init failed \backslash n"); \end_layout \begin_layout LyX-Code return -1; \end_layout \end_deeper \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code zc = zlog_get_category("my_cat"); \end_layout \begin_layout LyX-Code if (!zc) { \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout LyX-Code printf("get cat fail \backslash n"); \end_layout \begin_layout LyX-Code zlog_fini(); \end_layout \begin_layout LyX-Code return -2; \end_layout \end_deeper \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code zlog_info(zc, "1.hello, zlog"); \end_layout \begin_layout LyX-Code zlog_put_mdc("myname", "Zhang"); \end_layout \begin_layout LyX-Code zlog_info(zc, "2.hello, zlog"); \end_layout \begin_layout LyX-Code zlog_put_mdc("myname", "Li"); \end_layout \begin_layout LyX-Code zlog_info(zc, "3.hello, zlog"); \end_layout \begin_layout LyX-Code zlog_fini(); \end_layout \begin_layout LyX-Code return 0; \end_layout \end_deeper \begin_layout LyX-Code } \end_layout \begin_layout Standard The configure file is \end_layout \begin_layout LyX-Code $ cat test_mdc.conf \end_layout \begin_layout LyX-Code [formats] \end_layout \begin_layout LyX-Code mdc_format= "%d.%ms %-6V (%c:%F:%L) [%M(myname)] - %m%n" \end_layout \begin_layout LyX-Code [rules] \end_layout \begin_layout LyX-Code *.* >stdout; mdc_format \end_layout \begin_layout Standard And the output is \end_layout \begin_layout LyX-Code $ ./test_mdc \end_layout \begin_layout LyX-Code 2012-03-12 09:26:37.740 INFO (my_cat:test_mdc.c:47) [] - 1.hello, zlog \end_layout \begin_layout LyX-Code 2012-03-12 09:26:37.740 INFO (my_cat:test_mdc.c:51) [Zhang] - 2.hello, zlog \end_layout \begin_layout LyX-Code 2012-03-12 09:26:37.740 INFO (my_cat:test_mdc.c:55) [Li] - 3.hello, zlog \end_layout \begin_layout Standard You can see zlog_put_mdc() function sets the map with key "myname" and value "Zhang", and in configuration file \emph on %M(myname) \emph default indicates where the value shows in each log. The second time, value of key "myname" is overwritten to "Li", and the log changes also. \end_layout \begin_layout Standard When should MDC be used? That mainly depends on when a user need to separate same log action with different scenarios. For example, in .c \end_layout \begin_layout LyX-Code zlog_put_mdc("customer_name", get_customer_name_from_db() ); \end_layout \begin_layout LyX-Code zlog_info("get in"); \end_layout \begin_layout LyX-Code zlog_info("pick product"); \end_layout \begin_layout LyX-Code zlog_info("pay"); \end_layout \begin_layout LyX-Code zlog_info("get out"); \end_layout \begin_layout Standard in .conf \end_layout \begin_layout LyX-Code &format "%M(customer_name) %m%n" \end_layout \begin_layout Standard When the program processes two customers at the same time, the output could be: \end_layout \begin_layout LyX-Code Zhang get in \end_layout \begin_layout LyX-Code Li get in \end_layout \begin_layout LyX-Code Zhang pick product \end_layout \begin_layout LyX-Code Zhang pay \end_layout \begin_layout LyX-Code Li pick product \end_layout \begin_layout LyX-Code Li pay \end_layout \begin_layout LyX-Code Zhang get out \end_layout \begin_layout LyX-Code Li get out \end_layout \begin_layout Standard Now you can distinguish one customer from another, by using grep afterwards \end_layout \begin_layout LyX-Code $ grep Zhang aa.log > Zhang.log \end_layout \begin_layout LyX-Code $ grep Li aa.log >Li.log \end_layout \begin_layout Standard Or, another way is to seperate them to different log file when log action is taken. In .conf \end_layout \begin_layout LyX-Code *.* "mdc_%M(customer_name).log"; \end_layout \begin_layout Standard It will produce 3 logs \end_layout \begin_layout LyX-Code mdc_.log mdc_Zhang.log mdc_Li.log \end_layout \begin_layout Standard That's a quick way, if you know what you are doing. \end_layout \begin_layout Standard In MDC, the map belongs to a thread and each thread has it's own map. In one thread zlog_mdc_put() will not affect other thread's map. Still, if you want to distinguish one thread from another, using the %t conversion character is enough. \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code \end_layout \begin_layout Section Profile zlog Itself \begin_inset CommandInset label LatexCommand label name "sec:Profile-zlog-Itself" \end_inset \end_layout \begin_layout Standard Until this point we have assumed that the zlog library never fails. It helps the application to write log entries and to debug the application. But if zlog itself has some problem, how can we find out? Other programs debug through the log library so how can a log library debug itself? The answer is the same, zlog library has its own log. This profile log is usually closed, and can be opened by setting environment variables. \end_layout \begin_layout LyX-Code $ export ZLOG_PROFILE_DEBUG=/tmp/zlog.debug.log \end_layout \begin_layout LyX-Code $ export ZLOG_PROFILE_ERROR=/tmp/zlog.error.log \end_layout \begin_layout Standard profile log has just 2 levels, debug and error. After setting them, run test_hello program in \begin_inset CommandInset ref LatexCommand ref reference "sec:Hello-World-Example" \end_inset , and the debug log will be \end_layout \begin_layout LyX-Code $ more zlog.debug.log \end_layout \begin_layout LyX-Code 03-13 09:46:56 DEBUG (7503:zlog.c:115) ------zlog_init start, compile time[Mar 13 2012 11:28:56]------ \end_layout \begin_layout LyX-Code 03-13 09:46:56 DEBUG (7503:spec.c:825) spec:[0x7fdf96b7c010][%d(%F %T)][%F %T 29][] \end_layout \begin_layout LyX-Code 03-13 09:46:56 DEBUG (7503:spec.c:825) spec:[0x7fdf96b52010][ ][ 0][] \end_layout \begin_layout LyX-Code ...... \end_layout \begin_layout LyX-Code 03-13 09:52:40 DEBUG (8139:zlog.c:291) ------zlog_fini end------ \end_layout \begin_layout Standard zlog.error.log is not created, as no error occurs. \end_layout \begin_layout Standard As you can see, debug log shows how zlog is inited and finished, but no debug log is written when zlog_info() is executed. That's for efficency. \end_layout \begin_layout Standard If there is anything wrong with zlog library, all will show in zlog.error.log. For example, using a wrong printf syntax in zlog() \end_layout \begin_layout LyX-Code zlog_info(zc, "%l", 1); \end_layout \begin_layout Standard Then run the program, the zlog.error.log should be \end_layout \begin_layout LyX-Code $ cat zlog.error.log \end_layout \begin_layout LyX-Code 03-13 10:04:58 ERROR (10102:buf.c:189) vsnprintf fail, errno[0] \end_layout \begin_layout LyX-Code 03-13 10:04:58 ERROR (10102:buf.c:191) nwrite[-1], size_left[1024], format[%l] \end_layout \begin_layout LyX-Code 03-13 10:04:58 ERROR (10102:spec.c:329) zlog_buf_vprintf maybe fail or overflow \end_layout \begin_layout LyX-Code 03-13 10:04:58 ERROR (10102:spec.c:467) a_spec->gen_buf fail \end_layout \begin_layout LyX-Code 03-13 10:04:58 ERROR (10102:format.c:160) zlog_spec_gen_msg fail \end_layout \begin_layout LyX-Code 03-13 10:04:58 ERROR (10102:rule.c:265) zlog_format_gen_msg fail \end_layout \begin_layout LyX-Code 03-13 10:04:58 ERROR (10102:category.c:164) hzb_log_rule_output fail \end_layout \begin_layout LyX-Code 03-13 10:04:58 ERROR (10102:zlog.c:632) zlog_output fail, srcfile[test_hello.c], srcline[41] \end_layout \begin_layout Standard Now, you could find the reason why the expected log doesn't generate, and fix the wrong printf syntax. \end_layout \begin_layout Standard Runtime profiling causes a loss of efficency. Normally, I keep ZLOG_PROFILE_ERROR on and ZLOG_PROFILE_DEBUG off in my environment. \end_layout \begin_layout Standard There is another way to profile the zlog library. zlog_init() reads the configuration file into memory. Throughout all log actions, the configure structure remains unchanged. There is possibility that this memory is damaged by other functions in an application, or the memory doesn't equal what the configuration file describes. So there is a function to show this memory at runtime and print it to ZLOG_PROF ILE_ERROR. \end_layout \begin_layout Standard see $(top_builddir)/test/test_profile.c \end_layout \begin_layout LyX-Code $ cat test_profile.c \end_layout \begin_layout LyX-Code #include \end_layout \begin_layout LyX-Code #include "zlog.h" \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code int main(int argc, char** argv) \end_layout \begin_layout LyX-Code { \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout LyX-Code int rc; \end_layout \begin_layout LyX-Code rc = dzlog_init("test_profile.conf", "my_cat"); \end_layout \begin_layout LyX-Code if (rc) { \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout LyX-Code printf("init failed \backslash n"); \end_layout \begin_layout LyX-Code return -1; \end_layout \end_deeper \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code dzlog_info("hello, zlog"); \end_layout \begin_layout LyX-Code zlog_profile(); \end_layout \begin_layout LyX-Code zlog_fini(); \end_layout \begin_layout LyX-Code return 0; \end_layout \end_deeper \begin_layout LyX-Code } \end_layout \begin_layout Standard zlog_profile() is the function. The configuration file is simple \end_layout \begin_layout LyX-Code $ cat test_profile.conf \end_layout \begin_layout LyX-Code [formats] \end_layout \begin_layout LyX-Code simple = "%m%n" \end_layout \begin_layout LyX-Code [rules] \end_layout \begin_layout LyX-Code my_cat.* >stdout; simple \end_layout \begin_layout Standard Then zlog.error.log is \end_layout \begin_layout LyX-Code $ cat /tmp/zlog.error.log \end_layout \begin_layout LyX-Code 06-01 11:21:26 WARN (7063:zlog.c:783) ------zlog_profile start------ \end_layout \begin_layout LyX-Code 06-01 11:21:26 WARN (7063:zlog.c:784) init_flag:[1] \end_layout \begin_layout LyX-Code 06-01 11:21:26 WARN (7063:conf.c:75) -conf[0x2333010]- \end_layout \begin_layout LyX-Code 06-01 11:21:26 WARN (7063:conf.c:76) --global-- \end_layout \begin_layout LyX-Code 06-01 11:21:26 WARN (7063:conf.c:77) ---file[test_profile.conf],mtime[2012-06-01 11:20:44]--- \end_layout \begin_layout LyX-Code 06-01 11:21:26 WARN (7063:conf.c:78) ---strict init[1]--- \end_layout \begin_layout LyX-Code 06-01 11:21:26 WARN (7063:conf.c:79) ---buffer min[1024]--- \end_layout \begin_layout LyX-Code 06-01 11:21:26 WARN (7063:conf.c:80) ---buffer max[2097152]--- \end_layout \begin_layout LyX-Code 06-01 11:21:26 WARN (7063:conf.c:82) ---default_format--- \end_layout \begin_layout LyX-Code 06-01 11:21:26 WARN (7063:format.c:48) ---format[0x235ef60][default = %d(%F %T) %V [%p:%F:%L] %m%n(0x233b810)]--- \end_layout \begin_layout LyX-Code 06-01 11:21:26 WARN (7063:conf.c:85) ---file perms[0600]--- \end_layout \begin_layout LyX-Code 06-01 11:21:26 WARN (7063:conf.c:87) ---rotate lock file[/tmp/zlog.lock]--- \end_layout \begin_layout LyX-Code 06-01 11:21:26 WARN (7063:rotater.c:48) --rotater[0x233b7d0][0x233b7d0,/tmp/zlog. lock,4]-- \end_layout \begin_layout LyX-Code 06-01 11:21:26 WARN (7063:level_list.c:37) --level_list[0x2335490]-- \end_layout \begin_layout LyX-Code 06-01 11:21:26 WARN (7063:level.c:37) ---level[0x23355c0][0,*,*,1,6]--- \end_layout \begin_layout LyX-Code 06-01 11:21:26 WARN (7063:level.c:37) ---level[0x23375e0][20,DEBUG,debug,5,7]--- \end_layout \begin_layout LyX-Code 06-01 11:21:26 WARN (7063:level.c:37) ---level[0x2339600][40,INFO,info,4,6]--- \end_layout \begin_layout LyX-Code 06-01 11:21:26 WARN (7063:level.c:37) ---level[0x233b830][60,NOTICE,notice,6,5]- -- \end_layout \begin_layout LyX-Code 06-01 11:21:26 WARN (7063:level.c:37) ---level[0x233d850][80,WARN,warn,4,4]--- \end_layout \begin_layout LyX-Code 06-01 11:21:26 WARN (7063:level.c:37) ---level[0x233fc80][100,ERROR,error,5,3]-- - \end_layout \begin_layout Section User-defined Level \begin_inset CommandInset label LatexCommand label name "sec:User-defined-Level" \end_inset \end_layout \begin_layout Standard Here are all the steps to define your own levels. \end_layout \begin_layout Enumerate Define levels in the configuration file. \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout LyX-Code $ cat $(top_builddir)/test/test_level.conf \end_layout \begin_layout LyX-Code [global] \end_layout \begin_layout LyX-Code default format = "%V %v %m%n" \end_layout \begin_layout LyX-Code [levels] \end_layout \begin_layout LyX-Code TRACE = 30, LOG_DEBUG \end_layout \begin_layout LyX-Code [rules] \end_layout \begin_layout LyX-Code my_cat.TRACE >stdout; \end_layout \begin_layout Standard The internal default levels are (no need to write them in the conf file): \end_layout \begin_layout LyX-Code DEBUG = 20, LOG_DEBUG \end_layout \begin_layout LyX-Code INFO = 40, LOG_INFO \end_layout \begin_layout LyX-Code NOTICE = 60, LOG_NOTICE \end_layout \begin_layout LyX-Code WARN = 80, LOG_WARNING \end_layout \begin_layout LyX-Code ERROR = 100, LOG_ERR \end_layout \begin_layout LyX-Code FATAL = 120, LOG_ALERT \end_layout \begin_layout LyX-Code UNKNOWN = 254, LOG_ERR \end_layout \begin_layout Standard In zlog, an integer(30) and a level string(TRACE) represent a level. Note that this integer must be in [1,253], any other number is illegal. Higher numbers are more important. TRACE is more important than DEBUG(30>20), and less important than INFO(30<40). After the definition, TRACE can be used in rule of configure file. This sentence \end_layout \begin_layout LyX-Code my_cat.TRACE >stdout; \end_layout \begin_layout Standard means that level >= TRACE, which is TRACE, INFO, NOTICE, WARN, ERROR, FATAL will be written to standard output. \end_layout \begin_layout Standard The conversion charactor %V of format string generates the uppercase value of the level string and %v generates the lowercase value of the level string. \end_layout \begin_layout Standard In the level definition LOG_DEBUG means when using >syslog in a rule, all TRACE log will output as syslog' s LOG_DEBUG level. \end_layout \end_deeper \begin_layout Enumerate Using the new log level in source file, the direct way is like this \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout LyX-Code zlog(cat, __FILE__, sizeof(__FILE__)-1, \backslash \end_layout \begin_layout LyX-Code __func__, sizeof(__func__)-1,__LINE__, \backslash \end_layout \begin_layout LyX-Code 30, "test %d", 1); \end_layout \begin_layout Standard For easy use, create a .h file \end_layout \begin_layout LyX-Code $ cat $(top_builddir)/test/test_level.h \end_layout \begin_layout LyX-Code #ifndef __test_level_h \end_layout \begin_layout LyX-Code #define __test_level_h \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code #include "zlog.h" \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code enum { \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout LyX-Code ZLOG_LEVEL_TRACE = 30, \end_layout \begin_layout LyX-Code /* must equals conf file setting */ \end_layout \end_deeper \begin_layout LyX-Code }; \end_layout \begin_layout LyX-Code #define zlog_trace(cat, format, ...) \backslash \end_layout \begin_layout LyX-Code zlog(cat, __FILE__, sizeof(__FILE__)-1, \backslash \end_layout \begin_layout LyX-Code __func__, sizeof(__func__)-1, __LINE__, \backslash \end_layout \begin_layout LyX-Code ZLOG_LEVEL_TRACE, format, ## __VA_ARGS__) \end_layout \begin_layout LyX-Code #endif \end_layout \end_deeper \begin_layout Enumerate Then zlog_trace can be used int .c file \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout LyX-Code $ cat $(top_builddir)/test/test_level.c \end_layout \begin_layout LyX-Code #include \end_layout \begin_layout LyX-Code #include "test_level.h" \end_layout \begin_layout LyX-Code int main(int argc, char** argv) \end_layout \begin_layout LyX-Code { \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout LyX-Code int rc; \end_layout \begin_layout LyX-Code zlog_category_t *zc; \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code rc = zlog_init("test_level.conf"); \end_layout \begin_layout LyX-Code if (rc) { \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout LyX-Code printf("init failed \backslash n"); \end_layout \begin_layout LyX-Code return -1; \end_layout \end_deeper \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code zc = zlog_get_category("my_cat"); \end_layout \begin_layout LyX-Code if (!zc) { \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout LyX-Code printf("get cat fail \backslash n"); \end_layout \begin_layout LyX-Code zlog_fini(); \end_layout \begin_layout LyX-Code return -2; \end_layout \end_deeper \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code zlog_trace(zc, "hello, zlog - trace"); \end_layout \begin_layout LyX-Code zlog_debug(zc, "hello, zlog - debug"); \end_layout \begin_layout LyX-Code zlog_info(zc, "hello, zlog - info"); \end_layout \begin_layout LyX-Code zlog_fini(); \end_layout \begin_layout LyX-Code return 0; \end_layout \end_deeper \begin_layout LyX-Code } \end_layout \end_deeper \begin_layout Enumerate Now we can see the output \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout LyX-Code $ ./test_level \end_layout \begin_layout LyX-Code TRACE trace hello, zlog - trace \end_layout \begin_layout LyX-Code INFO info hello, zlog - info \end_layout \begin_layout Standard That's just what we expect. The configuration file only allows >=TRACE ouput to screen. And %V and %v work as well. \end_layout \end_deeper \begin_layout Section User-defined Output \begin_inset CommandInset label LatexCommand label name "sec:User-defined-Output" \end_inset \end_layout \begin_layout Standard The goal of user-defined output is that zlog gives up some rights. zlog is only responsible for generating path and message dynamically as per the user's configuration, but leaves the log output, rotate and cleanup actions for the user to specify. You can do what ever you want by setting a function to special rules. Here are the steps to define your own output function. \end_layout \begin_layout Enumerate Define output in rules of configure file. \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout LyX-Code $ cat test_record.conf \end_layout \begin_layout LyX-Code [formats] \end_layout \begin_layout LyX-Code simple = "%m%n" \end_layout \begin_layout LyX-Code [rules] \end_layout \begin_layout LyX-Code my_cat.* $myoutput, " mypath %c %d";simple \end_layout \end_deeper \begin_layout Enumerate Set an output function for myoutput, then use it \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout LyX-Code #include \end_layout \begin_layout LyX-Code #include "zlog.h" \end_layout \begin_layout LyX-Code int output(zlog_msg_t *msg) \end_layout \begin_layout LyX-Code { \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout LyX-Code printf("[mystd]:[%s][%s][%ld] \backslash n", msg->path, msg->buf, (long)msg->len); \end_layout \begin_layout LyX-Code return 0; \end_layout \end_deeper \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code \end_layout \begin_layout LyX-Code int main(int argc, char** argv) \end_layout \begin_layout LyX-Code { \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout LyX-Code int rc; \end_layout \begin_layout LyX-Code zlog_category_t *zc; \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout LyX-Code rc = zlog_init("test_record.conf"); \end_layout \begin_layout LyX-Code if (rc) { \end_layout \begin_layout LyX-Code printf("init failed \backslash n"); \end_layout \begin_layout LyX-Code return -1; \end_layout \end_deeper \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code zlog_set_record("myoutput", output); \end_layout \begin_layout LyX-Code zc = zlog_get_category("my_cat"); \end_layout \begin_layout LyX-Code if (!zc) { \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout LyX-Code printf("get cat fail \backslash n"); \end_layout \begin_layout LyX-Code zlog_fini(); \end_layout \begin_layout LyX-Code return -2; \end_layout \end_deeper \begin_layout LyX-Code } \end_layout \begin_layout LyX-Code zlog_info(zc, "hello, zlog"); \end_layout \begin_layout LyX-Code zlog_fini(); \end_layout \begin_layout LyX-Code return 0; \end_layout \end_deeper \begin_layout LyX-Code } \end_layout \end_deeper \begin_layout Enumerate Now we can see how the user-defined output() works \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout LyX-Code $ ./test_record \end_layout \begin_layout LyX-Code [mystd]:[ mypath my_cat 2012-07-19 11:01:12][hello, zlog \end_layout \begin_layout LyX-Code ][12] \end_layout \begin_layout Standard As you can see, msglen is 12, and msg is formatted by zlog to contain a newline character. \end_layout \end_deeper \begin_layout Enumerate There are many other things you can do with user-defined output functions. As one user(flw@newsmth.net) provided: \begin_inset Separator latexpar \end_inset \end_layout \begin_deeper \begin_layout Enumerate Log name is foo.log \end_layout \begin_layout Enumerate If foo.log is larger than 100M, then generate a new logfile which contains all the contents of foo.log. And zlog truncates foo.log to 0 and re-appends to it when the next log happens. \end_layout \begin_layout Enumerate When the time is over 5 minutes after last logging, even if foo.log is not larger than 100M, zlog still jumps to a new file. \end_layout \begin_layout Enumerate The new file name should be defined by your own needs. For example add device number as prefix and time string as postfix. \end_layout \begin_layout Enumerate You might compress the new log file to save disk space and network bandwidth. \end_layout \begin_layout Standard I wish him good luck trying to write such a function for multi-process or multi-thread cases! \end_layout \end_deeper \begin_layout Chapter Epilog \end_layout \begin_layout Verse Here's to alcohol, the cause of – and solution to – all life’s problems. \end_layout \begin_layout Right Address Homer Simpson \end_layout \end_body \end_document zlog-1.2.18/doc/performence.txt000066400000000000000000000036461460570005000163770ustar00rootroot00000000000000------------------------------------------------- using makefile.linux for test, libzlog compile in O2 ------------------------------------------------- [direct write, no logging library] - The Sky! $ time ./test_press_write 1 10 100000 real 0m1.872s user 0m0.140s sys 0m3.290s $ time ./test_press_write2 1 10 100000 real 0m0.909s user 0m0.080s sys 0m1.710s ------------------------------------------------- v1.1.1 (fwrite is not atomic, so backup to write) $ time ./test_press_zlog 1 10 100000 real 0m2.334s user 0m1.780s sys 0m2.710s $ time ./test_press_zlog2 1 10 100000 real 0m2.134s user 0m1.840s sys 0m1.990s ------------------------------------------------- v1.1.0 $ time ./test_press_zlog 1 10 100000 real 0m1.107s user 0m1.500s sys 0m0.340s $ time ./test_press_zlog2 1 10 100000 real 0m0.898s user 0m1.480s sys 0m0.150s ------------------------------------------------- v1.0.7 $ time ./test_press_zlog 1 10 100000 real 0m1.783s user 0m3.000s sys 0m0.300s $ time ./test_press_zlog2 1 10 100000 real 0m1.621s user 0m2.980s sys 0m0.120s ------------------------------------------------- v1.0.6 $ time ./test_press_zlog 1 10 100000 real 0m1.814s user 0m3.060s sys 0m0.270s $ time ./test_press_zlog2 1 10 100000 real 0m1.605s user 0m2.990s sys 0m0.140s ------------------------------------------------- v1.0.5 $ time ./test_press_zlog 1 10 100000 real 0m2.779s user 0m4.170s sys 0m0.560s $ time ./test_press_zlog2 1 10 100000 real 0m2.713s user 0m4.410s sys 0m0.900s ------------------------------------------------- v1.0.3 $ time ./test_press_zlog 1 10 100000 real 0m6.349s user 0m6.300s sys 0m5.950s $ time ./test_press_zlog2 1 10 100000 real 0m6.377s user 0m6.240s sys 0m6.090s ------------------------------------------------- v1.0.0 $ time ./test_press_zlog 1 10 100000 real 0m6.364s user 0m6.040s sys 0m6.270s $ time ./test_press_zlog2 1 10 100000 real 0m6.361s user 0m6.150s sys 0m6.020s ------------------------------------------------- zlog-1.2.18/doc/zlog.conf000077500000000000000000000012571460570005000151520ustar00rootroot00000000000000[global] strict init = true reload conf period = 10M buffer min = 1024 buffer max = 2MB #rotate lock file = /tmp/zlog.lock rotate lock file = self default format = "%d(%F %T.%l) %-6V (%c:%F:%L) - %m%n" file perms = 600 fsync period = 1K [levels] TRACE = 10 CRIT = 130, LOG_CRIT [formats] simple = "%m%n" normal = "%d(%F %T.%l) %m%n" [rules] default.* >stdout; simple *.* -"%12.2E(HOME)/log/%c.log", \ 1MB * 12 ~ "%E(HOME)/log/%c.%D(%F) #2r #3s.log"; \ simple my_.INFO >stderr; my_cat.!ERROR "aa.log" my_dog.=DEBUG >syslog, LOG_LOCAL0; simple my_dog.=DEBUG | /usr/bin/cronolog /www/logs/example_%Y%m%d.log ; normal my_mice.* $record_func , "record_path%c"; normal zlog-1.2.18/src/000077500000000000000000000000001460570005000133425ustar00rootroot00000000000000zlog-1.2.18/src/CMakeLists.txt000066400000000000000000000021021460570005000160750ustar00rootroot00000000000000aux_source_directory(. SRCS) if (NOT WIN32) list(REMOVE_ITEM SRCS ./zlog_win.c) endif () list(REMOVE_ITEM SRCS ./zlog-chk-conf.c) add_library(zlog SHARED ${SRCS} ) target_link_libraries(zlog ${CMAKE_THREAD_PREFER_PTHREAD} ) if (WIN32) target_link_libraries(zlog ${UNIXEM_LIBRARY} Ws2_32 ) endif () set_target_properties(zlog PROPERTIES VERSION ${ZLOG_VERSION} SOVERSION ${ZLOG_SO_VERSION}) add_library(zlog_s STATIC ${SRCS} ) target_link_libraries(zlog_s ${CMAKE_THREAD_PREFER_PTHREAD} ) if (WIN32) target_link_libraries(zlog_s ${UNIXEM_LIBRARY} Ws2_32 ) endif () set_target_properties(zlog_s PROPERTIES OUTPUT_NAME zlog) add_executable(zlog-chk-conf zlog-chk-conf.c) target_link_libraries(zlog-chk-conf zlog) install(TARGETS zlog zlog_s zlog-chk-conf COMPONENT zlog ARCHIVE DESTINATION lib LIBRARY DESTINATION lib RUNTIME DESTINATION bin ) install(FILES zlog.h COMPONENT zlog DESTINATION include ) zlog-1.2.18/src/Makefile000066400000000000000000000200731460570005000150040ustar00rootroot00000000000000# zlog makefile # # Copyright (c) Hardy Simpson # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. OBJ= \ buf.o \ category.o \ category_table.o \ conf.o \ event.o \ format.o \ level.o \ level_list.o \ mdc.o \ record.o \ record_table.o \ rotater.o \ rule.o \ spec.o \ thread.o \ zc_arraylist.o \ zc_hashtable.o \ zc_profile.o \ zc_util.o \ lockfile.o \ zlog.o BINS=zlog-chk-conf LIBNAME=libzlog ZLOG_MAJOR=1 ZLOG_MINOR=2 # for mingw (cut the last part of the uname) UNAME := $(shell uname | cut -c -5) ifeq ($(UNAME), MINGW) $(print compiling on mingw !!) OBJ := $(OBJ) zlog_win.o endif # Fallback to gcc when $CC is not in $PATH. CC:=$(shell sh -c 'type $(CC) >/dev/null 2>/dev/null && echo $(CC) || echo gcc') OPTIMIZATION?=-O2 ifeq ($(UNAME), MINGW) #XXX: ignore warnings on mingw for now WARNINGS=-Wall -Wstrict-prototypes -fwrapv else WARNINGS=-Wall -Werror -Wstrict-prototypes -fwrapv endif DEBUG?= -g -ggdb REAL_CFLAGS=$(OPTIMIZATION) -fPIC -pthread $(CFLAGS) $(WARNINGS) $(DEBUG) REAL_LDFLAGS=$(LDFLAGS) -pthread DYLIBSUFFIX=so STLIBSUFFIX=a DYLIB_MINOR_NAME=$(LIBNAME).$(DYLIBSUFFIX).$(ZLOG_MAJOR).$(ZLOG_MINOR) DYLIB_MAJOR_NAME=$(LIBNAME).$(DYLIBSUFFIX).$(ZLOG_MAJOR) DYLIBNAME=$(LIBNAME).$(DYLIBSUFFIX) DYLIB_MAKE_CMD=$(CC) -shared -Wl,-soname,$(DYLIB_MINOR_NAME) -o $(DYLIBNAME) $(LDFLAGS) STLIBNAME=$(LIBNAME).$(STLIBSUFFIX) STLIB_MAKE_CMD=ar rcs $(STLIBNAME) # Installation related variables PREFIX?=/usr/local INCLUDE_PATH?=include LIBRARY_PATH?=lib BINARY_PATH=bin INSTALL_INCLUDE_PATH= $(PREFIX)/$(INCLUDE_PATH) INSTALL_LIBRARY_PATH= $(PREFIX)/$(LIBRARY_PATH) INSTALL_BINARY_PATH= $(PREFIX)/$(BINARY_PATH) # Platform-specific overrides uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not') compiler_platform := $(shell sh -c '$(CC) --version|grep -i apple') ifeq ($(uname_S),SunOS) # REAL_LDFLAGS+= -ldl -lnsl -lsocket DYLIB_MAKE_CMD=$(CC) -G -o $(DYLIBNAME) -h $(DYLIB_MINOR_NAME) $(LDFLAGS) INSTALL= cp -r endif # For Darwin builds, check the compiler platform above is not empty. The covers cross compilation on Linux ifneq ($(compiler_platform),) DYLIBSUFFIX=dylib DYLIB_MINOR_NAME=$(LIBNAME).$(ZLOG_MAJOR).$(ZLOG_MINOR).$(DYLIBSUFFIX) DYLIB_MAJOR_NAME=$(LIBNAME).$(ZLOG_MAJOR).$(DYLIBSUFFIX) DYLIB_MAKE_CMD=$(CC) -dynamiclib -install_name $(INSTALL_LIBRARY_PATH)/$(DYLIB_MINOR_NAME) -o $(DYLIBNAME) $(LDFLAGS) REAL_CFLAGS+= -D_DARWIN_C_SOURCE endif ifeq ($(uname_S),AIX) # this logic of minor major is not relevant on AIX or at least not widely used # not to mention dynamic linker .a preference... DYLIB_MAKE_CMD=$(CC) -shared -Wl,-G,-b64 -maix64 -pthread -o $(DYLIBNAME) $(LDFLAGS) REAL_CFLAGS+= -maix64 STLIB_MAKE_CMD=OBJECT_MODE=64 ar rcs $(STLIBNAME) $(DYLIB_MAJOR_NAME) endif ifeq ($(UNAME), MINGW) $(print compiling on mingw !!) DYLIBSUFFIX=dll STLIBSUFFIX=nogo REAL_CFLAGS := $(REAL_CFLAGS) -I$(INSTALL_INCLUDE_PATH) REAL_LDFLAGS := $(REAL_LDFLAGS) -L$(INSTALL_LIBRARY_PATH) -lws2_32 -lunixem endif all: $(DYLIBNAME) $(BINS) # Deps (use make dep to generate this) buf.o: buf.c zc_defs.h zc_profile.h zc_arraylist.h zc_hashtable.h \ zc_xplatform.h zc_util.h buf.h category.o: category.c fmacros.h category.h zc_defs.h zc_profile.h \ zc_arraylist.h zc_hashtable.h zc_xplatform.h zc_util.h thread.h event.h \ buf.h mdc.h rule.h format.h rotater.h record.h category_table.o: category_table.c zc_defs.h zc_profile.h zc_arraylist.h \ zc_hashtable.h zc_xplatform.h zc_util.h category_table.h category.h \ thread.h event.h buf.h mdc.h conf.o: conf.c fmacros.h conf.h zc_defs.h zc_profile.h zc_arraylist.h \ zc_hashtable.h zc_xplatform.h zc_util.h format.h thread.h event.h buf.h \ mdc.h rotater.h rule.h record.h level_list.h level.h event.o: event.c fmacros.h zc_defs.h zc_profile.h zc_arraylist.h \ zc_hashtable.h zc_xplatform.h zc_util.h event.h format.o: format.c zc_defs.h zc_profile.h zc_arraylist.h zc_hashtable.h \ zc_xplatform.h zc_util.h thread.h event.h buf.h mdc.h spec.h format.h level.o: level.c zc_defs.h zc_profile.h zc_arraylist.h zc_hashtable.h \ zc_xplatform.h zc_util.h level.h level_list.o: level_list.c zc_defs.h zc_profile.h zc_arraylist.h \ zc_hashtable.h zc_xplatform.h zc_util.h level.h level_list.h mdc.o: mdc.c mdc.h zc_defs.h zc_profile.h zc_arraylist.h zc_hashtable.h \ zc_xplatform.h zc_util.h record.o: record.c zc_defs.h zc_profile.h zc_arraylist.h zc_hashtable.h \ zc_xplatform.h zc_util.h record.h record_table.o: record_table.c zc_defs.h zc_profile.h zc_arraylist.h \ zc_hashtable.h zc_xplatform.h zc_util.h record_table.h record.h rotater.o: rotater.c zc_defs.h zc_profile.h zc_arraylist.h zc_hashtable.h \ zc_xplatform.h zc_util.h rotater.h rule.o: rule.c fmacros.h rule.h zc_defs.h zc_profile.h zc_arraylist.h \ zc_hashtable.h zc_xplatform.h zc_util.h format.h thread.h event.h buf.h \ mdc.h rotater.h record.h level_list.h level.h spec.h spec.o: spec.c fmacros.h spec.h event.h zc_defs.h zc_profile.h \ zc_arraylist.h zc_hashtable.h zc_xplatform.h zc_util.h buf.h thread.h \ mdc.h level_list.h level.h thread.o: thread.c zc_defs.h zc_profile.h zc_arraylist.h zc_hashtable.h \ zc_xplatform.h zc_util.h event.h buf.h thread.h mdc.h zc_arraylist.o: zc_arraylist.c zc_defs.h zc_profile.h zc_arraylist.h \ zc_hashtable.h zc_xplatform.h zc_util.h zc_hashtable.o: zc_hashtable.c zc_defs.h zc_profile.h zc_arraylist.h \ zc_hashtable.h zc_xplatform.h zc_util.h zc_profile.o: zc_profile.c fmacros.h zc_profile.h zc_xplatform.h zc_util.o: zc_util.c zc_defs.h zc_profile.h zc_arraylist.h zc_hashtable.h \ zc_xplatform.h zc_util.h zlog-chk-conf.o: zlog-chk-conf.c fmacros.h zlog.h lockfile.o: lockfile.c zlog.o: zlog.c fmacros.h conf.h zc_defs.h zc_profile.h zc_arraylist.h \ zc_hashtable.h zc_xplatform.h zc_util.h format.h thread.h event.h buf.h \ mdc.h rotater.h category_table.h category.h record_table.h \ record.h rule.h zlog_win.o: zlog_win.c $(DYLIBNAME): $(OBJ) $(DYLIB_MAKE_CMD) $(OBJ) $(REAL_LDFLAGS) # for use in test folder - linux and requirement for aix runtime # resolving cp -f $(DYLIBNAME) $(DYLIB_MAJOR_NAME) cp -f $(DYLIBNAME) $(DYLIB_MINOR_NAME) $(STLIBNAME): $(OBJ) $(STLIB_MAKE_CMD) $(OBJ) dynamic: $(DYLIBNAME) static: $(STLIBNAME) # Binaries: zlog-chk-conf: zlog-chk-conf.o $(STLIBNAME) $(DYLIBNAME) $(CC) -o $@ zlog-chk-conf.o -L. -lzlog $(REAL_LDFLAGS) .c.o: $(CC) -std=c99 -pedantic -c $(REAL_CFLAGS) $< clean: rm -rf $(DYLIBNAME) $(STLIBNAME) $(BINS) *.o *.gcda *.gcno *.gcov $(DYLIB_MINOR_NAME) $(DYLIB_MAJOR_NAME) dep: $(CC) -MM *.c # Installation target ifeq ($(uname_S),SunOS) INSTALL?= cp -r endif ifeq ($(uname_S),AIX) INSTALL?= cp -r endif INSTALL?= cp -a install: $(DYLIBNAME) $(STLIBNAME) mkdir -p $(INSTALL_INCLUDE_PATH) $(INSTALL_LIBRARY_PATH) $(INSTALL_BINARY_PATH) $(INSTALL) zlog.h $(INSTALL_INCLUDE_PATH) $(INSTALL) zlog-chk-conf $(INSTALL_BINARY_PATH) $(INSTALL) $(DYLIBNAME) $(INSTALL_LIBRARY_PATH)/$(DYLIB_MINOR_NAME) cd $(INSTALL_LIBRARY_PATH) && ln -sf $(DYLIB_MINOR_NAME) $(DYLIB_MAJOR_NAME) cd $(INSTALL_LIBRARY_PATH) && ln -sf $(DYLIB_MAJOR_NAME) $(DYLIBNAME) $(INSTALL) $(STLIBNAME) $(INSTALL_LIBRARY_PATH) 32bit: @echo "" @echo "WARNING: if this fails under Linux you probably need to install libc6-dev-i386" @echo "" $(MAKE) CFLAGS="-m32" LDFLAGS="-m32" gprof: $(MAKE) CFLAGS="-pg" LDFLAGS="-pg" gcov: $(MAKE) CFLAGS="-fprofile-arcs -ftest-coverage" LDFLAGS="-fprofile-arcs" coverage: gcov make check mkdir -p tmp/lcov lcov -d . -c -o tmp/lcov/hiredis.info genhtml --legend -o tmp/lcov/report tmp/lcov/hiredis.info noopt: $(MAKE) OPTIMIZATION="" .PHONY: all clean dep install 32bit gprof gcov noopt zlog-1.2.18/src/buf.c000066400000000000000000000433151460570005000142700ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include "zc_defs.h" #include "buf.h" /*******************************************************************************/ /* Author's Note * This buf.c is base on C99, that is, if buffer size is not enough, * the return value of vsnprintf(3) is a number tell how many character should * be output. vsnprintf in glibc 2.1 conforms to C99 , but glibc 2.0 doesn't. * see manpage of vsnprintf(3) on you platform for more detail. * So, what should you do if you want to using zlog on the platform that doesn't * conform C99? My Answer is, crack zlog with a portable C99-vsnprintf, like this * http://sourceforge.net/projects/ctrio/ * http://www.jhweiss.de/software/snprintf.html * If you can see this note, you can fix it yourself? Aren't you? ^_^ * Oh, I put the snprintf in C99 standard here, * vsnprintf is the same on return value. 7.19.6.5 The snprintf function Synopsis [#1] #include int snprintf(char * restrict s, size_t n, const char * restrict format, ...); Description [#2] The snprintf function is equivalent to fprintf, except that the output is written into an array (specified by argument s) rather than to a stream. If n is zero, nothing is written, and s may be a null pointer. Otherwise, output characters beyond the n-1st are discarded rather than being written to the array, and a null character is written at the end of the characters actually written into the array. If copying takes place between objects that overlap, the behavior is undefined. Returns [#3] The snprintf function returns the number of characters that would have been written had n been sufficiently large, not counting the terminating null character, or a negative value if an encoding error occurred. Thus, the null- terminated output has been completely written if and only if the returned value is nonnegative and less than n. */ /*******************************************************************************/ void zlog_buf_profile(zlog_buf_t * a_buf, int flag) { //zc_assert(a_buf,); zc_profile(flag, "---buf[%p][%ld-%ld][%ld][%s][%p:%ld]---", a_buf, a_buf->size_min, a_buf->size_max, a_buf->size_real, a_buf->truncate_str, a_buf->start, a_buf->tail - a_buf->start); return; } /*******************************************************************************/ void zlog_buf_del(zlog_buf_t * a_buf) { //zc_assert(a_buf,); if (a_buf->start) free(a_buf->start); zc_debug("zlog_buf_del[%p]", a_buf); free(a_buf); return; } zlog_buf_t *zlog_buf_new(size_t buf_size_min, size_t buf_size_max, const char *truncate_str) { zlog_buf_t *a_buf; if (buf_size_min == 0) { zc_error("buf_size_min == 0, not allowed"); return NULL; } if (buf_size_max != 0 && buf_size_max < buf_size_min) { zc_error("buf_size_max[%lu] < buf_size_min[%lu] && buf_size_max != 0", (unsigned long)buf_size_max, (unsigned long)buf_size_min); return NULL; } a_buf = calloc(1, sizeof(*a_buf)); if (!a_buf) { zc_error("calloc fail, errno[%d]", errno); return NULL; } if (truncate_str) { if (strlen(truncate_str) > sizeof(a_buf->truncate_str) - 1) { zc_error("truncate_str[%s] overflow", truncate_str); goto err; } else { strcpy(a_buf->truncate_str, truncate_str); } a_buf->truncate_str_len = strlen(truncate_str); } a_buf->size_min = buf_size_min; a_buf->size_max = buf_size_max; a_buf->size_real = a_buf->size_min; a_buf->start = calloc(1, a_buf->size_real); if (!a_buf->start) { zc_error("calloc fail, errno[%d]", errno); goto err; } a_buf->tail = a_buf->start; a_buf->end_plus_1 = a_buf->start + a_buf->size_real; a_buf->end = a_buf->end_plus_1 - 1; //zlog_buf_profile(a_buf, ZC_DEBUG); return a_buf; err: zlog_buf_del(a_buf); return NULL; } /*******************************************************************************/ static void zlog_buf_truncate(zlog_buf_t * a_buf) { char *p; size_t len; if ((a_buf->truncate_str)[0] == '\0') return; p = (a_buf->tail - a_buf->truncate_str_len); if (p < a_buf->start) p = a_buf->start; len = a_buf->tail - p; memcpy(p, a_buf->truncate_str, len); return; } /*******************************************************************************/ /* return 0: success * return <0: fail, set size_real to -1; * return >0: by conf limit, can't extend size * increment must > 0 */ static int zlog_buf_resize(zlog_buf_t * a_buf, size_t increment) { int rc = 0; size_t new_size = 0; size_t len = 0; char *p = NULL; if (a_buf->size_max != 0 && a_buf->size_real >= a_buf->size_max) { zc_error("a_buf->size_real[%ld] >= a_buf->size_max[%ld]", a_buf->size_real, a_buf->size_max); return 1; } if (a_buf->size_max == 0) { /* unlimit */ new_size = a_buf->size_real + 1.5 * increment; } else { /* limited */ if (a_buf->size_real + increment <= a_buf->size_max) { new_size = a_buf->size_real + increment; } else { new_size = a_buf->size_max; rc = 1; } } len = a_buf->tail - a_buf->start; p = realloc(a_buf->start, new_size); if (!p) { zc_error("realloc fail, errno[%d]", errno); free(a_buf->start); a_buf->start = NULL; a_buf->tail = NULL; a_buf->end = NULL; a_buf->end_plus_1 = NULL; return -1; } else { a_buf->start = p; a_buf->tail = p + len; a_buf->size_real = new_size; a_buf->end_plus_1 = a_buf->start + new_size; a_buf->end = a_buf->end_plus_1 - 1; } return rc; } int zlog_buf_vprintf(zlog_buf_t * a_buf, const char *format, va_list args) { va_list ap; size_t size_left; int nwrite; if (!a_buf->start) { zc_error("pre-use of zlog_buf_resize fail, so can't convert"); return -1; } va_copy(ap, args); size_left = a_buf->end_plus_1 - a_buf->tail; nwrite = vsnprintf(a_buf->tail, size_left, format, ap); if (nwrite >= 0 && nwrite < size_left) { a_buf->tail += nwrite; //*(a_buf->tail) = '\0'; return 0; } else if (nwrite < 0) { zc_error("vsnprintf fail, errno[%d]", errno); zc_error("nwrite[%d], size_left[%ld], format[%s]", nwrite, size_left, format); return -1; } else if (nwrite >= size_left) { int rc; //zc_debug("nwrite[%d]>=size_left[%ld],format[%s],resize", nwrite, size_left, format); rc = zlog_buf_resize(a_buf, nwrite - size_left + 1); if (rc > 0) { zc_error("conf limit to %ld, can't extend, so truncate", a_buf->size_max); va_copy(ap, args); size_left = a_buf->end_plus_1 - a_buf->tail; vsnprintf(a_buf->tail, size_left, format, ap); a_buf->tail += size_left - 1; //*(a_buf->tail) = '\0'; zlog_buf_truncate(a_buf); return 1; } else if (rc < 0) { zc_error("zlog_buf_resize fail"); return -1; } else { //zc_debug("zlog_buf_resize succ, to[%ld]", a_buf->size_real); va_copy(ap, args); size_left = a_buf->end_plus_1 - a_buf->tail; nwrite = vsnprintf(a_buf->tail, size_left, format, ap); if (nwrite < 0) { zc_error("vsnprintf fail, errno[%d]", errno); zc_error("nwrite[%d], size_left[%ld], format[%s]", nwrite, size_left, format); return -1; } else { a_buf->tail += nwrite; //*(a_buf->tail) = '\0'; return 0; } } } return 0; } /*******************************************************************************/ /* if width > num_len, 0 padding, else output num */ int zlog_buf_printf_dec32(zlog_buf_t * a_buf, uint32_t ui32, int width) { unsigned char *p; char *q; unsigned char tmp[ZLOG_INT32_LEN + 1]; size_t num_len, zero_len, out_len; if (!a_buf->start) { zc_error("pre-use of zlog_buf_resize fail, so can't convert"); return -1; } p = tmp + ZLOG_INT32_LEN; do { *--p = (unsigned char) (ui32 % 10 + '0'); } while (ui32 /= 10); /* zero or space padding */ num_len = (tmp + ZLOG_INT32_LEN) - p; if (width > num_len) { zero_len = width - num_len; out_len = width; } else { zero_len = 0; out_len = num_len; } if ((q = a_buf->tail + out_len) > a_buf->end) { int rc; //zc_debug("size_left not enough, resize"); rc = zlog_buf_resize(a_buf, out_len - (a_buf->end - a_buf->tail)); if (rc > 0) { size_t len_left; zc_error("conf limit to %ld, can't extend, so output", a_buf->size_max); len_left = a_buf->end - a_buf->tail; if (len_left <= zero_len) { zero_len = len_left; num_len = 0; } else if (len_left > zero_len) { /* zero_len not changed */ num_len = len_left - zero_len; } if (zero_len) memset(a_buf->tail, '0', zero_len); memcpy(a_buf->tail + zero_len, p, num_len); a_buf->tail += len_left; //*(a_buf->tail) = '\0'; zlog_buf_truncate(a_buf); return 1; } else if (rc < 0) { zc_error("zlog_buf_resize fail"); return -1; } else { //zc_debug("zlog_buf_resize succ, to[%ld]", a_buf->size_real); q = a_buf->tail + out_len; /* re-calculate p*/ } } if (zero_len) memset(a_buf->tail, '0', zero_len); memcpy(a_buf->tail + zero_len, p, num_len); a_buf->tail = q; //*(a_buf->tail) = '\0'; return 0; } /*******************************************************************************/ int zlog_buf_printf_dec64(zlog_buf_t * a_buf, uint64_t ui64, int width) { unsigned char *p; char *q; unsigned char tmp[ZLOG_INT64_LEN + 1]; size_t num_len, zero_len, out_len; uint32_t ui32; if (!a_buf->start) { zc_error("pre-use of zlog_buf_resize fail, so can't convert"); return -1; } p = tmp + ZLOG_INT64_LEN; if (ui64 <= ZLOG_MAX_UINT32_VALUE) { /* * To divide 64-bit numbers and to find remainders * on the x86 platform gcc and icc call the libc functions * [u]divdi3() and [u]moddi3(), they call another function * in its turn. On FreeBSD it is the qdivrem() function, * its source code is about 170 lines of the code. * The glibc counterpart is about 150 lines of the code. * * For 32-bit numbers and some divisors gcc and icc use * a inlined multiplication and shifts. For example, * unsigned "i32 / 10" is compiled to * * (i32 * 0xCCCCCCCD) >> 35 */ ui32 = (uint32_t) ui64; do { *--p = (unsigned char) (ui32 % 10 + '0'); } while (ui32 /= 10); } else { do { *--p = (unsigned char) (ui64 % 10 + '0'); } while (ui64 /= 10); } /* zero or space padding */ num_len = (tmp + ZLOG_INT64_LEN) - p; if (width > num_len) { zero_len = width - num_len; out_len = width; } else { zero_len = 0; out_len = num_len; } if ((q = a_buf->tail + out_len) > a_buf->end) { int rc; //zc_debug("size_left not enough, resize"); rc = zlog_buf_resize(a_buf, out_len - (a_buf->end - a_buf->tail)); if (rc > 0) { size_t len_left; zc_error("conf limit to %ld, can't extend, so output", a_buf->size_max); len_left = a_buf->end - a_buf->tail; if (len_left <= zero_len) { zero_len = len_left; num_len = 0; } else if (len_left > zero_len) { /* zero_len not changed */ num_len = len_left - zero_len; } if (zero_len) memset(a_buf->tail, '0', zero_len); memcpy(a_buf->tail + zero_len, p, num_len); a_buf->tail += len_left; //*(a_buf->tail) = '\0'; zlog_buf_truncate(a_buf); return 1; } else if (rc < 0) { zc_error("zlog_buf_resize fail"); return -1; } else { //zc_debug("zlog_buf_resize succ, to[%ld]", a_buf->size_real); q = a_buf->tail + out_len; /* re-calculate p*/ } } if (zero_len) memset(a_buf->tail, '0', zero_len); memcpy(a_buf->tail + zero_len, p, num_len); a_buf->tail = q; //*(a_buf->tail) = '\0'; return 0; } /*******************************************************************************/ int zlog_buf_printf_hex(zlog_buf_t * a_buf, uint32_t ui32, int width) { unsigned char *p; char *q; unsigned char tmp[ZLOG_INT32_LEN + 1]; size_t num_len, zero_len, out_len; static unsigned char hex[] = "0123456789abcdef"; //static unsigned char HEX[] = "0123456789ABCDEF"; if (!a_buf->start) { zc_error("pre-use of zlog_buf_resize fail, so can't convert"); return -1; } p = tmp + ZLOG_INT32_LEN; do { /* the "(uint32_t)" cast disables the BCC's warning */ *--p = hex[(uint32_t) (ui32 & 0xf)]; } while (ui32 >>= 4); #if 0 } else { /* is_hex == 2 */ do { /* the "(uint32_t)" cast disables the BCC's warning */ *--p = HEX[(uint32_t) (ui64 & 0xf)]; } while (ui64 >>= 4); } #endif /* zero or space padding */ num_len = (tmp + ZLOG_INT32_LEN) - p; if (width > num_len) { zero_len = width - num_len; out_len = width; } else { zero_len = 0; out_len = num_len; } if ((q = a_buf->tail + out_len) > a_buf->end) { int rc; //zc_debug("size_left not enough, resize"); rc = zlog_buf_resize(a_buf, out_len - (a_buf->end - a_buf->tail)); if (rc > 0) { size_t len_left; zc_error("conf limit to %ld, can't extend, so output", a_buf->size_max); len_left = a_buf->end - a_buf->tail; if (len_left <= zero_len) { zero_len = len_left; num_len = 0; } else if (len_left > zero_len) { /* zero_len not changed */ num_len = len_left - zero_len; } if (zero_len) memset(a_buf->tail, '0', zero_len); memcpy(a_buf->tail + zero_len, p, num_len); a_buf->tail += len_left; //*(a_buf->tail) = '\0'; zlog_buf_truncate(a_buf); return 1; } else if (rc < 0) { zc_error("zlog_buf_resize fail"); return -1; } else { //zc_debug("zlog_buf_resize succ, to[%ld]", a_buf->size_real); q = a_buf->tail + out_len; /* re-calculate p*/ } } if (zero_len) memset(a_buf->tail, '0', zero_len); memcpy(a_buf->tail + zero_len, p, num_len); a_buf->tail = q; //*(a_buf->tail) = '\0'; return 0; } /*******************************************************************************/ int zlog_buf_append(zlog_buf_t * a_buf, const char *str, size_t str_len) { char *p; #if 0 if (str_len <= 0 || str == NULL) { return 0; } if (!a_buf->start) { zc_error("pre-use of zlog_buf_resize fail, so can't convert"); return -1; } #endif if ((p = a_buf->tail + str_len) > a_buf->end) { int rc; //zc_debug("size_left not enough, resize"); rc = zlog_buf_resize(a_buf, str_len - (a_buf->end - a_buf->tail)); if (rc > 0) { size_t len_left; zc_error("conf limit to %ld, can't extend, so output", a_buf->size_max); len_left = a_buf->end - a_buf->tail; memcpy(a_buf->tail, str, len_left); a_buf->tail += len_left; //*(a_buf->tail) = '\0'; zlog_buf_truncate(a_buf); return 1; } else if (rc < 0) { zc_error("zlog_buf_resize fail"); return -1; } else { //zc_debug("zlog_buf_resize succ, to[%ld]", a_buf->size_real); p = a_buf->tail + str_len; /* re-calculate p*/ } } memcpy(a_buf->tail, str, str_len); a_buf->tail = p; // *(a_buf->tail) = '\0'; return 0; } /*******************************************************************************/ int zlog_buf_adjust_append(zlog_buf_t * a_buf, const char *str, size_t str_len, int left_adjust, int zero_pad, size_t in_width, size_t out_width) { size_t append_len = 0; size_t source_len = 0; size_t space_len = 0; #if 0 if (str_len <= 0 || str == NULL) { return 0; } #endif if (!a_buf->start) { zc_error("pre-use of zlog_buf_resize fail, so can't convert"); return -1; } /* calculate how many character will be got from str */ if (out_width == 0 || str_len < out_width) { source_len = str_len; } else { source_len = out_width; } /* calculate how many character will be output */ if (in_width == 0 || source_len >= in_width ) { append_len = source_len; space_len = 0; } else { append_len = in_width; space_len = in_width - source_len; } /* |-----append_len-----------| */ /* |-source_len---|-space_len-| left_adjust */ /* |-space_len---|-source_len-| right_adjust */ /* |-(size_real-1)---| size not enough */ if (append_len > a_buf->end - a_buf->tail) { int rc = 0; //zc_debug("size_left not enough, resize"); rc = zlog_buf_resize(a_buf, append_len - (a_buf->end -a_buf->tail)); if (rc > 0) { zc_error("conf limit to %ld, can't extend, so output", a_buf->size_max); append_len = (a_buf->end - a_buf->tail); if (left_adjust) { if (source_len < append_len) { space_len = append_len - source_len; } else { source_len = append_len; space_len = 0; } if (space_len) memset(a_buf->tail + source_len, ' ', space_len); memcpy(a_buf->tail, str, source_len); } else { if (space_len < append_len) { source_len = append_len - space_len; } else { space_len = append_len; source_len = 0; } if (space_len) { if (zero_pad) { memset(a_buf->tail, '0', space_len); } else { memset(a_buf->tail, ' ', space_len); } } memcpy(a_buf->tail + space_len, str, source_len); } a_buf->tail += append_len; //*(a_buf->tail) = '\0'; zlog_buf_truncate(a_buf); return 1; } else if (rc < 0) { zc_error("zlog_buf_resize fail"); return -1; } else { //zc_debug("zlog_buf_resize succ, to[%ld]", a_buf->size_real); } } if (left_adjust) { if (space_len) memset(a_buf->tail + source_len, ' ', space_len); memcpy(a_buf->tail, str, source_len); } else { if (space_len) { if (zero_pad) { memset(a_buf->tail, '0', space_len); } else { memset(a_buf->tail, ' ', space_len); } } memcpy(a_buf->tail + space_len, str, source_len); } a_buf->tail += append_len; //*(a_buf->tail) = '\0'; return 0; } /*******************************************************************************/ zlog-1.2.18/src/buf.h000066400000000000000000000040301460570005000142640ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __zlog_buf_h #define __zlog_buf_h /* buf, is a dynamic expand buffer for one single log, * as one single log will interlace if use multiple write() to file. * and buf is always keep in a thread, to make each thread has its * own buffer to avoid lock. */ #include #include typedef struct zlog_buf_s { char *start; char *tail; char *end; char *end_plus_1; size_t size_min; size_t size_max; size_t size_real; char truncate_str[MAXLEN_PATH + 1]; size_t truncate_str_len; } zlog_buf_t; zlog_buf_t *zlog_buf_new(size_t min, size_t max, const char *truncate_str); void zlog_buf_del(zlog_buf_t * a_buf); void zlog_buf_profile(zlog_buf_t * a_buf, int flag); int zlog_buf_vprintf(zlog_buf_t * a_buf, const char *format, va_list args); int zlog_buf_append(zlog_buf_t * a_buf, const char *str, size_t str_len); int zlog_buf_adjust_append(zlog_buf_t * a_buf, const char *str, size_t str_len, int left_adjust, int zero_pad, size_t in_width, size_t out_width); int zlog_buf_printf_dec32(zlog_buf_t * a_buf, uint32_t ui32, int width); int zlog_buf_printf_dec64(zlog_buf_t * a_buf, uint64_t ui64, int width); int zlog_buf_printf_hex(zlog_buf_t * a_buf, uint32_t ui32, int width); #define zlog_buf_restart(a_buf) do { \ a_buf->tail = a_buf->start; \ } while(0) #define zlog_buf_len(a_buf) (a_buf->tail - a_buf->start) #define zlog_buf_str(a_buf) (a_buf->start) #define zlog_buf_seal(a_buf) do {*(a_buf)->tail = '\0';} while (0) #endif zlog-1.2.18/src/category.c000066400000000000000000000151701460570005000153270ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "fmacros.h" #include #include #include #include "category.h" #include "rule.h" #include "zc_defs.h" void zlog_category_profile(zlog_category_t *a_category, int flag) { int i; zlog_rule_t *a_rule; zc_assert(a_category,); zc_profile(flag, "--category[%p][%s][%p]--", a_category, a_category->name, a_category->fit_rules); if (a_category->fit_rules) { zc_arraylist_foreach(a_category->fit_rules, i, a_rule) { zlog_rule_profile(a_rule, flag); } } return; } /*******************************************************************************/ void zlog_category_del(zlog_category_t * a_category) { zc_assert(a_category,); if (a_category->fit_rules) zc_arraylist_del(a_category->fit_rules); zc_debug("zlog_category_del[%p]", a_category); free(a_category); return; } /* overlap one rule's level bitmap to cateogry, * so category can judge whether a log level will be output by itself * It is safe when configure is reloaded, when rule will be released an recreated */ static void zlog_cateogry_overlap_bitmap(zlog_category_t * a_category, zlog_rule_t *a_rule) { int i; for(i = 0; i < sizeof(a_rule->level_bitmap); i++) { a_category->level_bitmap[i] |= a_rule->level_bitmap[i]; } } static int zlog_category_obtain_rules(zlog_category_t * a_category, zc_arraylist_t * rules) { int i; int count = 0; int fit = 0; zlog_rule_t *a_rule; zlog_rule_t *wastebin_rule = NULL; /* before set, clean last fit rules first */ if (a_category->fit_rules) zc_arraylist_del(a_category->fit_rules); memset(a_category->level_bitmap, 0x00, sizeof(a_category->level_bitmap)); a_category->fit_rules = zc_arraylist_new(NULL); if (!(a_category->fit_rules)) { zc_error("zc_arraylist_new fail"); return -1; } /* get match rules from all rules */ zc_arraylist_foreach(rules, i, a_rule) { fit = zlog_rule_match_category(a_rule, a_category->name); if (fit) { if (zc_arraylist_add(a_category->fit_rules, a_rule)) { zc_error("zc_arrylist_add fail"); goto err; } zlog_cateogry_overlap_bitmap(a_category, a_rule); count++; } if (zlog_rule_is_wastebin(a_rule)) { wastebin_rule = a_rule; } } if (count == 0) { if (wastebin_rule) { zc_debug("category[%s], no match rules, use wastebin_rule", a_category->name); if (zc_arraylist_add(a_category->fit_rules, wastebin_rule)) { zc_error("zc_arrylist_add fail"); goto err; } zlog_cateogry_overlap_bitmap(a_category, wastebin_rule); count++; } else { zc_debug("category[%s], no match rules & no wastebin_rule", a_category->name); } } return 0; err: zc_arraylist_del(a_category->fit_rules); a_category->fit_rules = NULL; return -1; } zlog_category_t *zlog_category_new(const char *name, zc_arraylist_t * rules) { size_t len; zlog_category_t *a_category; zc_assert(name, NULL); zc_assert(rules, NULL); len = strlen(name); if (len > sizeof(a_category->name) - 1) { zc_error("name[%s] too long", name); return NULL; } a_category = calloc(1, sizeof(zlog_category_t)); if (!a_category) { zc_error("calloc fail, errno[%d]", errno); return NULL; } strcpy(a_category->name, name); a_category->name_len = len; if (zlog_category_obtain_rules(a_category, rules)) { zc_error("zlog_category_fit_rules fail"); goto err; } zlog_category_profile(a_category, ZC_DEBUG); return a_category; err: zlog_category_del(a_category); return NULL; } /*******************************************************************************/ /* update success: fit_rules 1, fit_rules_backup 1 */ /* update fail: fit_rules 0, fit_rules_backup 1 */ int zlog_category_update_rules(zlog_category_t * a_category, zc_arraylist_t * new_rules) { zc_assert(a_category, -1); zc_assert(new_rules, -1); /* 1st, mv fit_rules fit_rules_backup */ if (a_category->fit_rules_backup) zc_arraylist_del(a_category->fit_rules_backup); a_category->fit_rules_backup = a_category->fit_rules; a_category->fit_rules = NULL; memcpy(a_category->level_bitmap_backup, a_category->level_bitmap, sizeof(a_category->level_bitmap)); /* 2nd, obtain new_rules to fit_rules */ if (zlog_category_obtain_rules(a_category, new_rules)) { zc_error("zlog_category_obtain_rules fail"); a_category->fit_rules = NULL; return -1; } /* keep the fit_rules_backup not change, return */ return 0; } /* commit fail: fit_rules_backup != 0 */ /* commit success: fit_rules 1, fit_rules_backup 0 */ void zlog_category_commit_rules(zlog_category_t * a_category) { zc_assert(a_category,); if (!a_category->fit_rules_backup) { zc_warn("a_category->fit_rules_backup is NULL, never update before"); return; } zc_arraylist_del(a_category->fit_rules_backup); a_category->fit_rules_backup = NULL; memset(a_category->level_bitmap_backup, 0x00, sizeof(a_category->level_bitmap_backup)); return; } /* rollback fail: fit_rules_backup != 0 */ /* rollback success: fit_rules 1, fit_rules_backup 0 */ /* so whether update succes or not, make things back to old */ void zlog_category_rollback_rules(zlog_category_t * a_category) { zc_assert(a_category,); if (!a_category->fit_rules_backup) { zc_warn("a_category->fit_rules_backup in NULL, never update before"); return; } if (a_category->fit_rules) { /* update success, rm new and backup */ zc_arraylist_del(a_category->fit_rules); a_category->fit_rules = a_category->fit_rules_backup; a_category->fit_rules_backup = NULL; } else { /* update fail, just backup */ a_category->fit_rules = a_category->fit_rules_backup; a_category->fit_rules_backup = NULL; } memcpy(a_category->level_bitmap, a_category->level_bitmap_backup, sizeof(a_category->level_bitmap)); memset(a_category->level_bitmap_backup, 0x00, sizeof(a_category->level_bitmap_backup)); return; /* always success */ } /*******************************************************************************/ int zlog_category_output(zlog_category_t * a_category, zlog_thread_t * a_thread) { int i; int rc = 0; zlog_rule_t *a_rule; /* go through all match rules to output */ zc_arraylist_foreach(a_category->fit_rules, i, a_rule) { rc = zlog_rule_output(a_rule, a_thread); } return rc; } zlog-1.2.18/src/category.h000066400000000000000000000031051460570005000153270ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __zlog_category_h #define __zlog_category_h #include "zc_defs.h" #include "thread.h" typedef struct zlog_category_s { char name[MAXLEN_PATH + 1]; size_t name_len; unsigned char level_bitmap[32]; unsigned char level_bitmap_backup[32]; zc_arraylist_t *fit_rules; zc_arraylist_t *fit_rules_backup; } zlog_category_t; zlog_category_t *zlog_category_new(const char *name, zc_arraylist_t * rules); void zlog_category_del(zlog_category_t * a_category); void zlog_category_profile(zlog_category_t *a_category, int flag); int zlog_category_update_rules(zlog_category_t * a_category, zc_arraylist_t * new_rules); void zlog_category_commit_rules(zlog_category_t * a_category); void zlog_category_rollback_rules(zlog_category_t * a_category); int zlog_category_output(zlog_category_t * a_category, zlog_thread_t * a_thread); #define zlog_category_needless_level(a_category, lv) \ a_category && (zlog_env_conf->level > lv || !((a_category->level_bitmap[lv/8] >> (7 - lv % 8)) & 0x01)) #endif zlog-1.2.18/src/category_table.c000066400000000000000000000073041460570005000164760ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include "zc_defs.h" #include "category_table.h" void zlog_category_table_profile(zc_hashtable_t * categories, int flag) { zc_hashtable_entry_t *a_entry; zlog_category_t *a_category; zc_assert(categories,); zc_profile(flag, "-category_table[%p]-", categories); zc_hashtable_foreach(categories, a_entry) { a_category = (zlog_category_t *) a_entry->value; zlog_category_profile(a_category, flag); } return; } /*******************************************************************************/ void zlog_category_table_del(zc_hashtable_t * categories) { zc_assert(categories,); zc_hashtable_del(categories); zc_debug("zlog_category_table_del[%p]", categories); return; } zc_hashtable_t *zlog_category_table_new(void) { zc_hashtable_t *categories; categories = zc_hashtable_new(20, (zc_hashtable_hash_fn) zc_hashtable_str_hash, (zc_hashtable_equal_fn) zc_hashtable_str_equal, NULL, (zc_hashtable_del_fn) zlog_category_del); if (!categories) { zc_error("zc_hashtable_new fail"); return NULL; } else { zlog_category_table_profile(categories, ZC_DEBUG); return categories; } } /*******************************************************************************/ int zlog_category_table_update_rules(zc_hashtable_t * categories, zc_arraylist_t * new_rules) { zc_hashtable_entry_t *a_entry; zlog_category_t *a_category; zc_assert(categories, -1); zc_hashtable_foreach(categories, a_entry) { a_category = (zlog_category_t *) a_entry->value; if (zlog_category_update_rules(a_category, new_rules)) { zc_error("zlog_category_update_rules fail, try rollback"); return -1; } } return 0; } void zlog_category_table_commit_rules(zc_hashtable_t * categories) { zc_hashtable_entry_t *a_entry; zlog_category_t *a_category; zc_assert(categories,); zc_hashtable_foreach(categories, a_entry) { a_category = (zlog_category_t *) a_entry->value; zlog_category_commit_rules(a_category); } return; } void zlog_category_table_rollback_rules(zc_hashtable_t * categories) { zc_hashtable_entry_t *a_entry; zlog_category_t *a_category; zc_assert(categories,); zc_hashtable_foreach(categories, a_entry) { a_category = (zlog_category_t *) a_entry->value; zlog_category_rollback_rules(a_category); } return; } /*******************************************************************************/ zlog_category_t *zlog_category_table_fetch_category(zc_hashtable_t * categories, const char *category_name, zc_arraylist_t * rules) { zlog_category_t *a_category; zc_assert(categories, NULL); /* 1st find category in global category map */ a_category = zc_hashtable_get(categories, category_name); if (a_category) return a_category; /* else not found, create one */ a_category = zlog_category_new(category_name, rules); if (!a_category) { zc_error("zc_category_new fail"); return NULL; } if(zc_hashtable_put(categories, a_category->name, a_category)) { zc_error("zc_hashtable_put fail"); goto err; } return a_category; err: zlog_category_del(a_category); return NULL; } /*******************************************************************************/ zlog-1.2.18/src/category_table.h000066400000000000000000000024321460570005000165000ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __zlog_category_table_h #define __zlog_category_table_h #include "zc_defs.h" #include "category.h" zc_hashtable_t *zlog_category_table_new(void); void zlog_category_table_del(zc_hashtable_t * categories); void zlog_category_table_profile(zc_hashtable_t * categories, int flag); /* if none, create new and return */ zlog_category_t *zlog_category_table_fetch_category( zc_hashtable_t * categories, const char *category_name, zc_arraylist_t * rules); int zlog_category_table_update_rules(zc_hashtable_t * categories, zc_arraylist_t * new_rules); void zlog_category_table_commit_rules(zc_hashtable_t * categories); void zlog_category_table_rollback_rules(zc_hashtable_t * categories); #endif zlog-1.2.18/src/conf.c000066400000000000000000000534121460570005000144400ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "fmacros.h" #include #include #include #include #include #include #include #ifndef _WIN32 #include #else #include "zlog_win.h" #endif #include #include "conf.h" #include "rule.h" #include "format.h" #include "level_list.h" #include "rotater.h" #include "zc_defs.h" #ifdef _WIN32 #define STATS_FILE stat #else #define STATS_FILE lstat #endif /*******************************************************************************/ #define ZLOG_CONF_DEFAULT_FORMAT "default = \"%D %V [%p:%F:%L] %m%n\"" #define ZLOG_CONF_DEFAULT_RULE "*.* >stdout" #define ZLOG_CONF_DEFAULT_BUF_SIZE_MIN 1024 #define ZLOG_CONF_DEFAULT_BUF_SIZE_MAX (2 * 1024 * 1024) #define ZLOG_CONF_DEFAULT_FILE_PERMS 0600 #define ZLOG_CONF_DEFAULT_RELOAD_CONF_PERIOD 0 #define ZLOG_CONF_DEFAULT_FSYNC_PERIOD 0 #define ZLOG_CONF_BACKUP_ROTATE_LOCK_FILE "/tmp/zlog.lock" /*******************************************************************************/ void zlog_conf_profile(zlog_conf_t * a_conf, int flag) { int i; zlog_rule_t *a_rule; zlog_format_t *a_format; zc_assert(a_conf,); zc_profile(flag, "-conf[%p]-", a_conf); zc_profile(flag, "--global--"); zc_profile(flag, "---file[%s],mtime[%s]---", a_conf->file, a_conf->mtime); zc_profile(flag, "---in-memory conf[%s]---", a_conf->cfg_ptr); zc_profile(flag, "---strict init[%d]---", a_conf->strict_init); zc_profile(flag, "---buffer min[%ld]---", a_conf->buf_size_min); zc_profile(flag, "---buffer max[%ld]---", a_conf->buf_size_max); if (a_conf->default_format) { zc_profile(flag, "---default_format---"); zlog_format_profile(a_conf->default_format, flag); } zc_profile(flag, "---file perms[0%o]---", a_conf->file_perms); zc_profile(flag, "---reload conf period[%ld]---", a_conf->reload_conf_period); zc_profile(flag, "---fsync period[%ld]---", a_conf->fsync_period); zc_profile(flag, "---rotate lock file[%s]---", a_conf->rotate_lock_file); if (a_conf->rotater) zlog_rotater_profile(a_conf->rotater, flag); if (a_conf->levels) zlog_level_list_profile(a_conf->levels, flag); if (a_conf->formats) { zc_profile(flag, "--format list[%p]--", a_conf->formats); zc_arraylist_foreach(a_conf->formats, i, a_format) { zlog_format_profile(a_format, flag); } } if (a_conf->rules) { zc_profile(flag, "--rule_list[%p]--", a_conf->rules); zc_arraylist_foreach(a_conf->rules, i, a_rule) { zlog_rule_profile(a_rule, flag); } } return; } /*******************************************************************************/ void zlog_conf_del(zlog_conf_t * a_conf) { zc_assert(a_conf,); if (a_conf->rotater) zlog_rotater_del(a_conf->rotater); if (a_conf->levels) zlog_level_list_del(a_conf->levels); if (a_conf->default_format) zlog_format_del(a_conf->default_format); if (a_conf->formats) zc_arraylist_del(a_conf->formats); if (a_conf->rules) zc_arraylist_del(a_conf->rules); free(a_conf); zc_debug("zlog_conf_del[%p]"); return; } static int zlog_conf_build_without_file(zlog_conf_t * a_conf); static int zlog_conf_build_with_file(zlog_conf_t * a_conf); static int zlog_conf_build_with_string(zlog_conf_t *a_conf, const char *conf_string); static int zlog_conf_build_with_in_memory(zlog_conf_t * a_conf); enum{ NO_CFG, FILE_CFG, IN_MEMORY_CFG }; /*******************************************************************************/ zlog_conf_t *zlog_conf_new(const char *config) { int nwrite = 0; int cfg_source = 0; zlog_conf_t *a_conf = NULL; a_conf = calloc(1, sizeof(zlog_conf_t)); if (!a_conf) { zc_error("calloc fail, errno[%d]", errno); return NULL; } // Find content of pointer. If it starts with '[' then content are configurations. if (config && config[0] != '\0' && config[0] != '[') { nwrite = snprintf(a_conf->file, sizeof(a_conf->file), "%s", config); cfg_source = FILE_CFG; } else if (getenv("ZLOG_CONF_PATH") != NULL) { nwrite = snprintf(a_conf->file, sizeof(a_conf->file), "%s", getenv("ZLOG_CONF_PATH")); cfg_source = FILE_CFG; } else if (config && config[0]=='[') { memset(a_conf->file, 0x00, sizeof(a_conf->file)); nwrite = snprintf(a_conf->cfg_ptr, sizeof(a_conf->cfg_ptr), "%s", config); cfg_source = IN_MEMORY_CFG; if (nwrite < 0 || nwrite >= sizeof(a_conf->file)) { zc_error("not enough space for configurations, nwrite=[%d], errno[%d]", nwrite, errno); goto err; } } else { memset(a_conf->file, 0x00, sizeof(a_conf->file)); cfg_source = NO_CFG; } if ((nwrite < 0) || ((nwrite >= sizeof(a_conf->file)) && (cfg_source == FILE_CFG))) { zc_error("not enough space for path name, nwrite=[%d], errno[%d]", nwrite, errno); goto err; } /* set default configuration start */ a_conf->strict_init = 1; a_conf->buf_size_min = ZLOG_CONF_DEFAULT_BUF_SIZE_MIN; a_conf->buf_size_max = ZLOG_CONF_DEFAULT_BUF_SIZE_MAX; if (cfg_source == FILE_CFG) { /* configure file as default lock file */ strcpy(a_conf->rotate_lock_file, a_conf->file); } else { strcpy(a_conf->rotate_lock_file, ZLOG_CONF_BACKUP_ROTATE_LOCK_FILE); } strcpy(a_conf->default_format_line, ZLOG_CONF_DEFAULT_FORMAT); a_conf->file_perms = ZLOG_CONF_DEFAULT_FILE_PERMS; a_conf->reload_conf_period = ZLOG_CONF_DEFAULT_RELOAD_CONF_PERIOD; a_conf->fsync_period = ZLOG_CONF_DEFAULT_FSYNC_PERIOD; /* set default configuration end */ a_conf->levels = zlog_level_list_new(); if (!a_conf->levels) { zc_error("zlog_level_list_new fail"); goto err; } a_conf->formats = zc_arraylist_new((zc_arraylist_del_fn) zlog_format_del); if (!a_conf->formats) { zc_error("zc_arraylist_new fail"); goto err; } a_conf->rules = zc_arraylist_new((zc_arraylist_del_fn) zlog_rule_del); if (!a_conf->rules) { zc_error("init rule_list fail"); goto err; } if (cfg_source == FILE_CFG) { if (zlog_conf_build_with_file(a_conf)) { zc_error("zlog_conf_build_with_file fail"); goto err; } } else if (cfg_source == IN_MEMORY_CFG) { if(zlog_conf_build_with_in_memory(a_conf)){ zc_error("zlog_conf_build_with_in_memory fail"); goto err; } } else { if (zlog_conf_build_without_file(a_conf)) { zc_error("zlog_conf_build_without_file fail"); goto err; } } zlog_conf_profile(a_conf, ZC_DEBUG); return a_conf; err: zlog_conf_del(a_conf); return NULL; } /*******************************************************************************/ zlog_conf_t *zlog_conf_new_from_string(const char *config_string) { zlog_conf_t *a_conf = NULL; a_conf = calloc(1, sizeof(zlog_conf_t)); if (!a_conf) { zc_error("calloc fail, errno[%d]", errno); return NULL; } // no configuration file memset(a_conf->file, 0x00, sizeof(a_conf->file)); a_conf->levels = zlog_level_list_new(); if (!a_conf->levels) { zc_error("zlog_level_list_new fail"); goto err; } a_conf->formats = zc_arraylist_new((zc_arraylist_del_fn) zlog_format_del); if (!a_conf->formats) { zc_error("zc_arraylist_new fail"); goto err; } a_conf->rules = zc_arraylist_new((zc_arraylist_del_fn) zlog_rule_del); if (!a_conf->rules) { zc_error("init rule_list fail"); goto err; } strcpy(a_conf->rotate_lock_file, "zlog-rotate.lock"); a_conf->strict_init = 1; a_conf->buf_size_min = ZLOG_CONF_DEFAULT_BUF_SIZE_MIN; a_conf->buf_size_max = ZLOG_CONF_DEFAULT_BUF_SIZE_MAX; strcpy(a_conf->default_format_line, ZLOG_CONF_DEFAULT_FORMAT); a_conf->file_perms = ZLOG_CONF_DEFAULT_FILE_PERMS; a_conf->reload_conf_period = ZLOG_CONF_DEFAULT_RELOAD_CONF_PERIOD; a_conf->fsync_period = ZLOG_CONF_DEFAULT_FSYNC_PERIOD; a_conf->default_format = zlog_format_new(a_conf->default_format_line, &(a_conf->time_cache_count)); if (!a_conf->default_format) { zc_error("zlog_format_new fail"); goto err; } a_conf->rotater = zlog_rotater_new(a_conf->rotate_lock_file); if (!a_conf->rotater) { zc_error("zlog_rotater_new fail"); goto err; } if (zlog_conf_build_with_string(a_conf, config_string)) { zc_error("zlog_conf_build_with_string fail"); goto err; } zlog_conf_profile(a_conf, ZC_DEBUG); return a_conf; err: zlog_conf_del(a_conf); return NULL; } /*******************************************************************************/ static int zlog_conf_build_without_file(zlog_conf_t * a_conf) { zlog_rule_t *default_rule; a_conf->default_format = zlog_format_new(a_conf->default_format_line, &(a_conf->time_cache_count)); if (!a_conf->default_format) { zc_error("zlog_format_new fail"); return -1; } a_conf->rotater = zlog_rotater_new(a_conf->rotate_lock_file); if (!a_conf->rotater) { zc_error("zlog_rotater_new fail"); return -1; } default_rule = zlog_rule_new( ZLOG_CONF_DEFAULT_RULE, a_conf->levels, a_conf->default_format, a_conf->formats, a_conf->file_perms, a_conf->fsync_period, &(a_conf->time_cache_count)); if (!default_rule) { zc_error("zlog_rule_new fail"); return -1; } /* add default rule */ if (zc_arraylist_add(a_conf->rules, default_rule)) { zlog_rule_del(default_rule); zc_error("zc_arraylist_add fail"); return -1; } return 0; } /*******************************************************************************/ static int zlog_conf_parse_line(zlog_conf_t * a_conf, char *line, int *section); char *sgets(char *s, int size, char **string) { if (*string == NULL) return NULL; char* nlp = strchr(*string, '\n'); char *fstring = *string; if (nlp == NULL) { if (strlen(fstring) > 0) { nlp = fstring + strlen(fstring); } else { return NULL; } } int ss = (int)(nlp + 1 - fstring); if (size > ss) size = ss; memcpy(s, *string, size); s[size] = 0; if (strlen(*string) == strlen(s)) { *string = NULL; } else { *string += size; } return s; } static int zlog_conf_build_with_string(zlog_conf_t *a_conf, const char *conf_string) { int rc = 0; char line[MAXLEN_CFG_LINE + 1]; size_t line_len; char *pline = NULL; char *p = NULL; int line_no = 0; int i = 0; int in_quotation = 0; char *conf_string_l = (char*) conf_string; int section = 0; /* [global:1] [levels:2] [formats:3] [rules:4] */ if (a_conf == NULL) { return -1; } /* Now process the file. */ pline = line; memset(&line, 0x00, sizeof(line)); while (sgets(pline, MAXLEN_CFG_LINE, &conf_string_l) != NULL) { ++line_no; line_len = strlen(pline); if (pline[line_len - 1] == '\n') { pline[line_len - 1] = '\0'; } /* check for end-of-section, comments, strip off trailing * spaces and newline character. */ p = pline; while (*p && isspace((int)*p)) ++p; if (*p == '\0' || *p == '#') continue; for (i = 0; p[i] != '\0'; ++i) { pline[i] = p[i]; } pline[i] = '\0'; for (p = pline + strlen(pline) - 1; isspace((int)*p); --p) /*EMPTY*/; if (*p == '\\') { if ((p - line) > MAXLEN_CFG_LINE - 30) { /* Oops the buffer is full - what now? */ pline = line; } else { for (p--; isspace((int)*p); --p) /*EMPTY*/; p++; *p = 0; pline = p; continue; } } else { memmove(line, pline, strlen(pline)+1); } *++p = '\0'; /* clean the tail comments start from # and not in quotation */ in_quotation = 0; for (p = line; *p != '\0'; p++) { if (*p == '"') { in_quotation ^= 1; continue; } if (*p == '#' && !in_quotation) { *p = '\0'; break; } } /* we now have the complete line, * and are positioned at the first non-whitespace * character. So let's process it */ rc = zlog_conf_parse_line(a_conf, line, §ion); if (rc < 0) { zc_error("parse configure file[%s]line_no[%ld] fail", a_conf->file, line_no); zc_error("line[%s]", line); goto exit; } else if (rc > 0) { zc_warn("parse configure file[%s]line_no[%ld] fail", a_conf->file, line_no); zc_warn("line[%s]", line); zc_warn("as strict init is set to false, ignore and go on"); rc = 0; continue; } } exit: return rc; } static int zlog_conf_build_with_file(zlog_conf_t * a_conf) { int rc = 0; struct zlog_stat a_stat; struct tm local_time; FILE *fp = NULL; char line[MAXLEN_CFG_LINE + 1]; size_t line_len; char *pline = NULL; char *p = NULL; int line_no = 0; int i = 0; int in_quotation = 0; int section = 0; /* [global:1] [levels:2] [formats:3] [rules:4] */ if (STATS_FILE(a_conf->file, &a_stat)) { zc_error("lstat conf file[%s] fail, errno[%d]", a_conf->file, errno); return -1; } localtime_r(&(a_stat.st_mtime), &local_time); strftime(a_conf->mtime, sizeof(a_conf->mtime), "%Y-%m-%d %H:%M:%S", &local_time); if ((fp = fopen(a_conf->file, "r")) == NULL) { zc_error("open configure file[%s] fail", a_conf->file); return -1; } a_conf->log_level[0] = '\0'; /* Now process the file. */ pline = line; memset(&line, 0x00, sizeof(line)); while (fgets((char *)pline, sizeof(line) - (pline - line), fp) != NULL) { ++line_no; line_len = strlen(pline); if (0 == line_len) { continue; } if (pline[line_len - 1] == '\n') { pline[line_len - 1] = '\0'; } /* check for end-of-section, comments, strip off trailing * spaces and newline character. */ p = pline; while (*p && isspace((int)*p)) ++p; if (*p == '\0' || *p == '#') continue; for (i = 0; p[i] != '\0'; ++i) { pline[i] = p[i]; } pline[i] = '\0'; for (p = pline + strlen(pline) - 1; isspace((int)*p); --p) /*EMPTY*/; if (*p == '\\') { if ((p - line) > MAXLEN_CFG_LINE - 30) { /* Oops the buffer is full - what now? */ pline = line; } else { for (p--; p >= line && isspace((int)*p); --p) /*EMPTY*/; p++; *p = 0; pline = p; continue; } } else pline = line; *++p = '\0'; /* clean the tail comments start from # and not in quotation */ in_quotation = 0; for (p = line; *p != '\0'; p++) { if (*p == '"') { in_quotation ^= 1; continue; } if (*p == '#' && !in_quotation) { *p = '\0'; break; } } /* we now have the complete line, * and are positioned at the first non-whitespace * character. So let's process it */ rc = zlog_conf_parse_line(a_conf, line, §ion); if (rc < 0) { zc_error("parse configure file[%s]line_no[%ld] fail", a_conf->file, line_no); zc_error("line[%s]", line); goto exit; } else if (rc > 0) { zc_warn("parse configure file[%s]line_no[%ld] fail", a_conf->file, line_no); zc_warn("line[%s]", line); zc_warn("as strict init is set to false, ignore and go on"); rc = 0; continue; } } a_conf->level = zlog_level_list_atoi(a_conf->levels, a_conf->log_level); exit: fclose(fp); return rc; } /**********************************************************************/ static int zlog_conf_build_with_in_memory(zlog_conf_t * a_conf) { int rc = 0; char line[MAXLEN_CFG_LINE + 1]; char *pline = NULL; int section = 0; pline = line; memset(&line, 0x00, sizeof(line)); pline = strtok((char *)a_conf->cfg_ptr, "\n"); while (pline != NULL) { rc = zlog_conf_parse_line(a_conf, pline, §ion); if (rc < 0) { zc_error("parse in-memory configurations[%s] line [%s] fail", a_conf->cfg_ptr, pline); break; } else if (rc > 0) { zc_error("parse in-memory configurations[%s] line [%s] fail", a_conf->cfg_ptr, pline); zc_warn("as strict init is set to false, ignore and go on"); rc = 0; continue; } pline = strtok(NULL, "\n"); } return rc; } /* section [global:1] [levels:2] [formats:3] [rules:4] */ static int zlog_conf_parse_line(zlog_conf_t * a_conf, char *line, int *section) { int nscan; int nread; char name[MAXLEN_CFG_LINE + 1] = ""; char word_1[MAXLEN_CFG_LINE + 1]; char word_2[MAXLEN_CFG_LINE + 1]; char word_3[MAXLEN_CFG_LINE + 1]; char value[MAXLEN_CFG_LINE + 1]; zlog_format_t *a_format = NULL; zlog_rule_t *a_rule = NULL; if (strlen(line) > MAXLEN_CFG_LINE) { zc_error ("line_len[%ld] > MAXLEN_CFG_LINE[%ld], may cause overflow", strlen(line), MAXLEN_CFG_LINE); return -1; } /* get and set outer section flag, so it is a closure? haha */ if (line[0] == '[') { int last_section = *section; nscan = sscanf(line, "[ %[^] \t]", name); if (STRCMP(name, ==, "global")) { *section = 1; } else if (STRCMP(name, ==, "levels")) { *section = 2; } else if (STRCMP(name, ==, "formats")) { *section = 3; } else if (STRCMP(name, ==, "rules")) { *section = 4; } else { zc_error("wrong section name[%s]", name); return -1; } /* check the sequence of section, must increase */ if (last_section >= *section) { zc_error("wrong sequence of section, must follow global->levels->formats->rules"); return -1; } if (*section == 4) { if (a_conf->reload_conf_period != 0 && a_conf->fsync_period >= a_conf->reload_conf_period) { /* as all rule will be rebuilt when conf is reload, * so fsync_period > reload_conf_period will never * cause rule to fsync it's file. * fsync_period will be meaningless and down speed, * so make it zero. */ zc_warn("fsync_period[%ld] >= reload_conf_period[%ld]," "set fsync_period to zero"); a_conf->fsync_period = 0; } /* now build rotater and default_format * from the unchanging global setting, * for zlog_rule_new() */ a_conf->rotater = zlog_rotater_new(a_conf->rotate_lock_file); if (!a_conf->rotater) { zc_error("zlog_rotater_new fail"); return -1; } a_conf->default_format = zlog_format_new(a_conf->default_format_line, &(a_conf->time_cache_count)); if (!a_conf->default_format) { zc_error("zlog_format_new fail"); return -1; } } return 0; } /* process detail */ switch (*section) { case 1: memset(name, 0x00, sizeof(name)); memset(value, 0x00, sizeof(value)); nscan = sscanf(line, " %[^=]= %s ", name, value); if (nscan != 2) { zc_error("sscanf [%s] fail, name or value is null", line); return -1; } memset(word_1, 0x00, sizeof(word_1)); memset(word_2, 0x00, sizeof(word_2)); memset(word_3, 0x00, sizeof(word_3)); nread = 0; nscan = sscanf(name, "%s%n%s%s", word_1, &nread, word_2, word_3); if (STRCMP(word_1, ==, "strict") && STRCMP(word_2, ==, "init")) { /* if environment variable ZLOG_STRICT_INIT is set * then always make it strict */ if (STRICMP(value, ==, "false") && !getenv("ZLOG_STRICT_INIT")) { a_conf->strict_init = 0; } else { a_conf->strict_init = 1; } } else if (STRCMP(word_1, ==, "log") && STRCMP(word_2, ==, "level")) { strcpy(a_conf->log_level, value); } else if (STRCMP(word_1, ==, "buffer") && STRCMP(word_2, ==, "min")) { a_conf->buf_size_min = zc_parse_byte_size(value); } else if (STRCMP(word_1, ==, "buffer") && STRCMP(word_2, ==, "max")) { a_conf->buf_size_max = zc_parse_byte_size(value); } else if (STRCMP(word_1, ==, "file") && STRCMP(word_2, ==, "perms")) { sscanf(value, "%o", &(a_conf->file_perms)); } else if (STRCMP(word_1, ==, "rotate") && STRCMP(word_2, ==, "lock") && STRCMP(word_3, ==, "file")) { /* may overwrite the inner default value, or last value */ if (STRCMP(value, ==, "self")) { strcpy(a_conf->rotate_lock_file, a_conf->file); } else { strcpy(a_conf->rotate_lock_file, value); } } else if (STRCMP(word_1, ==, "default") && STRCMP(word_2, ==, "format")) { /* so the input now is [format = "xxyy"], fit format's style */ strcpy(a_conf->default_format_line, line + nread); } else if (STRCMP(word_1, ==, "reload") && STRCMP(word_2, ==, "conf") && STRCMP(word_3, ==, "period")) { a_conf->reload_conf_period = zc_parse_byte_size(value); } else if (STRCMP(word_1, ==, "fsync") && STRCMP(word_2, ==, "period")) { a_conf->fsync_period = zc_parse_byte_size(value); } else { zc_error("name[%s] is not any one of global options", name); if (a_conf->strict_init) return -1; } break; case 2: if (zlog_level_list_set(a_conf->levels, line)) { zc_error("zlog_level_list_set fail"); if (a_conf->strict_init) return -1; } break; case 3: a_format = zlog_format_new(line, &(a_conf->time_cache_count)); if (!a_format) { zc_error("zlog_format_new fail [%s]", line); if (a_conf->strict_init) return -1; else break; } if (zc_arraylist_add(a_conf->formats, a_format)) { zlog_format_del(a_format); zc_error("zc_arraylist_add fail"); return -1; } break; case 4: a_rule = zlog_rule_new(line, a_conf->levels, a_conf->default_format, a_conf->formats, a_conf->file_perms, a_conf->fsync_period, &(a_conf->time_cache_count)); if (!a_rule) { zc_error("zlog_rule_new fail [%s]", line); if (a_conf->strict_init) return -1; else break; } if (zc_arraylist_add(a_conf->rules, a_rule)) { zlog_rule_del(a_rule); zc_error("zc_arraylist_add fail"); return -1; } break; default: zc_error("not in any section"); return -1; } return 0; } /*******************************************************************************/ zlog-1.2.18/src/conf.h000066400000000000000000000030101460570005000144320ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __zlog_conf_h #define __zlog_conf_h #include "zc_defs.h" #include "format.h" #include "rotater.h" typedef struct zlog_conf_s { char file[MAXLEN_PATH + 1]; char cfg_ptr[MAXLEN_CFG_LINE*MAXLINES_NO]; char mtime[20 + 1]; int strict_init; size_t buf_size_min; size_t buf_size_max; char rotate_lock_file[MAXLEN_CFG_LINE + 1]; zlog_rotater_t *rotater; char default_format_line[MAXLEN_CFG_LINE + 1]; zlog_format_t *default_format; unsigned int file_perms; size_t fsync_period; size_t reload_conf_period; zc_arraylist_t *levels; zc_arraylist_t *formats; zc_arraylist_t *rules; int time_cache_count; char log_level[MAXLEN_CFG_LINE + 1]; int level; } zlog_conf_t; extern zlog_conf_t * zlog_env_conf; zlog_conf_t *zlog_conf_new(const char *config); zlog_conf_t *zlog_conf_new_from_string(const char *config_string); void zlog_conf_del(zlog_conf_t * a_conf); void zlog_conf_profile(zlog_conf_t * a_conf, int flag); #endif zlog-1.2.18/src/event.c000066400000000000000000000124171460570005000146340ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define _GNU_SOURCE // For distros like Centos for syscall interface #include "fmacros.h" #include #include #include #include #include #include #include #include #include #include #include "zc_defs.h" #include "event.h" #ifdef _WIN32 #include #endif void zlog_event_profile(zlog_event_t * a_event, int flag) { zc_assert(a_event,); zc_profile(flag, "---event[%p][%s,%s][%s(%ld),%s(%ld),%ld,%d][%p,%s][%ld,%ld][%ld,%ld][%d]---", a_event, a_event->category_name, a_event->host_name, a_event->file, a_event->file_len, a_event->func, a_event->func_len, a_event->line, a_event->level, a_event->hex_buf, a_event->str_format, a_event->time_stamp.tv_sec, a_event->time_stamp.tv_usec, (long)a_event->pid, (long)a_event->tid, a_event->time_cache_count); return; } /*******************************************************************************/ void zlog_event_del(zlog_event_t * a_event) { zc_assert(a_event,); if (a_event->time_caches) free(a_event->time_caches); zc_debug("zlog_event_del[%p]", a_event); free(a_event); return; } zlog_event_t *zlog_event_new(int time_cache_count) { zlog_event_t *a_event; a_event = calloc(1, sizeof(zlog_event_t)); if (!a_event) { zc_error("calloc fail, errno[%d]", errno); return NULL; } a_event->time_caches = calloc(time_cache_count, sizeof(zlog_time_cache_t)); if (!a_event->time_caches) { zc_error("calloc fail, errno[%d]", errno); free(a_event); return NULL; } a_event->time_cache_count = time_cache_count; /* * at the zlog_init we gethostname, * u don't always change your hostname, eh? */ if (gethostname(a_event->host_name, sizeof(a_event->host_name) - 1)) { zc_error("gethostname fail, errno[%d]", errno); goto err; } a_event->host_name_len = strlen(a_event->host_name); /* tid is bound to a_event * as in whole lifecycle event persists * even fork to oth pid, tid not change */ a_event->tid = pthread_self(); a_event->tid_str_len = sprintf(a_event->tid_str, "%lu", (unsigned long)a_event->tid); a_event->tid_hex_str_len = sprintf(a_event->tid_hex_str, "%x", (unsigned int)a_event->tid); #ifdef __linux__ a_event->ktid = syscall(SYS_gettid); #elif __APPLE__ uint64_t tid64; pthread_threadid_np(NULL, &tid64); a_event->tid = (pthread_t)tid64; #endif #if defined __linux__ || __APPLE__ a_event->ktid_str_len = sprintf(a_event->ktid_str, "%u", (unsigned int)a_event->ktid); #endif //zlog_event_profile(a_event, ZC_DEBUG); return a_event; err: zlog_event_del(a_event); return NULL; } /*******************************************************************************/ void zlog_event_set_fmt(zlog_event_t * a_event, char *category_name, size_t category_name_len, const char *file, size_t file_len, const char *func, size_t func_len, long line, int level, const char *str_format, va_list str_args) { /* * category_name point to zlog_category_output's category.name */ a_event->category_name = category_name; a_event->category_name_len = category_name_len; a_event->file = (char *) file; a_event->file_len = file_len; a_event->func = (char *) func; a_event->func_len = func_len; a_event->line = line; a_event->level = level; a_event->generate_cmd = ZLOG_FMT; a_event->str_format = str_format; va_copy(a_event->str_args, str_args); /* pid should fetch eveytime, as no one knows, * when does user fork his process * so clean here, and fetch at spec.c */ a_event->pid = (pid_t) 0; /* in a event's life cycle, time will be get when spec need, * and keep unchange though all event's life cycle * zlog_spec_write_time gettimeofday */ a_event->time_stamp.tv_sec = 0; return; } void zlog_event_set_hex(zlog_event_t * a_event, char *category_name, size_t category_name_len, const char *file, size_t file_len, const char *func, size_t func_len, long line, int level, const void *hex_buf, size_t hex_buf_len) { /* * category_name point to zlog_category_output's category.name */ a_event->category_name = category_name; a_event->category_name_len = category_name_len; a_event->file = (char *) file; a_event->file_len = file_len; a_event->func = (char *) func; a_event->func_len = func_len; a_event->line = line; a_event->level = level; a_event->generate_cmd = ZLOG_HEX; a_event->hex_buf = hex_buf; a_event->hex_buf_len = hex_buf_len; /* pid should fetch eveytime, as no one knows, * when does user fork his process * so clean here, and fetch at spec.c */ a_event->pid = (pid_t) 0; /* in a event's life cycle, time will be get when spec need, * and keep unchange though all event's life cycle */ a_event->time_stamp.tv_sec = 0; return; } zlog-1.2.18/src/event.h000066400000000000000000000046561460570005000146470ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __zlog_event_h #define __zlog_event_h #include /* for pid_t */ #include /* for struct timeval */ #include /* for pthread_t */ #include /* for va_list */ #include "zc_defs.h" typedef enum { ZLOG_FMT = 0, ZLOG_HEX = 1, } zlog_event_cmd; typedef struct zlog_time_cache_s { char str[MAXLEN_CFG_LINE + 1]; size_t len; time_t sec; } zlog_time_cache_t; typedef struct { char *category_name; size_t category_name_len; char host_name[256 + 1]; size_t host_name_len; const char *file; size_t file_len; const char *func; size_t func_len; long line; int level; const void *hex_buf; size_t hex_buf_len; const char *str_format; va_list str_args; zlog_event_cmd generate_cmd; struct timeval time_stamp; time_t time_utc_sec; struct tm time_utc; time_t time_local_sec; struct tm time_local; zlog_time_cache_t *time_caches; int time_cache_count; pid_t pid; pid_t last_pid; char pid_str[30 + 1]; size_t pid_str_len; pthread_t tid; char tid_str[30 + 1]; size_t tid_str_len; char tid_hex_str[30 + 1]; size_t tid_hex_str_len; #if defined __linux__ || __APPLE__ pid_t ktid; char ktid_str[30+1]; size_t ktid_str_len; #endif } zlog_event_t; zlog_event_t *zlog_event_new(int time_cache_count); void zlog_event_del(zlog_event_t * a_event); void zlog_event_profile(zlog_event_t * a_event, int flag); void zlog_event_set_fmt(zlog_event_t * a_event, char *category_name, size_t category_name_len, const char *file, size_t file_len, const char *func, size_t func_len, long line, int level, const char *str_format, va_list str_args); void zlog_event_set_hex(zlog_event_t * a_event, char *category_name, size_t category_name_len, const char *file, size_t file_len, const char *func, size_t func_len, long line, int level, const void *hex_buf, size_t hex_buf_len); #endif zlog-1.2.18/src/fmacros.h000066400000000000000000000013201460570005000151410ustar00rootroot00000000000000#ifndef __zlog_fmacro_h #define __zlog_fmacro_h #ifndef _BSD_SOURCE #define _BSD_SOURCE #endif #ifndef _DEFAULT_SOURCE #define _DEFAULT_SOURCE #endif #if defined(__linux__) || defined(__OpenBSD__) || defined(_AIX) #ifndef _XOPEN_SOURCE #define _XOPEN_SOURCE 700 #endif #ifndef _XOPEN_SOURCE_EXTENDED #define _XOPEN_SOURCE_EXTENDED #endif #if defined(__APPLE__) #include #else #ifndef _POSIX_C_SOURCE #define _POSIX_C_SOURCE 200809L #endif #endif #ifndef _XOPEN_SOURCE #define _XOPEN_SOURCE #endif #endif #ifndef _LARGEFILE_SOURCE #define _LARGEFILE_SOURCE #endif #ifndef _LARGEFILE_SOURCE #define _LARGEFILE_SOURCE #endif #ifndef _FILE_OFFSET_BITS #define _FILE_OFFSET_BITS 64 #endif #endif zlog-1.2.18/src/format.c000066400000000000000000000077671460570005000150170ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include "zc_defs.h" #include "thread.h" #include "spec.h" #include "format.h" void zlog_format_profile(zlog_format_t * a_format, int flag) { zc_assert(a_format,); zc_profile(flag, "---format[%p][%s = %s(%p)]---", a_format, a_format->name, a_format->pattern, a_format->pattern_specs); #if 0 int i; zlog_spec_t *a_spec; zc_arraylist_foreach(a_format->pattern_specs, i, a_spec) { zlog_spec_profile(a_spec, flag); } #endif return; } /*******************************************************************************/ void zlog_format_del(zlog_format_t * a_format) { zc_assert(a_format,); if (a_format->pattern_specs) { zc_arraylist_del(a_format->pattern_specs); } zc_debug("zlog_format_del[%p]", a_format); free(a_format); return; } zlog_format_t *zlog_format_new(char *line, int * time_cache_count) { int nscan = 0; zlog_format_t *a_format = NULL; int nread = 0; const char *p_start; const char *p_end; char *p; char *q; zlog_spec_t *a_spec; zc_assert(line, NULL); a_format = calloc(1, sizeof(zlog_format_t)); if (!a_format) { zc_error("calloc fail, errno[%d]", errno); return NULL; } /* line default = "%d(%F %X.%l) %-6V (%c:%F:%L) - %m%n" * name default * pattern %d(%F %X.%l) %-6V (%c:%F:%L) - %m%n */ memset(a_format->name, 0x00, sizeof(a_format->name)); nread = 0; nscan = sscanf(line, " %[^= \t] = %n", a_format->name, &nread); if (nscan != 1) { zc_error("format[%s], syntax wrong", line); goto err; } if (*(line + nread) != '"') { zc_error("the 1st char of pattern is not \", line+nread[%s]", line+nread); goto err; } for (p = a_format->name; *p != '\0'; p++) { if ((!isalnum(*p)) && (*p != '_')) { zc_error("a_format->name[%s] character is not in [a-Z][0-9][_]", a_format->name); goto err; } } p_start = line + nread + 1; p_end = strrchr(p_start, '"'); if (!p_end) { zc_error("there is no \" at end of pattern, line[%s]", line); goto err; } if (p_end - p_start > sizeof(a_format->pattern) - 1) { zc_error("pattern is too long"); goto err; } memset(a_format->pattern, 0x00, sizeof(a_format->pattern)); memcpy(a_format->pattern, p_start, p_end - p_start); if (zc_str_replace_env(a_format->pattern, sizeof(a_format->pattern))) { zc_error("zc_str_replace_env fail"); goto err; } a_format->pattern_specs = zc_arraylist_new((zc_arraylist_del_fn) zlog_spec_del); if (!(a_format->pattern_specs)) { zc_error("zc_arraylist_new fail"); goto err; } for (p = a_format->pattern; *p != '\0'; p = q) { a_spec = zlog_spec_new(p, &q, time_cache_count); if (!a_spec) { zc_error("zlog_spec_new fail"); goto err; } if (zc_arraylist_add(a_format->pattern_specs, a_spec)) { zlog_spec_del(a_spec); zc_error("zc_arraylist_add fail"); goto err; } } zlog_format_profile(a_format, ZC_DEBUG); return a_format; err: zlog_format_del(a_format); return NULL; } /*******************************************************************************/ /* return 0 success, or buf is full * return -1 fail */ int zlog_format_gen_msg(zlog_format_t * a_format, zlog_thread_t * a_thread) { int i; zlog_spec_t *a_spec; zlog_buf_restart(a_thread->msg_buf); zc_arraylist_foreach(a_format->pattern_specs, i, a_spec) { if (zlog_spec_gen_msg(a_spec, a_thread) == 0) { continue; } else { return -1; } } return 0; } zlog-1.2.18/src/format.h000066400000000000000000000022721460570005000150060ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __zlog_format_h #define __zlog_format_h #include "thread.h" #include "zc_defs.h" typedef struct zlog_format_s zlog_format_t; struct zlog_format_s { char name[MAXLEN_CFG_LINE + 1]; char pattern[MAXLEN_CFG_LINE + 1]; zc_arraylist_t *pattern_specs; }; zlog_format_t *zlog_format_new(char *line, int * time_cache_count); void zlog_format_del(zlog_format_t * a_format); void zlog_format_profile(zlog_format_t * a_format, int flag); int zlog_format_gen_msg(zlog_format_t * a_format, zlog_thread_t * a_thread); #define zlog_format_has_name(a_format, fname) \ STRCMP(a_format->name, ==, fname) #endif zlog-1.2.18/src/level.c000066400000000000000000000067201460570005000146220ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #ifndef _WIN32 #include #else #include "zlog_win.h" #endif #include "zc_defs.h" #include "level.h" void zlog_level_profile(zlog_level_t *a_level, int flag) { zc_assert(a_level,); zc_profile(flag, "---level[%p][%d,%s,%s,%d,%d]---", a_level, a_level->int_level, a_level->str_uppercase, a_level->str_lowercase, (int) a_level->str_len, a_level->syslog_level); return; } /*******************************************************************************/ void zlog_level_del(zlog_level_t *a_level) { zc_assert(a_level,); zc_debug("zlog_level_del[%p]", a_level); free(a_level); return; } static int syslog_level_atoi(char *str) { /* guess no unix system will choose -187 * as its syslog level, so it is a safe return value */ zc_assert(str, -187); if (STRICMP(str, ==, "LOG_EMERG")) return LOG_EMERG; if (STRICMP(str, ==, "LOG_ALERT")) return LOG_ALERT; if (STRICMP(str, ==, "LOG_CRIT")) return LOG_CRIT; if (STRICMP(str, ==, "LOG_ERR")) return LOG_ERR; if (STRICMP(str, ==, "LOG_WARNING")) return LOG_WARNING; if (STRICMP(str, ==, "LOG_NOTICE")) return LOG_NOTICE; if (STRICMP(str, ==, "LOG_INFO")) return LOG_INFO; if (STRICMP(str, ==, "LOG_DEBUG")) return LOG_DEBUG; zc_error("wrong syslog level[%s]", str); return -187; } /* line: TRACE = 10, LOG_ERR */ zlog_level_t *zlog_level_new(char *line) { zlog_level_t *a_level = NULL; int i; int nscan; char str[MAXLEN_CFG_LINE + 1]; int l = 0; char sl[MAXLEN_CFG_LINE + 1]; zc_assert(line, NULL); memset(str, 0x00, sizeof(str)); memset(sl, 0x00, sizeof(sl)); nscan = sscanf(line, " %[^= \t] = %d ,%s", str, &l, sl); if (nscan < 2) { zc_error("level[%s], syntax wrong", line); return NULL; } /* check level and str */ if ((l < 0) || (l > 255)) { zc_error("l[%d] not in [0,255], wrong", l); return NULL; } if (str[0] == '\0') { zc_error("str[0] = 0"); return NULL; } a_level = calloc(1, sizeof(zlog_level_t)); if (!a_level) { zc_error("calloc fail, errno[%d]", errno); return NULL; } a_level->int_level = l; /* fill syslog level */ if (sl[0] == '\0') { a_level->syslog_level = LOG_DEBUG; } else { a_level->syslog_level = syslog_level_atoi(sl); if (a_level->syslog_level == -187) { zc_error("syslog_level_atoi fail"); goto err; } } /* strncpy and toupper(str) */ for (i = 0; (i < sizeof(a_level->str_uppercase) - 1) && str[i] != '\0'; i++) { (a_level->str_uppercase)[i] = toupper(str[i]); (a_level->str_lowercase)[i] = tolower(str[i]); } if (str[i] != '\0') { /* overflow */ zc_error("not enough space for str, str[%s] > %d", str, i); goto err; } else { (a_level->str_uppercase)[i] = '\0'; (a_level->str_lowercase)[i] = '\0'; } a_level->str_len = i; //zlog_level_profile(a_level, ZC_DEBUG); return a_level; err: zc_error("line[%s]", line); zlog_level_del(a_level); return NULL; } zlog-1.2.18/src/level.h000066400000000000000000000017501460570005000146250ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __zlog_level_h #define __zlog_level_h #include "zc_defs.h" typedef struct zlog_level_s { int int_level; char str_uppercase[MAXLEN_PATH + 1]; char str_lowercase[MAXLEN_PATH + 1]; size_t str_len; int syslog_level; } zlog_level_t; zlog_level_t *zlog_level_new(char *line); void zlog_level_del(zlog_level_t *a_level); void zlog_level_profile(zlog_level_t *a_level, int flag); #endif zlog-1.2.18/src/level_list.c000066400000000000000000000073311460570005000156540ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #ifdef _WIN32 #include "zlog_win.h" #endif #include "zc_defs.h" #include "level.h" #include "level_list.h" /* zlog_level_list == zc_arraylist_t */ void zlog_level_list_profile(zc_arraylist_t *levels, int flag) { int i; zlog_level_t *a_level; zc_assert(levels,); zc_profile(flag, "--level_list[%p]--", levels); zc_arraylist_foreach(levels, i, a_level) { /* skip empty slots */ if (a_level) zlog_level_profile(a_level, flag); } return; } /*******************************************************************************/ void zlog_level_list_del(zc_arraylist_t *levels) { zc_assert(levels,); zc_arraylist_del(levels); zc_debug("zc_level_list_del[%p]", levels); return; } static int zlog_level_list_set_default(zc_arraylist_t *levels) { return zlog_level_list_set(levels, "* = 0, LOG_INFO") || zlog_level_list_set(levels, "DEBUG = 20, LOG_DEBUG") || zlog_level_list_set(levels, "INFO = 40, LOG_INFO") || zlog_level_list_set(levels, "NOTICE = 60, LOG_NOTICE") || zlog_level_list_set(levels, "WARN = 80, LOG_WARNING") || zlog_level_list_set(levels, "ERROR = 100, LOG_ERR") || zlog_level_list_set(levels, "FATAL = 120, LOG_ALERT") || zlog_level_list_set(levels, "UNKNOWN = 254, LOG_ERR") || zlog_level_list_set(levels, "! = 255, LOG_INFO"); } zc_arraylist_t *zlog_level_list_new(void) { zc_arraylist_t *levels; levels = zc_arraylist_new((zc_arraylist_del_fn)zlog_level_del); if (!levels) { zc_error("zc_arraylist_new fail"); return NULL; } if (zlog_level_list_set_default(levels)) { zc_error("zlog_level_set_default fail"); goto err; } //zlog_level_list_profile(levels, ZC_DEBUG); return levels; err: zc_arraylist_del(levels); return NULL; } /*******************************************************************************/ int zlog_level_list_set(zc_arraylist_t *levels, char *line) { zlog_level_t *a_level; a_level = zlog_level_new(line); if (!a_level) { zc_error("zlog_level_new fail"); return -1; } if (zc_arraylist_set(levels, a_level->int_level, a_level)) { zc_error("zc_arraylist_set fail"); goto err; } return 0; err: zc_error("line[%s]", line); zlog_level_del(a_level); return -1; } zlog_level_t *zlog_level_list_get(zc_arraylist_t *levels, int l) { zlog_level_t *a_level; #if 0 if ((l <= 0) || (l > 254)) { /* illegal input from zlog() */ zc_error("l[%d] not in (0,254), set to UNKOWN", l); l = 254; } #endif a_level = zc_arraylist_get(levels, l); if (a_level) { return a_level; } else { /* empty slot */ zc_error("l[%d] not in (0,254), or has no level defined," "see configure file define, set to UNKOWN", l); return zc_arraylist_get(levels, 254); } } /*******************************************************************************/ int zlog_level_list_atoi(zc_arraylist_t *levels, char *str) { int i; zlog_level_t *a_level; if (str == NULL || *str == '\0') { zc_error("str is [%s], can't find level", str); return -1; } zc_arraylist_foreach(levels, i, a_level) { if (a_level && STRICMP(str, ==, a_level->str_uppercase)) { return i; } } zc_error("str[%s] can't found in level list", str); return -1; } zlog-1.2.18/src/level_list.h000066400000000000000000000023671460570005000156650ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __zlog_level_list_h #define __zlog_level_list_h #include "zc_defs.h" #include "level.h" zc_arraylist_t *zlog_level_list_new(void); void zlog_level_list_del(zc_arraylist_t *levels); void zlog_level_list_profile(zc_arraylist_t *levels, int flag); /* conf init use, slow */ /* if l is wrong or str=="", return -1 */ int zlog_level_list_set(zc_arraylist_t *levels, char *line); /* spec output use, fast */ /* rule output use, fast */ /* if not found, return levels[254] */ zlog_level_t *zlog_level_list_get(zc_arraylist_t *levels, int l); /* rule init use, slow */ /* if not found, return -1 */ int zlog_level_list_atoi(zc_arraylist_t *levels, char *str); #endif zlog-1.2.18/src/lockfile.c000066400000000000000000000025241460570005000153010ustar00rootroot00000000000000/** * ============================================================================= * * \file lockfile.c * \breif * \version 1.0 * \date 2013-07-07 11:55:38 * \author Song min.Li (Li), lisongmin@126.com * \copyright Copyright (c) 2013, skybility * * ============================================================================= */ #include "lockfile.h" #include "zc_profile.h" LOCK_FD lock_file(char* path) { if (!path || strlen(path) <= 0) { return INVALID_LOCK_FD; } #ifdef _WIN32 LOCK_FD fd = CreateFile(path, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (fd == INVALID_LOCK_FD) { DWORD err = GetLastError(); zc_error("lock file error : %d ", err); } #else LOCK_FD fd = open(path, O_RDWR | O_CREAT, S_IRWXU | S_IRWXG | S_IRWXO); if (fd == INVALID_LOCK_FD) { zc_error("lock file error : %s ", strerror(errno)); } #endif return fd; } bool unlock_file(LOCK_FD fd) { if (fd == INVALID_LOCK_FD) { return true; } #ifdef _WIN32 bool ret = CloseHandle(fd); if (ret == false) { DWORD err = GetLastError(); zc_error("unlock file error : %d ", err); } #else bool ret = close(fd) == 0; if (ret == false) { zc_error("unlock file error : %s ", strerror(errno)); } #endif return ret; }zlog-1.2.18/src/lockfile.h000066400000000000000000000016001460570005000153000ustar00rootroot00000000000000/** * ============================================================================= * * \file lockfile.h * \breif * \version 1.0 * \date 2013-07-07 12:34:20 * \author Song min.Li (Li), lisongmin@126.com * \copyright Copyright (c) 2013, skybility * * ============================================================================= */ #ifndef __ZLOG_LOCK_FILE_H__ #define __ZLOG_LOCK_FILE_H__ #ifdef _WIN32 #include #include #define LOCK_FD HANDLE #define INVALID_LOCK_FD INVALID_HANDLE_VALUE #else //_WIN32 #include #include #include #include #include #include #define LOCK_FD int #define INVALID_LOCK_FD -1 #endif #include /** * lock file. */ LOCK_FD lock_file(char* path); /** * unlock file. */ bool unlock_file(LOCK_FD fd); #endif //__ZLOG_LOCK_FILE_H__ zlog-1.2.18/src/mdc.c000066400000000000000000000066341460570005000142620ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include "mdc.h" #include "zc_defs.h" void zlog_mdc_profile(zlog_mdc_t *a_mdc, int flag) { zc_hashtable_entry_t *a_entry; zlog_mdc_kv_t *a_mdc_kv; zc_assert(a_mdc,); zc_profile(flag, "---mdc[%p]---", a_mdc); zc_hashtable_foreach(a_mdc->tab, a_entry) { a_mdc_kv = a_entry->value; zc_profile(flag, "----mdc_kv[%p][%s]-[%s]----", a_mdc_kv, a_mdc_kv->key, a_mdc_kv->value); } return; } /*******************************************************************************/ void zlog_mdc_del(zlog_mdc_t * a_mdc) { zc_assert(a_mdc,); if (a_mdc->tab) zc_hashtable_del(a_mdc->tab); zc_debug("zlog_mdc_del[%p]", a_mdc); free(a_mdc); return; } static void zlog_mdc_kv_del(zlog_mdc_kv_t * a_mdc_kv) { zc_debug("zlog_mdc_kv_del[%p]", a_mdc_kv); free(a_mdc_kv); } static zlog_mdc_kv_t *zlog_mdc_kv_new(const char *key, const char *value) { zlog_mdc_kv_t *a_mdc_kv; a_mdc_kv = calloc(1, sizeof(zlog_mdc_kv_t)); if (!a_mdc_kv) { zc_error("calloc fail, errno[%d]", errno); return NULL; } snprintf(a_mdc_kv->key, sizeof(a_mdc_kv->key), "%s", key); a_mdc_kv->value_len = snprintf(a_mdc_kv->value, sizeof(a_mdc_kv->value), "%s", value); return a_mdc_kv; } zlog_mdc_t *zlog_mdc_new(void) { zlog_mdc_t *a_mdc; a_mdc = calloc(1, sizeof(zlog_mdc_t)); if (!a_mdc) { zc_error("calloc fail, errno[%d]", errno); return NULL; } a_mdc->tab = zc_hashtable_new(20, zc_hashtable_str_hash, zc_hashtable_str_equal, NULL, (zc_hashtable_del_fn) zlog_mdc_kv_del); if (!a_mdc->tab) { zc_error("zc_hashtable_new fail"); goto err; } //zlog_mdc_profile(a_mdc, ZC_DEBUG); return a_mdc; err: zlog_mdc_del(a_mdc); return NULL; } /*******************************************************************************/ int zlog_mdc_put(zlog_mdc_t * a_mdc, const char *key, const char *value) { zlog_mdc_kv_t *a_mdc_kv; a_mdc_kv = zlog_mdc_kv_new(key, value); if (!a_mdc_kv) { zc_error("zlog_mdc_kv_new failed"); return -1; } if (zc_hashtable_put(a_mdc->tab, a_mdc_kv->key, a_mdc_kv)) { zc_error("zc_hashtable_put fail"); zlog_mdc_kv_del(a_mdc_kv); return -1; } return 0; } void zlog_mdc_clean(zlog_mdc_t * a_mdc) { zc_hashtable_clean(a_mdc->tab); return; } char *zlog_mdc_get(zlog_mdc_t * a_mdc, const char *key) { zlog_mdc_kv_t *a_mdc_kv; a_mdc_kv = zc_hashtable_get(a_mdc->tab, key); if (!a_mdc_kv) { zc_error("zc_hashtable_get fail"); return NULL; } else { return a_mdc_kv->value; } } zlog_mdc_kv_t *zlog_mdc_get_kv(zlog_mdc_t * a_mdc, const char *key) { zlog_mdc_kv_t *a_mdc_kv; a_mdc_kv = zc_hashtable_get(a_mdc->tab, key); if (!a_mdc_kv) { zc_error("zc_hashtable_get fail"); return NULL; } else { return a_mdc_kv; } } void zlog_mdc_remove(zlog_mdc_t * a_mdc, const char *key) { zc_hashtable_remove(a_mdc->tab, key); return; } zlog-1.2.18/src/mdc.h000066400000000000000000000024311460570005000142560ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __zlog_mdc_h #define __zlog_mdc_h #include "zc_defs.h" typedef struct zlog_mdc_s zlog_mdc_t; struct zlog_mdc_s { zc_hashtable_t *tab; }; zlog_mdc_t *zlog_mdc_new(void); void zlog_mdc_del(zlog_mdc_t * a_mdc); void zlog_mdc_profile(zlog_mdc_t *a_mdc, int flag); void zlog_mdc_clean(zlog_mdc_t * a_mdc); int zlog_mdc_put(zlog_mdc_t * a_mdc, const char *key, const char *value); char *zlog_mdc_get(zlog_mdc_t * a_mdc, const char *key); void zlog_mdc_remove(zlog_mdc_t * a_mdc, const char *key); typedef struct zlog_mdc_kv_s { char key[MAXLEN_PATH + 1]; char value[MAXLEN_PATH + 1]; size_t value_len; } zlog_mdc_kv_t; zlog_mdc_kv_t *zlog_mdc_get_kv(zlog_mdc_t * a_mdc, const char *key); #endif zlog-1.2.18/src/record.c000066400000000000000000000030141460570005000147620ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "errno.h" #include "zc_defs.h" #include "record.h" void zlog_record_profile(zlog_record_t *a_record, int flag) { zc_assert(a_record,); zc_profile(flag, "--record:[%p][%s:%p]--", a_record, a_record->name, a_record->output); return; } void zlog_record_del(zlog_record_t *a_record) { zc_assert(a_record,); zc_debug("zlog_record_del[%p]", a_record); free(a_record); return; } zlog_record_t *zlog_record_new(const char *name, zlog_record_fn output) { zlog_record_t *a_record; zc_assert(name, NULL); zc_assert(output, NULL); a_record = calloc(1, sizeof(zlog_record_t)); if (!a_record) { zc_error("calloc fail, errno[%d]", errno); return NULL; } if (strlen(name) > sizeof(a_record->name) - 1) { zc_error("name[%s] is too long", name); goto err; } strcpy(a_record->name, name); a_record->output = output; zlog_record_profile(a_record, ZC_DEBUG); return a_record; err: zlog_record_del(a_record); return NULL; } zlog-1.2.18/src/record.h000066400000000000000000000023051460570005000147710ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __zlog_record_h #define __zlog_record_h #include "zc_defs.h" /* record is user-defined output function and it's name from configure file */ typedef struct zlog_msg_s { char *buf; size_t len; char *path; } zlog_msg_t; /* 3 of this first, see need thread or not later */ typedef int (*zlog_record_fn)(zlog_msg_t * msg); typedef struct zlog_record_s { char name[MAXLEN_PATH + 1]; zlog_record_fn output; } zlog_record_t; zlog_record_t *zlog_record_new(const char *name, zlog_record_fn output); void zlog_record_del(zlog_record_t *a_record); void zlog_record_profile(zlog_record_t *a_record, int flag); #endif zlog-1.2.18/src/record_table.c000066400000000000000000000033721460570005000161400ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include "zc_defs.h" #include "record_table.h" void zlog_record_table_profile(zc_hashtable_t * records, int flag) { zc_hashtable_entry_t *a_entry; zlog_record_t *a_record; zc_assert(records,); zc_profile(flag, "-record_table[%p]-", records); zc_hashtable_foreach(records, a_entry) { a_record = (zlog_record_t *) a_entry->value; zlog_record_profile(a_record, flag); } return; } /*******************************************************************************/ void zlog_record_table_del(zc_hashtable_t * records) { zc_assert(records,); zc_hashtable_del(records); zc_debug("zlog_record_table_del[%p]", records); return; } zc_hashtable_t *zlog_record_table_new(void) { zc_hashtable_t *records; records = zc_hashtable_new(20, (zc_hashtable_hash_fn) zc_hashtable_str_hash, (zc_hashtable_equal_fn) zc_hashtable_str_equal, NULL, (zc_hashtable_del_fn) zlog_record_del); if (!records) { zc_error("zc_hashtable_new fail"); return NULL; } else { zlog_record_table_profile(records, ZC_DEBUG); return records; } } /*******************************************************************************/ zlog-1.2.18/src/record_table.h000066400000000000000000000015531460570005000161440ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __zlog_record_table_h #define __zlog_record_table_h #include "zc_defs.h" #include "record.h" zc_hashtable_t *zlog_record_table_new(void); void zlog_record_table_del(zc_hashtable_t * records); void zlog_record_table_profile(zc_hashtable_t * records, int flag); #endif zlog-1.2.18/src/rotater.c000066400000000000000000000324711460570005000151750ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #ifdef _WIN32 #include #endif #include #include #include #include #include #include #include #include #include #include "zc_defs.h" #include "rotater.h" #define ROLLING 1 /* aa.02->aa.03, aa.01->aa.02, aa->aa.01 */ #define SEQUENCE 2 /* aa->aa.03 */ typedef struct { int index; char path[MAXLEN_PATH + 1]; } zlog_file_t; void zlog_rotater_profile(zlog_rotater_t * a_rotater, int flag) { zc_assert(a_rotater,); zc_profile(flag, "--rotater[%p][%p,%s,%d][%s,%s,%s,%ld,%ld,%d,%d,%d]--", a_rotater, &(a_rotater->lock_mutex), a_rotater->lock_file, a_rotater->lock_fd, a_rotater->base_path, a_rotater->archive_path, a_rotater->glob_path, (long)a_rotater->num_start_len, (long)a_rotater->num_end_len, a_rotater->num_width, a_rotater->mv_type, a_rotater->max_count ); if (a_rotater->files) { int i; zlog_file_t *a_file; zc_arraylist_foreach(a_rotater->files, i, a_file) { zc_profile(flag, "[%s,%d]->", a_file->path, a_file->index); } } return; } /*******************************************************************************/ void zlog_rotater_del(zlog_rotater_t *a_rotater) { zc_assert(a_rotater,); if (a_rotater->lock_fd != INVALID_LOCK_FD) { if (!unlock_file(a_rotater->lock_fd)) { zc_error("close fail, errno[%d]", errno); } a_rotater->lock_fd = INVALID_LOCK_FD; } if (pthread_mutex_destroy(&(a_rotater->lock_mutex))) { zc_error("pthread_mutex_destroy fail, errno[%d]", errno); } zc_debug("zlog_rotater_del[%p]", a_rotater); free(a_rotater); return; } zlog_rotater_t *zlog_rotater_new(char *lock_file) { zlog_rotater_t *a_rotater; zc_assert(lock_file, NULL); a_rotater = calloc(1, sizeof(zlog_rotater_t)); if (!a_rotater) { zc_error("calloc fail, errno[%d]", errno); return NULL; } if (pthread_mutex_init(&(a_rotater->lock_mutex), NULL)) { zc_error("pthread_mutex_init fail, errno[%d]", errno); free(a_rotater); return NULL; } a_rotater->lock_fd = INVALID_LOCK_FD; a_rotater->lock_file = lock_file; //zlog_rotater_profile(a_rotater, ZC_DEBUG); return a_rotater; zlog_rotater_del(a_rotater); return NULL; } /*******************************************************************************/ static void zlog_file_del(zlog_file_t * a_file) { zc_debug("del onefile[%p]", a_file); zc_debug("a_file->path[%s]", a_file->path); free(a_file); } static zlog_file_t *zlog_file_check_new(zlog_rotater_t * a_rotater, const char *path) { int nwrite; int nread; zlog_file_t *a_file; /* base_path will not be in list */ if (STRCMP(a_rotater->base_path, ==, path)) { return NULL; } /* omit dirs */ if ((path)[strlen(path) - 1] == '/') { return NULL; } a_file = calloc(1, sizeof(zlog_file_t)); if (!a_file) { zc_error("calloc fail, errno[%d]", errno); return NULL; } nwrite = snprintf(a_file->path, sizeof(a_file->path), "%s", path); if (nwrite < 0 || nwrite >= sizeof(a_file->path)) { zc_error("snprintf fail or overflow, nwrite=[%d], errno[%d]", nwrite, errno); goto err; } nread = 0; sscanf(a_file->path + a_rotater->num_start_len, "%d%n", &(a_file->index), &(nread)); if (a_rotater->num_width != 0) { if (nread < a_rotater->num_width) { zc_warn("aa.1.log is not expect, need aa.01.log"); goto err; } } /* else all file is ok */ return a_file; err: free(a_file); return NULL; } static int zlog_file_cmp(zlog_file_t * a_file_1, zlog_file_t * a_file_2) { return (a_file_1->index > a_file_2->index); } static int zlog_rotater_add_archive_files(zlog_rotater_t * a_rotater) { int rc = 0; glob_t glob_buf; size_t pathc; char **pathv; zlog_file_t *a_file; a_rotater->files = zc_arraylist_new((zc_arraylist_del_fn)zlog_file_del); if (!a_rotater->files) { zc_error("zc_arraylist_new fail"); return -1; } /* scan file which is aa.*.log and aa */ rc = glob(a_rotater->glob_path, GLOB_ERR | GLOB_MARK | GLOB_NOSORT, NULL, &glob_buf); if (rc == GLOB_NOMATCH) { goto exit; } else if (rc) { zc_error("glob err, rc=[%d], errno[%d]", rc, errno); return -1; } pathv = glob_buf.gl_pathv; pathc = glob_buf.gl_pathc; /* check and find match aa.[0-9]*.log, depend on num_width */ for (; pathc-- > 0; pathv++) { a_file = zlog_file_check_new(a_rotater, *pathv); if (!a_file) { zc_warn("not the expect pattern file"); continue; } /* file in list aa.00, aa.01, aa.02... */ rc = zc_arraylist_sortadd(a_rotater->files, (zc_arraylist_cmp_fn)zlog_file_cmp, a_file); if (rc) { zc_error("zc_arraylist_sortadd fail"); goto err; } } exit: globfree(&glob_buf); return 0; err: globfree(&glob_buf); return -1; } static int zlog_rotater_seq_files(zlog_rotater_t * a_rotater) { int rc = 0; int nwrite = 0; int i, j; zlog_file_t *a_file; char new_path[MAXLEN_PATH + 1]; zc_arraylist_foreach(a_rotater->files, i, a_file) { if (a_rotater->max_count > 0 && i < zc_arraylist_len(a_rotater->files) - a_rotater->max_count) { /* unlink aa.0 aa.1 .. aa.(n-c) */ rc = unlink(a_file->path); if (rc) { zc_error("unlink[%s] fail, errno[%d]",a_file->path , errno); return -1; } continue; } } if (zc_arraylist_len(a_rotater->files) > 0) { /* list is not empty */ a_file = zc_arraylist_get(a_rotater->files, zc_arraylist_len(a_rotater->files)-1); if (!a_file) { zc_error("zc_arraylist_get fail"); return -1; } j = zc_max(zc_arraylist_len(a_rotater->files)-1, a_file->index) + 1; } else { j = 0; } /* do the base_path mv */ memset(new_path, 0x00, sizeof(new_path)); nwrite = snprintf(new_path, sizeof(new_path), "%.*s%0*d%s", (int) a_rotater->num_start_len, a_rotater->glob_path, a_rotater->num_width, j, a_rotater->glob_path + a_rotater->num_end_len); if (nwrite < 0 || nwrite >= sizeof(new_path)) { zc_error("nwirte[%d], overflow or errno[%d]", nwrite, errno); return -1; } if (rename(a_rotater->base_path, new_path)) { zc_error("rename[%s]->[%s] fail, errno[%d]", a_rotater->base_path, new_path, errno); return -1; } return 0; } static int zlog_rotater_roll_files(zlog_rotater_t * a_rotater) { int i; int rc = 0; int nwrite; char new_path[MAXLEN_PATH + 1]; zlog_file_t *a_file; /* now in the list, aa.0 aa.1 aa.2 aa.02... */ for (i = zc_arraylist_len(a_rotater->files) - 1; i > -1; i--) { a_file = zc_arraylist_get(a_rotater->files, i); if (!a_file) { zc_error("zc_arraylist_get fail"); return -1; } if (a_rotater->max_count > 0 && i >= a_rotater->max_count - 1) { /* remove file.3 >= 3*/ rc = unlink(a_file->path); if (rc) { zc_error("unlink[%s] fail, errno[%d]",a_file->path , errno); return -1; } continue; } /* begin rename aa.01.log -> aa.02.log , using i, as index in list maybe repeat */ memset(new_path, 0x00, sizeof(new_path)); nwrite = snprintf(new_path, sizeof(new_path), "%.*s%0*d%s", (int) a_rotater->num_start_len, a_rotater->glob_path, a_rotater->num_width, i + 1, a_rotater->glob_path + a_rotater->num_end_len); if (nwrite < 0 || nwrite >= sizeof(new_path)) { zc_error("nwirte[%d], overflow or errno[%d]", nwrite, errno); return -1; } if (rename(a_file->path, new_path)) { zc_error("rename[%s]->[%s] fail, errno[%d]", a_file->path, new_path, errno); return -1; } } /* do the base_path mv */ memset(new_path, 0x00, sizeof(new_path)); nwrite = snprintf(new_path, sizeof(new_path), "%.*s%0*d%s", (int) a_rotater->num_start_len, a_rotater->glob_path, a_rotater->num_width, 0, a_rotater->glob_path + a_rotater->num_end_len); if (nwrite < 0 || nwrite >= sizeof(new_path)) { zc_error("nwirte[%d], overflow or errno[%d]", nwrite, errno); return -1; } if (rename(a_rotater->base_path, new_path)) { zc_error("rename[%s]->[%s] fail, errno[%d]", a_rotater->base_path, new_path, errno); return -1; } return 0; } static int zlog_rotater_parse_archive_path(zlog_rotater_t * a_rotater) { int nwrite; int nread; char *p; size_t len; /* no archive path is set */ if (a_rotater->archive_path[0] == '\0') { nwrite = snprintf(a_rotater->glob_path, sizeof(a_rotater->glob_path), "%s.*", a_rotater->base_path); if (nwrite < 0 || nwrite > sizeof(a_rotater->glob_path)) { zc_error("nwirte[%d], overflow or errno[%d]", nwrite, errno); return -1; } a_rotater->mv_type = ROLLING; a_rotater->num_width = 0; a_rotater->num_start_len = strlen(a_rotater->base_path) + 1; a_rotater->num_end_len = strlen(a_rotater->base_path) + 2; return 0; } else { /* find the 1st # */ p = strchr(a_rotater->archive_path, '#'); if (!p) { zc_error("no # in archive_path[%s]", a_rotater->archive_path); return -1; } nread = 0; sscanf(p, "#%d%n", &(a_rotater->num_width), &nread); if (nread == 0) nread = 1; if (*(p+nread) == 'r') { a_rotater->mv_type = ROLLING; } else if (*(p+nread) == 's') { a_rotater->mv_type = SEQUENCE; } else { zc_error("#r or #s not found"); return -1; } /* copy and substitue #i to * in glob_path*/ len = p - a_rotater->archive_path; if (len > sizeof(a_rotater->glob_path) - 1) { zc_error("sizeof glob_path not enough,len[%ld]", (long) len); return -1; } memcpy(a_rotater->glob_path, a_rotater->archive_path, len); nwrite = snprintf(a_rotater->glob_path + len, sizeof(a_rotater->glob_path) - len, "*%s", p + nread + 1); if (nwrite < 0 || nwrite > sizeof(a_rotater->glob_path) - len) { zc_error("nwirte[%d], overflow or errno[%d]", nwrite, errno); return -1; } a_rotater->num_start_len = len; a_rotater->num_end_len = len + 1; } return 0; } static void zlog_rotater_clean(zlog_rotater_t *a_rotater) { a_rotater->base_path = NULL; a_rotater->archive_path = NULL; a_rotater->max_count = 0; a_rotater->mv_type = 0; a_rotater->num_width = 0; a_rotater->num_start_len = 0; a_rotater->num_end_len = 0; memset(a_rotater->glob_path, 0x00, sizeof(a_rotater->glob_path)); if (a_rotater->files) zc_arraylist_del(a_rotater->files); a_rotater->files = NULL; } static int zlog_rotater_lsmv(zlog_rotater_t *a_rotater, char *base_path, char *archive_path, int archive_max_count) { int rc = 0; a_rotater->base_path = base_path; a_rotater->archive_path = archive_path; a_rotater->max_count = archive_max_count; rc = zlog_rotater_parse_archive_path(a_rotater); if (rc) { zc_error("zlog_rotater_parse_archive_path fail"); goto err; } rc = zlog_rotater_add_archive_files(a_rotater); if (rc) { zc_error("zlog_rotater_add_archive_files fail"); goto err; } if (a_rotater->mv_type == ROLLING) { rc = zlog_rotater_roll_files(a_rotater); if (rc) { zc_error("zlog_rotater_roll_files fail"); goto err; } } else if (a_rotater->mv_type == SEQUENCE) { rc = zlog_rotater_seq_files(a_rotater); if (rc) { zc_error("zlog_rotater_seq_files fail"); goto err; } } zlog_rotater_clean(a_rotater); return 0; err: zlog_rotater_clean(a_rotater); return -1; } /*******************************************************************************/ static int zlog_rotater_trylock(zlog_rotater_t *a_rotater) { int rc; rc = pthread_mutex_trylock(&(a_rotater->lock_mutex)); if (rc == EBUSY) { zc_warn("pthread_mutex_trylock fail, as lock_mutex is locked by other threads"); return -1; } else if (rc != 0) { zc_error("pthread_mutex_trylock fail, rc[%d]", rc); return -1; } a_rotater->lock_fd = lock_file(a_rotater->lock_file); if (a_rotater->lock_fd == INVALID_LOCK_FD) { return -1; } return 0; } static int zlog_rotater_unlock(zlog_rotater_t *a_rotater) { int rc = 0; if (!unlock_file(a_rotater->lock_fd)) { rc = -1; } else { a_rotater->lock_fd = INVALID_LOCK_FD; } if (pthread_mutex_unlock(&(a_rotater->lock_mutex))) { rc = -1; zc_error("pthread_mutext_unlock fail, errno[%d]", errno); } return rc; } int zlog_rotater_rotate(zlog_rotater_t *a_rotater, char *base_path, size_t msg_len, char *archive_path, long archive_max_size, int archive_max_count) { int rc = 0; struct zlog_stat info; zc_assert(base_path, -1); if (zlog_rotater_trylock(a_rotater)) { zc_warn("zlog_rotater_trylock fail, maybe lock by other process or threads"); return 0; } if (stat(base_path, &info)) { rc = -1; zc_error("stat [%s] fail, errno[%d]", base_path, errno); goto exit; } if (info.st_size + msg_len <= archive_max_size) { /* file not so big, * may alread rotate by oth process or thread, * return */ rc = 0; goto exit; } /* begin list and move files */ rc = zlog_rotater_lsmv(a_rotater, base_path, archive_path, archive_max_count); if (rc) { zc_error("zlog_rotater_lsmv [%s] fail, return", base_path); rc = -1; } /* else if (rc == 0) */ //zc_debug("zlog_rotater_file_ls_mv success"); exit: /* unlock file */ if (zlog_rotater_unlock(a_rotater)) { zc_error("zlog_rotater_unlock fail"); } return rc; } /*******************************************************************************/ zlog-1.2.18/src/rotater.h000066400000000000000000000030411460570005000151710ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __zlog_rotater_h #define __zlog_rotater_h #include "zc_defs.h" #include "lockfile.h" typedef struct zlog_rotater_s { pthread_mutex_t lock_mutex; char *lock_file; LOCK_FD lock_fd; /* single-use members */ char *base_path; /* aa.log */ char *archive_path; /* aa.#5i.log */ char glob_path[MAXLEN_PATH + 1]; /* aa.*.log */ size_t num_start_len; /* 3, offset to glob_path */ size_t num_end_len; /* 6, offset to glob_path */ int num_width; /* 5 */ int mv_type; /* ROLLING or SEQUENCE */ int max_count; zc_arraylist_t *files; } zlog_rotater_t; zlog_rotater_t *zlog_rotater_new(char *lock_file); void zlog_rotater_del(zlog_rotater_t *a_rotater); /* * return * -1 fail * 0 no rotate, or rotate and success */ int zlog_rotater_rotate(zlog_rotater_t *a_rotater, char *base_path, size_t msg_len, char *archive_path, long archive_max_size, int archive_max_count); void zlog_rotater_profile(zlog_rotater_t *a_rotater, int flag); #endif zlog-1.2.18/src/rule.c000066400000000000000000000662161460570005000144700ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "fmacros.h" #include #include #ifndef _WIN32 #include #else #include "zlog_win.h" #endif #include #include #include #include #include #include #include #include "rule.h" #include "format.h" #include "buf.h" #include "thread.h" #include "level_list.h" #include "rotater.h" #include "spec.h" #include "conf.h" #include "zc_defs.h" void zlog_rule_profile(zlog_rule_t * a_rule, int flag) { int i; zlog_spec_t *a_spec; zc_assert(a_rule,); zc_profile(flag, "---rule:[%p][%s%c%d]-[%d,%d][%s,%p,%d:%ld*%d~%s][%d][%d][%s:%s:%p];[%p]---", a_rule, a_rule->category, a_rule->compare_char, a_rule->level, a_rule->file_perms, a_rule->file_open_flags, a_rule->file_path, a_rule->dynamic_specs, a_rule->static_fd, a_rule->archive_max_size, a_rule->archive_max_count, a_rule->archive_path, a_rule->pipe_fd, a_rule->syslog_facility, a_rule->record_name, a_rule->record_path, a_rule->record_func, a_rule->format); if (a_rule->dynamic_specs) { zc_arraylist_foreach(a_rule->dynamic_specs, i, a_spec) { zlog_spec_profile(a_spec, flag); } } return; } /*******************************************************************************/ static int zlog_rule_output_static_file_single(zlog_rule_t * a_rule, zlog_thread_t * a_thread) { struct stat stb; int do_file_reload = 0; int redo_inode_stat = 0; if (zlog_format_gen_msg(a_rule->format, a_thread)) { zc_error("zlog_format_gen_msg fail"); return -1; } /* check if the output file was changed by an external tool by comparing the inode to our saved off one */ if (stat(a_rule->file_path, &stb)) { if (errno != ENOENT) { zc_error("stat fail on [%s], errno[%d]", a_rule->file_path, errno); return -1; } else { do_file_reload = 1; redo_inode_stat = 1; /* we'll have to restat the newly created file to get the inode info */ } } else { do_file_reload = (stb.st_ino != a_rule->static_ino || stb.st_dev != a_rule->static_dev); } if (do_file_reload) { close(a_rule->static_fd); a_rule->static_fd = open(a_rule->file_path, O_WRONLY | O_APPEND | O_CREAT | a_rule->file_open_flags, a_rule->file_perms); if (a_rule->static_fd < 0) { zc_error("open file[%s] fail, errno[%d]", a_rule->file_path, errno); return -1; } /* save off the new dev/inode info from the stat call we already did */ if (redo_inode_stat) { if (stat(a_rule->file_path, &stb)) { zc_error("stat fail on new file[%s], errno[%d]", a_rule->file_path, errno); return -1; } } a_rule->static_dev = stb.st_dev; a_rule->static_ino = stb.st_ino; } if (write(a_rule->static_fd, zlog_buf_str(a_thread->msg_buf), zlog_buf_len(a_thread->msg_buf)) < 0) { zc_error("write fail, errno[%d]", errno); return -1; } /* not so thread safe here, as multiple thread may ++fsync_count at the same time */ if (a_rule->fsync_period && ++a_rule->fsync_count >= a_rule->fsync_period) { a_rule->fsync_count = 0; if (fsync(a_rule->static_fd)) { zc_error("fsync[%d] fail, errno[%d]", a_rule->static_fd, errno); } } return 0; } static char * zlog_rule_gen_archive_path(zlog_rule_t *a_rule, zlog_thread_t *a_thread) { int i; zlog_spec_t *a_spec; if (!a_rule->archive_specs) return a_rule->archive_path; zlog_buf_restart(a_thread->archive_path_buf); zc_arraylist_foreach(a_rule->archive_specs, i, a_spec) { if (zlog_spec_gen_archive_path(a_spec, a_thread)) { zc_error("zlog_spec_gen_path fail"); return NULL; } } zlog_buf_seal(a_thread->archive_path_buf); return zlog_buf_str(a_thread->archive_path_buf); } static int zlog_rule_output_static_file_rotate(zlog_rule_t * a_rule, zlog_thread_t * a_thread) { size_t len; struct zlog_stat info; int fd; if (zlog_format_gen_msg(a_rule->format, a_thread)) { zc_error("zlog_format_gen_msg fail"); return -1; } fd = open(a_rule->file_path, a_rule->file_open_flags | O_WRONLY | O_APPEND | O_CREAT, a_rule->file_perms); if (fd < 0) { zc_error("open file[%s] fail, errno[%d]", a_rule->file_path, errno); return -1; } len = zlog_buf_len(a_thread->msg_buf); if (write(fd, zlog_buf_str(a_thread->msg_buf), len) < 0) { zc_error("write fail, errno[%d]", errno); close(fd); return -1; } if (a_rule->fsync_period && ++a_rule->fsync_count >= a_rule->fsync_period) { a_rule->fsync_count = 0; if (fsync(fd)) zc_error("fsync[%d] fail, errno[%d]", fd, errno); } if (close(fd) < 0) { zc_error("close fail, maybe cause by write, errno[%d]", errno); return -1; } if (len > a_rule->archive_max_size) { zc_debug("one msg's len[%ld] > archive_max_size[%ld], no rotate", (long)len, (long)a_rule->archive_max_size); return 0; } if (stat(a_rule->file_path, &info)) { zc_warn("stat [%s] fail, errno[%d], maybe in rotating", a_rule->file_path, errno); return 0; } /* file not so big, return */ if (info.st_size + len < a_rule->archive_max_size) return 0; if (zlog_rotater_rotate(zlog_env_conf->rotater, a_rule->file_path, len, zlog_rule_gen_archive_path(a_rule, a_thread), a_rule->archive_max_size, a_rule->archive_max_count) ) { zc_error("zlog_rotater_rotate fail"); return -1; } /* success or no rotation do nothing */ return 0; } /* return path success * return NULL fail */ #define zlog_rule_gen_path(a_rule, a_thread) do { \ int i; \ zlog_spec_t *a_spec; \ \ zlog_buf_restart(a_thread->path_buf); \ \ zc_arraylist_foreach(a_rule->dynamic_specs, i, a_spec) { \ if (zlog_spec_gen_path(a_spec, a_thread)) { \ zc_error("zlog_spec_gen_path fail"); \ return -1; \ } \ } \ \ zlog_buf_seal(a_thread->path_buf); \ } while(0) static int zlog_rule_output_dynamic_file_single(zlog_rule_t * a_rule, zlog_thread_t * a_thread) { int fd; zlog_rule_gen_path(a_rule, a_thread); if (zlog_format_gen_msg(a_rule->format, a_thread)) { zc_error("zlog_format_output fail"); return -1; } fd = open(zlog_buf_str(a_thread->path_buf), a_rule->file_open_flags | O_WRONLY | O_APPEND | O_CREAT, a_rule->file_perms); if (fd < 0) { zc_error("open file[%s] fail, errno[%d]", zlog_buf_str(a_thread->path_buf), errno); return -1; } if (write(fd, zlog_buf_str(a_thread->msg_buf), zlog_buf_len(a_thread->msg_buf)) < 0) { zc_error("write fail, errno[%d]", errno); close(fd); return -1; } if (a_rule->fsync_period && ++a_rule->fsync_count >= a_rule->fsync_period) { a_rule->fsync_count = 0; if (fsync(fd)) zc_error("fsync[%d] fail, errno[%d]", fd, errno); } if (close(fd) < 0) { zc_error("close fail, maybe cause by write, errno[%d]", errno); return -1; } return 0; } static int zlog_rule_output_dynamic_file_rotate(zlog_rule_t * a_rule, zlog_thread_t * a_thread) { int fd; char *path; size_t len; struct zlog_stat info; zlog_rule_gen_path(a_rule, a_thread); if (zlog_format_gen_msg(a_rule->format, a_thread)) { zc_error("zlog_format_output fail"); return -1; } path = zlog_buf_str(a_thread->path_buf); fd = open(path, a_rule->file_open_flags | O_WRONLY | O_APPEND | O_CREAT, a_rule->file_perms); if (fd < 0) { zc_error("open file[%s] fail, errno[%d]", zlog_buf_str(a_thread->path_buf), errno); return -1; } len = zlog_buf_len(a_thread->msg_buf); if (write(fd, zlog_buf_str(a_thread->msg_buf), len) < 0) { zc_error("write fail, errno[%d]", errno); close(fd); return -1; } if (a_rule->fsync_period && ++a_rule->fsync_count >= a_rule->fsync_period) { a_rule->fsync_count = 0; if (fsync(fd)) zc_error("fsync[%d] fail, errno[%d]", fd, errno); } if (close(fd) < 0) { zc_error("write fail, maybe cause by write, errno[%d]", errno); return -1; } if (len > a_rule->archive_max_size) { zc_debug("one msg's len[%ld] > archive_max_size[%ld], no rotate", (long)len, (long) a_rule->archive_max_size); return 0; } if (stat(path, &info)) { zc_warn("stat [%s] fail, errno[%d], maybe in rotating", path, errno); return 0; } /* file not so big, return */ if (info.st_size + len < a_rule->archive_max_size) return 0; if (zlog_rotater_rotate(zlog_env_conf->rotater, path, len, zlog_rule_gen_archive_path(a_rule, a_thread), a_rule->archive_max_size, a_rule->archive_max_count) ) { zc_error("zlog_rotater_rotate fail"); return -1; } /* success or no rotation do nothing */ return 0; } static int zlog_rule_output_pipe(zlog_rule_t * a_rule, zlog_thread_t * a_thread) { if (zlog_format_gen_msg(a_rule->format, a_thread)) { zc_error("zlog_format_gen_msg fail"); return -1; } if (write(a_rule->pipe_fd, zlog_buf_str(a_thread->msg_buf), zlog_buf_len(a_thread->msg_buf)) < 0) { zc_error("write fail, errno[%d]", errno); return -1; } return 0; } static int zlog_rule_output_syslog(zlog_rule_t * a_rule, zlog_thread_t * a_thread) { #ifndef _WIN32 zlog_level_t *a_level; if (zlog_format_gen_msg(a_rule->format, a_thread)) { zc_error("zlog_format_gen_msg fail"); return -1; } /* msg = a_thread->msg_buf->start; msg_len = a_thread->msg_buf->end - a_thread->msg_buf->start; */ a_level = zlog_level_list_get(zlog_env_conf->levels, a_thread->event->level); zlog_buf_seal(a_thread->msg_buf); syslog(a_rule->syslog_facility | a_level->syslog_level, "%s", zlog_buf_str(a_thread->msg_buf)); #endif return 0; } static int zlog_rule_output_static_record(zlog_rule_t * a_rule, zlog_thread_t * a_thread) { zlog_msg_t msg; if (!a_rule->record_func) { zc_error("user defined record funcion for [%s] not set, no output", a_rule->record_name); return -1; } if (zlog_format_gen_msg(a_rule->format, a_thread)) { zc_error("zlog_format_gen_msg fail"); return -1; } zlog_buf_seal(a_thread->msg_buf); msg.buf = zlog_buf_str(a_thread->msg_buf); msg.len = zlog_buf_len(a_thread->msg_buf); msg.path = a_rule->record_path; if (a_rule->record_func(&msg)) { zc_error("a_rule->record fail"); return -1; } return 0; } static int zlog_rule_output_dynamic_record(zlog_rule_t * a_rule, zlog_thread_t * a_thread) { zlog_msg_t msg; if (!a_rule->record_func) { zc_error("user defined record funcion for [%s] not set, no output", a_rule->record_name); return -1; } zlog_rule_gen_path(a_rule, a_thread); if (zlog_format_gen_msg(a_rule->format, a_thread)) { zc_error("zlog_format_gen_msg fail"); return -1; } zlog_buf_seal(a_thread->msg_buf); msg.buf = zlog_buf_str(a_thread->msg_buf); msg.len = zlog_buf_len(a_thread->msg_buf); msg.path = zlog_buf_str(a_thread->path_buf); if (a_rule->record_func(&msg)) { zc_error("a_rule->record fail"); return -1; } return 0; } static int zlog_rule_output_stdout(zlog_rule_t * a_rule, zlog_thread_t * a_thread) { if (zlog_format_gen_msg(a_rule->format, a_thread)) { zc_error("zlog_format_gen_msg fail"); return -1; } if (write(STDOUT_FILENO, zlog_buf_str(a_thread->msg_buf), zlog_buf_len(a_thread->msg_buf)) < 0) { zc_error("write fail, errno[%d]", errno); return -1; } return 0; } static int zlog_rule_output_stderr(zlog_rule_t * a_rule, zlog_thread_t * a_thread) { if (zlog_format_gen_msg(a_rule->format, a_thread)) { zc_error("zlog_format_gen_msg fail"); return -1; } if (write(STDERR_FILENO, zlog_buf_str(a_thread->msg_buf), zlog_buf_len(a_thread->msg_buf)) < 0) { zc_error("write fail, errno[%d]", errno); return -1; } return 0; } /*******************************************************************************/ static int syslog_facility_atoi(char *facility) { #ifndef _WIN32 /* guess no unix system will choose -187 * as its syslog facility, so it is a safe return value */ zc_assert(facility, -187); if (STRICMP(facility, ==, "LOG_LOCAL0")) return LOG_LOCAL0; if (STRICMP(facility, ==, "LOG_LOCAL1")) return LOG_LOCAL1; if (STRICMP(facility, ==, "LOG_LOCAL2")) return LOG_LOCAL2; if (STRICMP(facility, ==, "LOG_LOCAL3")) return LOG_LOCAL3; if (STRICMP(facility, ==, "LOG_LOCAL4")) return LOG_LOCAL4; if (STRICMP(facility, ==, "LOG_LOCAL5")) return LOG_LOCAL5; if (STRICMP(facility, ==, "LOG_LOCAL6")) return LOG_LOCAL6; if (STRICMP(facility, ==, "LOG_LOCAL7")) return LOG_LOCAL7; if (STRICMP(facility, ==, "LOG_USER")) return LOG_USER; if (STRICMP(facility, ==, "LOG_AUTHPRIV")) return LOG_AUTHPRIV; if (STRICMP(facility, ==, "LOG_CRON")) return LOG_CRON; if (STRICMP(facility, ==, "LOG_DAEMON")) return LOG_DAEMON; if (STRICMP(facility, ==, "LOG_FTP")) return LOG_FTP; if (STRICMP(facility, ==, "LOG_KERN")) return LOG_KERN; if (STRICMP(facility, ==, "LOG_LPR")) return LOG_LPR; if (STRICMP(facility, ==, "LOG_MAIL")) return LOG_MAIL; if (STRICMP(facility, ==, "LOG_NEWS")) return LOG_NEWS; if (STRICMP(facility, ==, "LOG_SYSLOG")) return LOG_SYSLOG; return LOG_AUTHPRIV; #endif zc_error("wrong syslog facility[%s], must in LOG_LOCAL[0-7] or LOG_USER", facility); return -187; } static int zlog_rule_parse_path(char *path_start, /* start with a " */ char *path_str, size_t path_size, zc_arraylist_t **path_specs, int *time_cache_count) { char *p, *q; size_t len; zlog_spec_t *a_spec; zc_arraylist_t *specs; p = path_start + 1; q = strrchr(p, '"'); if (!q) { zc_error("matching \" not found in conf line[%s]", path_start); return -1; } len = q - p; if (len > path_size - 1) { zc_error("file_path too long %ld > %ld", len, path_size - 1); return -1; } memcpy(path_str, p, len); /* replace any environment variables like %E(HOME) */ if (zc_str_replace_env(path_str, path_size)) { zc_error("zc_str_replace_env fail"); return -1; } if (strchr(path_str, '%') == NULL) { /* static, no need create specs */ return 0; } specs = zc_arraylist_new((zc_arraylist_del_fn)zlog_spec_del); if (!path_specs) { zc_error("zc_arraylist_new fail"); return -1; } for (p = path_str; *p != '\0'; p = q) { a_spec = zlog_spec_new(p, &q, time_cache_count); if (!a_spec) { zc_error("zlog_spec_new fail"); goto err; } if (zc_arraylist_add(specs, a_spec)) { zc_error("zc_arraylist_add fail"); goto err; } } *path_specs = specs; return 0; err: if (specs) zc_arraylist_del(specs); if (a_spec) zlog_spec_del(a_spec); return -1; } zlog_rule_t *zlog_rule_new(char *line, zc_arraylist_t *levels, zlog_format_t * default_format, zc_arraylist_t * formats, unsigned int file_perms, size_t fsync_period, int * time_cache_count) { int rc = 0; int nscan = 0; int nread = 0; zlog_rule_t *a_rule; char selector[MAXLEN_CFG_LINE + 1]; char category[MAXLEN_CFG_LINE + 1]; char level[MAXLEN_CFG_LINE + 1]; char *action; char output[MAXLEN_CFG_LINE + 1]; char format_name[MAXLEN_CFG_LINE + 1]; char file_path[MAXLEN_CFG_LINE + 1]; char archive_max_size[MAXLEN_CFG_LINE + 1]; char *file_limit; char *p; char *q; size_t len; zc_assert(line, NULL); zc_assert(default_format, NULL); zc_assert(formats, NULL); a_rule = calloc(1, sizeof(zlog_rule_t)); if (!a_rule) { zc_error("calloc fail, errno[%d]", errno); return NULL; } a_rule->file_perms = file_perms; a_rule->fsync_period = fsync_period; /* line [f.INFO "%H/log/aa.log", 20MB * 12; MyTemplate] * selector [f.INFO] * *action ["%H/log/aa.log", 20MB * 12; MyTemplate] */ memset(&selector, 0x00, sizeof(selector)); nscan = sscanf(line, "%s %n", selector, &nread); if (nscan != 1) { zc_error("sscanf [%s] fail, selector", line); goto err; } action = line + nread; /* * selector [f.INFO] * category [f] * level [.INFO] */ memset(category, 0x00, sizeof(category)); memset(level, 0x00, sizeof(level)); nscan = sscanf(selector, " %[^.].%s", category, level); if (nscan != 2) { zc_error("sscanf [%s] fail, category or level is null", selector); goto err; } /* check and set category */ for (p = category; *p != '\0'; p++) { if ((!isalnum(*p)) && (*p != '_') && (*p != '-') && (*p != '*') && (*p != '!')) { zc_error("category name[%s] character is not in [a-Z][0-9][_!*-]", category); goto err; } } /* as one line can't be longer than MAXLEN_CFG_LINE, same as category */ strcpy(a_rule->category, category); /* check and set level */ switch (level[0]) { case '=': /* aa.=debug */ a_rule->compare_char = '='; p = level + 1; break; case '!': /* aa.!debug */ a_rule->compare_char = '!'; p = level + 1; break; case '*': /* aa.* */ a_rule->compare_char = '*'; p = level; break; default: /* aa.debug */ a_rule->compare_char = '.'; p = level; break; } a_rule->level = zlog_level_list_atoi(levels, p); /* level_bit is a bitmap represents which level can be output * 32bytes, [0-255] levels, see level.c * which bit field is 1 means allow output and 0 not */ switch (a_rule->compare_char) { case '=': memset(a_rule->level_bitmap, 0x00, sizeof(a_rule->level_bitmap)); a_rule->level_bitmap[a_rule->level / 8] |= (1 << (7 - a_rule->level % 8)); break; case '!': memset(a_rule->level_bitmap, 0xFF, sizeof(a_rule->level_bitmap)); a_rule->level_bitmap[a_rule->level / 8] &= ~(1 << (7 - a_rule->level % 8)); break; case '*': memset(a_rule->level_bitmap, 0xFF, sizeof(a_rule->level_bitmap)); break; case '.': memset(a_rule->level_bitmap, 0x00, sizeof(a_rule->level_bitmap)); a_rule->level_bitmap[a_rule->level / 8] |= ~(0xFF << (8 - a_rule->level % 8)); memset(a_rule->level_bitmap + a_rule->level / 8 + 1, 0xFF, sizeof(a_rule->level_bitmap) - a_rule->level / 8 - 1); break; } /* action ["%H/log/aa.log", 20MB * 12 ; MyTemplate] * output ["%H/log/aa.log", 20MB * 12] * format [MyTemplate] */ memset(output, 0x00, sizeof(output)); memset(format_name, 0x00, sizeof(format_name)); nscan = sscanf(action, " %[^;];%s", output, format_name); if (nscan < 1) { zc_error("sscanf [%s] fail", action); goto err; } /* check and get format */ if (STRCMP(format_name, ==, "")) { zc_debug("no format specified, use default"); a_rule->format = default_format; } else { int i; int find_flag = 0; zlog_format_t *a_format; zc_arraylist_foreach(formats, i, a_format) { if (zlog_format_has_name(a_format, format_name)) { a_rule->format = a_format; find_flag = 1; break; } } if (!find_flag) { zc_error("in conf file can't find format[%s], pls check", format_name); goto err; } } /* output [-"%E(HOME)/log/aa.log" , 20MB*12] [>syslog , LOG_LOCAL0 ] * file_path [-"%E(HOME)/log/aa.log" ] [>syslog ] * *file_limit [20MB * 12 ~ "aa.#i.log" ] [LOG_LOCAL0] */ memset(file_path, 0x00, sizeof(file_path)); nscan = sscanf(output, " %[^,],", file_path); if (nscan < 1) { zc_error("sscanf [%s] fail", action); goto err; } file_limit = strchr(output, ','); if (file_limit) { file_limit++; /* skip the , */ while( isspace(*file_limit) ) { file_limit++; } } p = NULL; switch (file_path[0]) { case '-' : /* sync file each time write log */ if (file_path[1] != '"') { zc_error(" - must set before a file output"); goto err; } /* no need to fsync, as file is opened by O_SYNC, write immediately */ a_rule->fsync_period = 0; p = file_path + 1; #ifndef _WIN32 a_rule->file_open_flags = O_SYNC; /* fall through */ #endif case '"' : if (!p) p = file_path; rc = zlog_rule_parse_path(p, a_rule->file_path, sizeof(a_rule->file_path), &(a_rule->dynamic_specs), time_cache_count); if (rc) { zc_error("zlog_rule_parse_path fail"); goto err; } if (file_limit) { memset(archive_max_size, 0x00, sizeof(archive_max_size)); nscan = sscanf(file_limit, " %[0-9GgMmKkBb] * %d ~", archive_max_size, &(a_rule->archive_max_count)); if (nscan) { a_rule->archive_max_size = zc_parse_byte_size(archive_max_size); } p = strchr(file_limit, '"'); if (p) { /* archive file path exist */ rc = zlog_rule_parse_path(p, a_rule->archive_path, sizeof(a_rule->file_path), &(a_rule->archive_specs), time_cache_count); if (rc) { zc_error("zlog_rule_parse_path fail"); goto err; } p = strchr(a_rule->archive_path, '#'); if ( (p == NULL) || ((strchr(p, 'r') == NULL) && (strchr(p, 's') == NULL))) { zc_error("archive_path must contain #r or #s"); goto err; } } } /* try to figure out if the log file path is dynamic or static */ if (a_rule->dynamic_specs) { if (a_rule->archive_max_size <= 0) { a_rule->output = zlog_rule_output_dynamic_file_single; } else { a_rule->output = zlog_rule_output_dynamic_file_rotate; } } else { struct stat stb; if (a_rule->archive_max_size <= 0) { a_rule->output = zlog_rule_output_static_file_single; } else { /* as rotate, so need to reopen everytime */ a_rule->output = zlog_rule_output_static_file_rotate; } a_rule->static_fd = open(a_rule->file_path, O_WRONLY | O_APPEND | O_CREAT | a_rule->file_open_flags, a_rule->file_perms); if (a_rule->static_fd < 0) { zc_error("open file[%s] fail, errno[%d]", a_rule->file_path, errno); goto err; } /* save off the inode information for checking for a changed file later on */ if (fstat(a_rule->static_fd, &stb)) { zc_error("stat [%s] fail, errno[%d], failing to open static_fd", a_rule->file_path, errno); goto err; } if (a_rule->archive_max_size > 0) { close(a_rule->static_fd); a_rule->static_fd = -1; } a_rule->static_dev = stb.st_dev; a_rule->static_ino = stb.st_ino; } break; case '|' : a_rule->pipe_fp = popen(output + 1, "w"); if (!a_rule->pipe_fp) { zc_error("popen fail, errno[%d]", errno); goto err; } a_rule->pipe_fd = fileno(a_rule->pipe_fp); if (a_rule->pipe_fd < 0 ) { zc_error("fileno fail, errno[%d]", errno); goto err; } a_rule->output = zlog_rule_output_pipe; break; case '>' : if (STRNCMP(file_path + 1, ==, "syslog", 6)) { a_rule->syslog_facility = syslog_facility_atoi(file_limit); #ifdef _WIN32 zc_error("syslog not support under windows!"); #else if (a_rule->syslog_facility == -187) { zc_error("-187 get"); goto err; } a_rule->output = zlog_rule_output_syslog; openlog(NULL, LOG_NDELAY | LOG_NOWAIT | LOG_PID, LOG_USER); #endif } else if (STRNCMP(file_path + 1, ==, "stdout", 6)) { a_rule->output = zlog_rule_output_stdout; } else if (STRNCMP(file_path + 1, ==, "stderr", 6)) { a_rule->output = zlog_rule_output_stderr; } else { zc_error ("[%s]the string after is not syslog, stdout or stderr", output); goto err; } break; case '$' : // read only MAXLEN_PATH characters from the file_path + 1 strncpy(a_rule->record_name, file_path + 1, MAXLEN_PATH); a_rule->record_name[MAXLEN_PATH] = '\0'; if (file_limit) { /* record path exists */ p = strchr(file_limit, '"'); if (!p) { zc_error("record_path not start with \", [%s]", file_limit); goto err; } p++; /* skip 1st " */ q = strrchr(p, '"'); if (!q) { zc_error("matching \" not found in conf line[%s]", p); goto err; } len = q - p; if (len > sizeof(a_rule->record_path) - 1) { zc_error("record_path too long %ld > %ld", len, sizeof(a_rule->record_path) - 1); goto err; } memcpy(a_rule->record_path, p, len); } /* replace any environment variables like %E(HOME) */ rc = zc_str_replace_env(a_rule->record_path, sizeof(a_rule->record_path)); if (rc) { zc_error("zc_str_replace_env fail"); goto err; } /* try to figure out if the log file path is dynamic or static */ if (strchr(a_rule->record_path, '%') == NULL) { a_rule->output = zlog_rule_output_static_record; } else { zlog_spec_t *a_spec; a_rule->output = zlog_rule_output_dynamic_record; a_rule->dynamic_specs = zc_arraylist_new((zc_arraylist_del_fn)zlog_spec_del); if (!(a_rule->dynamic_specs)) { zc_error("zc_arraylist_new fail"); goto err; } for (p = a_rule->record_path; *p != '\0'; p = q) { a_spec = zlog_spec_new(p, &q, time_cache_count); if (!a_spec) { zc_error("zlog_spec_new fail"); goto err; } rc = zc_arraylist_add(a_rule->dynamic_specs, a_spec); if (rc) { zlog_spec_del(a_spec); zc_error("zc_arraylist_add fail"); goto err; } } } break; default : zc_error("the 1st char[%c] of file_path[%s] is wrong", file_path[0], file_path); goto err; } return a_rule; err: zlog_rule_del(a_rule); return NULL; } void zlog_rule_del(zlog_rule_t * a_rule) { zc_assert(a_rule,); if (a_rule->dynamic_specs) { zc_arraylist_del(a_rule->dynamic_specs); a_rule->dynamic_specs = NULL; } if (a_rule->static_fd > 0) { if (close(a_rule->static_fd)) { zc_error("close fail, maybe cause by write, errno[%d]", errno); } } #ifndef _WIN32 if (a_rule->pipe_fp) { if (pclose(a_rule->pipe_fp) == -1) { zc_error("pclose fail, errno[%d]", errno); } } #endif if (a_rule->archive_specs) { zc_arraylist_del(a_rule->archive_specs); a_rule->archive_specs = NULL; } zc_debug("zlog_rule_del[%p]", a_rule); free(a_rule); return; } /*******************************************************************************/ int zlog_rule_output(zlog_rule_t * a_rule, zlog_thread_t * a_thread) { switch (a_rule->compare_char) { case '*' : return a_rule->output(a_rule, a_thread); break; case '.' : if (a_thread->event->level >= a_rule->level) { return a_rule->output(a_rule, a_thread); } else { return 0; } break; case '=' : if (a_thread->event->level == a_rule->level) { return a_rule->output(a_rule, a_thread); } else { return 0; } break; case '!' : if (a_thread->event->level != a_rule->level) { return a_rule->output(a_rule, a_thread); } else { return 0; } break; } return 0; } /*******************************************************************************/ int zlog_rule_is_wastebin(zlog_rule_t * a_rule) { zc_assert(a_rule, -1); if (STRCMP(a_rule->category, ==, "!")) { return 1; } return 0; } /*******************************************************************************/ int zlog_rule_match_category(zlog_rule_t * a_rule, char *category) { zc_assert(a_rule, -1); zc_assert(category, -1); if (STRCMP(a_rule->category, ==, "*")) { /* '*' match anything, so go on */ return 1; } else if (STRCMP(a_rule->category, ==, category)) { /* accurate compare */ return 1; } else { /* aa_ match aa_xx & aa, but not match aa1_xx */ size_t len; len = strlen(a_rule->category); if (a_rule->category[len - 1] == '_') { if (strlen(category) == len - 1) { len--; } if (STRNCMP(a_rule->category, ==, category, len)) { return 1; } } } return 0; } /*******************************************************************************/ int zlog_rule_set_record(zlog_rule_t * a_rule, zc_hashtable_t *records) { zlog_record_t *a_record; if (a_rule->output != zlog_rule_output_static_record && a_rule->output != zlog_rule_output_dynamic_record) { return 0; /* fliter, may go through not record rule */ } a_record = zc_hashtable_get(records, a_rule->record_name); if (a_record) { a_rule->record_func = a_record->output; } return 0; } zlog-1.2.18/src/rule.h000066400000000000000000000046301460570005000144650ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * @file rule.h * @brief rule decide to output in format by category & level */ #ifndef __zlog_rule_h #define __zlog_rule_h #include #include #include "zc_defs.h" #include "format.h" #include "thread.h" #include "rotater.h" #include "record.h" typedef struct zlog_rule_s zlog_rule_t; typedef int (*zlog_rule_output_fn) (zlog_rule_t * a_rule, zlog_thread_t * a_thread); struct zlog_rule_s { char category[MAXLEN_CFG_LINE + 1]; char compare_char; /* * [*] log all level * [.] log level >= rule level, default * [=] log level == rule level * [!] log level != rule level */ int level; unsigned char level_bitmap[32]; /* for category determine whether output or not */ unsigned int file_perms; int file_open_flags; char file_path[MAXLEN_PATH + 1]; zc_arraylist_t *dynamic_specs; int static_fd; dev_t static_dev; ino_t static_ino; long archive_max_size; int archive_max_count; char archive_path[MAXLEN_PATH + 1]; zc_arraylist_t *archive_specs; FILE *pipe_fp; int pipe_fd; size_t fsync_period; size_t fsync_count; zc_arraylist_t *levels; int syslog_facility; zlog_format_t *format; zlog_rule_output_fn output; char record_name[MAXLEN_PATH + 1]; char record_path[MAXLEN_PATH + 1]; zlog_record_fn record_func; }; zlog_rule_t *zlog_rule_new(char * line, zc_arraylist_t * levels, zlog_format_t * default_format, zc_arraylist_t * formats, unsigned int file_perms, size_t fsync_period, int * time_cache_count); void zlog_rule_del(zlog_rule_t * a_rule); void zlog_rule_profile(zlog_rule_t * a_rule, int flag); int zlog_rule_match_category(zlog_rule_t * a_rule, char *category); int zlog_rule_is_wastebin(zlog_rule_t * a_rule); int zlog_rule_set_record(zlog_rule_t * a_rule, zc_hashtable_t *records); int zlog_rule_output(zlog_rule_t * a_rule, zlog_thread_t * a_thread); #endif zlog-1.2.18/src/spec.c000066400000000000000000000500511460570005000144410ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "fmacros.h" #include #include #include #include #include #include #include #include #include #include "conf.h" #include "spec.h" #include "level_list.h" #include "zc_defs.h" #ifdef _WIN32 #define ZLOG_DEFAULT_TIME_FMT "%Y-%m-%d %H:%M:%S" #else #define ZLOG_DEFAULT_TIME_FMT "%F %T" #endif #define ZLOG_HEX_HEAD \ "\n 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF" /*******************************************************************************/ void zlog_spec_profile(zlog_spec_t * a_spec, int flag) { zc_assert(a_spec,); zc_profile(flag, "----spec[%p][%.*s][%s|%d][%s,%ld,%ld,%s][%s]----", a_spec, a_spec->len, a_spec->str, a_spec->time_fmt, a_spec->time_cache_index, a_spec->print_fmt, (long)a_spec->max_width, (long)a_spec->min_width, a_spec->left_fill_zeros ? "true" : "false", a_spec->mdc_key); return; } /*******************************************************************************/ /* implementation of write function */ static int zlog_spec_write_time_internal(zlog_spec_t * a_spec, zlog_thread_t * a_thread, zlog_buf_t * a_buf, short use_utc) { zlog_time_cache_t * a_cache = a_thread->event->time_caches + a_spec->time_cache_index; time_t now_sec = a_thread->event->time_stamp.tv_sec; struct tm *time; time_t *time_sec; typedef struct tm *(*zlog_spec_time_fn) (const time_t*, struct tm *); zlog_spec_time_fn time_stamp_convert_function; if( use_utc ) { time = &(a_thread->event->time_utc); time_sec = &(a_thread->event->time_utc_sec); time_stamp_convert_function = gmtime_r; } else { time = &(a_thread->event->time_local); time_sec = &(a_thread->event->time_local_sec); time_stamp_convert_function = localtime_r; } /* the event meet the 1st time_spec in his life cycle */ if (!now_sec) { gettimeofday(&(a_thread->event->time_stamp), NULL); now_sec = a_thread->event->time_stamp.tv_sec; } /* When this event's last cached time_local is not now */ if (*time_sec != now_sec) { time_stamp_convert_function(&(now_sec), time); *time_sec = now_sec; } /* When this spec's last cache time string is not now */ if (a_cache->sec != now_sec) { a_cache->len = strftime(a_cache->str, sizeof(a_cache->str), a_spec->time_fmt, time); a_cache->sec = now_sec; } return zlog_buf_append(a_buf, a_cache->str, a_cache->len); } static int zlog_spec_write_time_UTC(zlog_spec_t * a_spec, zlog_thread_t * a_thread, zlog_buf_t * a_buf) { return zlog_spec_write_time_internal(a_spec, a_thread, a_buf, 1); } static int zlog_spec_write_time_local(zlog_spec_t * a_spec, zlog_thread_t * a_thread, zlog_buf_t * a_buf) { return zlog_spec_write_time_internal(a_spec, a_thread, a_buf, 0); } #if 0 static int zlog_spec_write_time_D(zlog_spec_t * a_spec, zlog_thread_t * a_thread, zlog_buf_t * a_buf) { if (!a_thread->event->time_stamp.tv_sec) { gettimeofday(&(a_thread->event->time_stamp), NULL); } /* * It is modified when time slips one second. * So it is a strong cache, as Default time format is always %F %T. * That's why I said %D is faster than %d() */ if (a_thread->event->time_stamp.tv_sec != a_thread->event->time_last_D) { a_thread->event->time_last_D = a_thread->event->time_stamp.tv_sec; localtime_r(&(a_thread->event->time_stamp.tv_sec), &(a_thread->event->time_local)); strftime(a_thread->event->time_cache_D, sizeof(a_thread->event->time_cache_D), ZLOG_DEFAULT_TIME_FMT, &(a_thread->event->time_local) ); } return zlog_buf_append(a_buf, a_thread->event->time_cache_D, sizeof(a_thread->event->time_cache_D) - 1); } #endif static int zlog_spec_write_ms(zlog_spec_t * a_spec, zlog_thread_t * a_thread, zlog_buf_t * a_buf) { if (!a_thread->event->time_stamp.tv_sec) { gettimeofday(&(a_thread->event->time_stamp), NULL); } return zlog_buf_printf_dec32(a_buf, (a_thread->event->time_stamp.tv_usec / 1000), 3); } static int zlog_spec_write_us(zlog_spec_t * a_spec, zlog_thread_t * a_thread, zlog_buf_t * a_buf) { if (!a_thread->event->time_stamp.tv_sec) { gettimeofday(&(a_thread->event->time_stamp), NULL); } return zlog_buf_printf_dec32(a_buf, a_thread->event->time_stamp.tv_usec, 6); } static int zlog_spec_write_mdc(zlog_spec_t * a_spec, zlog_thread_t * a_thread, zlog_buf_t * a_buf) { zlog_mdc_kv_t *a_mdc_kv; a_mdc_kv = zlog_mdc_get_kv(a_thread->mdc, a_spec->mdc_key); if (!a_mdc_kv) { zc_error("zlog_mdc_get_kv key[%s] fail", a_spec->mdc_key); return 0; } return zlog_buf_append(a_buf, a_mdc_kv->value, a_mdc_kv->value_len); } static int zlog_spec_write_str(zlog_spec_t * a_spec, zlog_thread_t * a_thread, zlog_buf_t * a_buf) { return zlog_buf_append(a_buf, a_spec->str, a_spec->len); } static int zlog_spec_write_category(zlog_spec_t * a_spec, zlog_thread_t * a_thread, zlog_buf_t * a_buf) { return zlog_buf_append(a_buf, a_thread->event->category_name, a_thread->event->category_name_len); } static int zlog_spec_write_srcfile(zlog_spec_t * a_spec, zlog_thread_t * a_thread, zlog_buf_t * a_buf) { if (!a_thread->event->file) { return zlog_buf_append(a_buf, "(file=null)", sizeof("(file=null)") - 1); } else { return zlog_buf_append(a_buf, a_thread->event->file, a_thread->event->file_len); } } static int zlog_spec_write_srcfile_neat(zlog_spec_t * a_spec, zlog_thread_t * a_thread, zlog_buf_t * a_buf) { char *p; if ((p = strrchr(a_thread->event->file, '/')) != NULL) { return zlog_buf_append(a_buf, p + 1, (char*)a_thread->event->file + a_thread->event->file_len - p - 1); } else { if (!a_thread->event->file) { return zlog_buf_append(a_buf, "(file=null)", sizeof("(file=null)") - 1); } else { return zlog_buf_append(a_buf, a_thread->event->file, a_thread->event->file_len); } } } static int zlog_spec_write_srcline(zlog_spec_t * a_spec, zlog_thread_t * a_thread, zlog_buf_t * a_buf) { return zlog_buf_printf_dec64(a_buf, a_thread->event->line, 0); } static int zlog_spec_write_srcfunc(zlog_spec_t * a_spec, zlog_thread_t * a_thread, zlog_buf_t * a_buf) { if (!a_thread->event->file) { return zlog_buf_append(a_buf, "(func=null)", sizeof("(func=null)") - 1); } else { return zlog_buf_append(a_buf, a_thread->event->func, a_thread->event->func_len); } } static int zlog_spec_write_hostname(zlog_spec_t * a_spec, zlog_thread_t * a_thread, zlog_buf_t * a_buf) { return zlog_buf_append(a_buf, a_thread->event->host_name, a_thread->event->host_name_len); } static int zlog_spec_write_newline(zlog_spec_t * a_spec, zlog_thread_t * a_thread, zlog_buf_t * a_buf) { return zlog_buf_append(a_buf, FILE_NEWLINE, FILE_NEWLINE_LEN); } static int zlog_spec_write_cr(zlog_spec_t * a_spec, zlog_thread_t * a_thread, zlog_buf_t * a_buf) { return zlog_buf_append(a_buf, "\r", 1); } static int zlog_spec_write_percent(zlog_spec_t * a_spec, zlog_thread_t * a_thread, zlog_buf_t * a_buf) { return zlog_buf_append(a_buf, "%", 1); } static int zlog_spec_write_pid(zlog_spec_t * a_spec, zlog_thread_t * a_thread, zlog_buf_t * a_buf) { /* 1st in event lifecycle */ if (!a_thread->event->pid) { a_thread->event->pid = getpid(); /* compare with previous event */ if (a_thread->event->pid != a_thread->event->last_pid) { a_thread->event->last_pid = a_thread->event->pid; a_thread->event->pid_str_len = sprintf(a_thread->event->pid_str, "%u", a_thread->event->pid); } } return zlog_buf_append(a_buf, a_thread->event->pid_str, a_thread->event->pid_str_len); } static int zlog_spec_write_tid_hex(zlog_spec_t * a_spec, zlog_thread_t * a_thread, zlog_buf_t * a_buf) { /* don't need to get tid again, as tmap_new_thread fetched it already */ /* and fork not change tid */ return zlog_buf_append(a_buf, a_thread->event->tid_hex_str, a_thread->event->tid_hex_str_len); } static int zlog_spec_write_tid_long(zlog_spec_t * a_spec, zlog_thread_t * a_thread, zlog_buf_t * a_buf) { /* don't need to get tid again, as tmap_new_thread fetched it already */ /* and fork not change tid */ return zlog_buf_append(a_buf, a_thread->event->tid_str, a_thread->event->tid_str_len); } static int zlog_spec_write_ktid(zlog_spec_t * a_spec, zlog_thread_t * a_thread, zlog_buf_t * a_buf) { /* don't need to get ktid again, as tmap_new_thread fetched it already */ /* and fork not change tid */ return zlog_buf_append(a_buf, a_thread->event->ktid_str, a_thread->event->ktid_str_len); } static int zlog_spec_write_level_lowercase(zlog_spec_t * a_spec, zlog_thread_t * a_thread, zlog_buf_t * a_buf) { zlog_level_t *a_level; a_level = zlog_level_list_get(zlog_env_conf->levels, a_thread->event->level); return zlog_buf_append(a_buf, a_level->str_lowercase, a_level->str_len); } static int zlog_spec_write_level_uppercase(zlog_spec_t * a_spec, zlog_thread_t * a_thread, zlog_buf_t * a_buf) { zlog_level_t *a_level; a_level = zlog_level_list_get(zlog_env_conf->levels, a_thread->event->level); return zlog_buf_append(a_buf, a_level->str_uppercase, a_level->str_len); } static int zlog_spec_write_usrmsg(zlog_spec_t * a_spec, zlog_thread_t * a_thread, zlog_buf_t * a_buf) { if (a_thread->event->generate_cmd == ZLOG_FMT) { if (a_thread->event->str_format) { return zlog_buf_vprintf(a_buf, a_thread->event->str_format, a_thread->event->str_args); } else { return zlog_buf_append(a_buf, "format=(null)", sizeof("format=(null)")-1); } } else if (a_thread->event->generate_cmd == ZLOG_HEX) { int rc; long line_offset; long byte_offset; /* thread buf start == null or len <= 0 */ if (a_thread->event->hex_buf == NULL) { rc = zlog_buf_append(a_buf, "buf=(null)", sizeof("buf=(null)")-1); goto zlog_hex_exit; } rc = zlog_buf_append(a_buf, ZLOG_HEX_HEAD, sizeof(ZLOG_HEX_HEAD)-1); if (rc) { goto zlog_hex_exit; } line_offset = 0; //byte_offset = 0; while (1) { unsigned char c; rc = zlog_buf_append(a_buf, "\n", 1); if (rc) goto zlog_hex_exit; rc = zlog_buf_printf_dec64(a_buf, line_offset + 1, 10); if (rc) goto zlog_hex_exit; rc = zlog_buf_append(a_buf, " ", 3); if (rc) goto zlog_hex_exit; for (byte_offset = 0; byte_offset < 16; byte_offset++) { if (line_offset * 16 + byte_offset < a_thread->event->hex_buf_len) { c = *((unsigned char *)a_thread->event->hex_buf + line_offset * 16 + byte_offset); rc = zlog_buf_printf_hex(a_buf, c, 2); if (rc) goto zlog_hex_exit; rc = zlog_buf_append(a_buf, " ", 1); if (rc) goto zlog_hex_exit; } else { rc = zlog_buf_append(a_buf, " ", 3); if (rc) goto zlog_hex_exit; } } rc = zlog_buf_append(a_buf, " ", 2); if (rc) goto zlog_hex_exit; for (byte_offset = 0; byte_offset < 16; byte_offset++) { if (line_offset * 16 + byte_offset < a_thread->event->hex_buf_len) { c = *((unsigned char *)a_thread->event->hex_buf + line_offset * 16 + byte_offset); if (c >= 32 && c <= 126) { rc = zlog_buf_append(a_buf,(char*)&c, 1); if (rc) goto zlog_hex_exit; } else { rc = zlog_buf_append(a_buf, ".", 1); if (rc) goto zlog_hex_exit; } } else { rc = zlog_buf_append(a_buf, " ", 1); if (rc) goto zlog_hex_exit; } } if (line_offset * 16 + byte_offset >= a_thread->event->hex_buf_len) { break; } line_offset++; } zlog_hex_exit: if (rc < 0) { zc_error("write hex msg fail"); return -1; } else if (rc > 0) { zc_error("write hex msg, buf is full"); return 1; } return 0; } return 0; } /*******************************************************************************/ /* implementation of gen function */ static int zlog_spec_gen_msg_direct(zlog_spec_t * a_spec, zlog_thread_t * a_thread) { /* no need to reprint %1.2d here */ return a_spec->write_buf(a_spec, a_thread, a_thread->msg_buf); } static int zlog_spec_gen_msg_reformat(zlog_spec_t * a_spec, zlog_thread_t * a_thread) { int rc; zlog_buf_restart(a_thread->pre_msg_buf); rc = a_spec->write_buf(a_spec, a_thread, a_thread->pre_msg_buf); if (rc < 0) { zc_error("a_spec->gen_buf fail"); return -1; } else if (rc > 0) { /* buf is full, try printf */ } return zlog_buf_adjust_append(a_thread->msg_buf, zlog_buf_str(a_thread->pre_msg_buf), zlog_buf_len(a_thread->pre_msg_buf), a_spec->left_adjust, a_spec->left_fill_zeros, a_spec->min_width, a_spec->max_width); } /*******************************************************************************/ static int zlog_spec_gen_path_direct(zlog_spec_t * a_spec, zlog_thread_t * a_thread) { /* no need to reprint %1.2d here */ return a_spec->write_buf(a_spec, a_thread, a_thread->path_buf); } static int zlog_spec_gen_path_reformat(zlog_spec_t * a_spec, zlog_thread_t * a_thread) { int rc; zlog_buf_restart(a_thread->pre_path_buf); rc = a_spec->write_buf(a_spec, a_thread, a_thread->pre_path_buf); if (rc < 0) { zc_error("a_spec->gen_buf fail"); return -1; } else if (rc > 0) { /* buf is full, try printf */ } return zlog_buf_adjust_append(a_thread->path_buf, zlog_buf_str(a_thread->pre_path_buf), zlog_buf_len(a_thread->pre_path_buf), a_spec->left_adjust, a_spec->left_fill_zeros, a_spec->min_width, a_spec->max_width); } /*******************************************************************************/ static int zlog_spec_gen_archive_path_direct(zlog_spec_t * a_spec, zlog_thread_t * a_thread) { /* no need to reprint %1.2d here */ return a_spec->write_buf(a_spec, a_thread, a_thread->archive_path_buf); } static int zlog_spec_gen_archive_path_reformat(zlog_spec_t * a_spec, zlog_thread_t * a_thread) { int rc; zlog_buf_restart(a_thread->pre_path_buf); rc = a_spec->write_buf(a_spec, a_thread, a_thread->pre_path_buf); if (rc < 0) { zc_error("a_spec->gen_buf fail"); return -1; } else if (rc > 0) { /* buf is full, try printf */ } return zlog_buf_adjust_append(a_thread->archive_path_buf, zlog_buf_str(a_thread->pre_path_buf), zlog_buf_len(a_thread->pre_path_buf), a_spec->left_adjust, a_spec->left_fill_zeros, a_spec->min_width, a_spec->max_width); } /*******************************************************************************/ static int zlog_spec_parse_print_fmt(zlog_spec_t * a_spec) { /* -12.35 12 .35 */ char *p, *q; long i, j; p = a_spec->print_fmt; if (*p == '-') { a_spec->left_adjust = 1; p++; } else { if (*p == '0') { a_spec->left_fill_zeros = 1; } a_spec->left_adjust = 0; } i = j = 0; sscanf(p, "%ld.", &i); q = strchr(p, '.'); if (q) sscanf(q, ".%ld", &j); a_spec->min_width = (size_t) i; a_spec->max_width = (size_t) j; return 0; } /*******************************************************************************/ void zlog_spec_del(zlog_spec_t * a_spec) { zc_assert(a_spec,); zc_debug("zlog_spec_del[%p]", a_spec); free(a_spec); } /* a spec may consist of * a const string: /home/bb * a string begin with %: %12.35d(%F %X,%l) */ zlog_spec_t *zlog_spec_new(char *pattern_start, char **pattern_next, int *time_cache_count) { char *p; int nscan = 0; int nread = 0; zlog_spec_t *a_spec; zc_assert(pattern_start, NULL); zc_assert(pattern_next, NULL); a_spec = calloc(1, sizeof(zlog_spec_t)); if (!a_spec) { zc_error("calloc fail, errno[%d]", errno); return NULL; } a_spec->str = p = pattern_start; switch (*p) { case '%': /* a string begin with %: %12.35d(%F %X) */ /* process width and precision char in %-12.35P */ nread = 0; nscan = sscanf(p, "%%%[.0-9-]%n", a_spec->print_fmt, &nread); if (nscan == 1) { a_spec->gen_msg = zlog_spec_gen_msg_reformat; a_spec->gen_path = zlog_spec_gen_path_reformat; a_spec->gen_archive_path = zlog_spec_gen_archive_path_reformat; if (zlog_spec_parse_print_fmt(a_spec)) { zc_error("zlog_spec_parse_print_fmt fail"); goto err; } } else { nread = 1; /* skip the % char */ a_spec->gen_msg = zlog_spec_gen_msg_direct; a_spec->gen_path = zlog_spec_gen_path_direct; a_spec->gen_archive_path = zlog_spec_gen_archive_path_direct; } p += nread; if (*p == 'd' || *p == 'g') { short use_utc = *p == 'g'; if (*(p+1) != '(') { /* without '(' , use default */ strcpy(a_spec->time_fmt, ZLOG_DEFAULT_TIME_FMT); p++; } else if (STRNCMP(p, ==, "d()", 3)) { /* with () but without detail time format, * keep a_spec->time_fmt=="" */ strcpy(a_spec->time_fmt, ZLOG_DEFAULT_TIME_FMT); p += 3; } else { nread = 0; p++; nscan = sscanf(p, "(%[^)])%n", a_spec->time_fmt, &nread); if (nscan != 1) { nread = 0; } p += nread; if (*(p - 1) != ')') { zc_error("in string[%s] can't find match \')\'", a_spec->str); goto err; } } a_spec->time_cache_index = *time_cache_count; (*time_cache_count)++; if (use_utc) { a_spec->write_buf = zlog_spec_write_time_UTC; } else { a_spec->write_buf = zlog_spec_write_time_local; } *pattern_next = p; a_spec->len = p - a_spec->str; break; } if (*p == 'M') { nread = 0; nscan = sscanf(p, "M(%[^)])%n", a_spec->mdc_key, &nread); if (nscan != 1) { nread = 0; if (STRNCMP(p, ==, "M()", 3)) { nread = 3; } } p += nread; if (*(p - 1) != ')') { zc_error("in string[%s] can't find match \')\'", a_spec->str); goto err; } *pattern_next = p; a_spec->len = p - a_spec->str; a_spec->write_buf = zlog_spec_write_mdc; break; } if (STRNCMP(p, ==, "ms", 2)) { p += 2; *pattern_next = p; a_spec->len = p - a_spec->str; a_spec->write_buf = zlog_spec_write_ms; break; } else if (STRNCMP(p, ==, "us", 2)) { p += 2; *pattern_next = p; a_spec->len = p - a_spec->str; a_spec->write_buf = zlog_spec_write_us; break; } *pattern_next = p + 1; a_spec->len = p - a_spec->str + 1; switch (*p) { case 'c': a_spec->write_buf = zlog_spec_write_category; break; case 'D': strcpy(a_spec->time_fmt, ZLOG_DEFAULT_TIME_FMT); a_spec->time_cache_index = *time_cache_count; (*time_cache_count)++; a_spec->write_buf = zlog_spec_write_time_local; break; case 'F': a_spec->write_buf = zlog_spec_write_srcfile; break; case 'f': a_spec->write_buf = zlog_spec_write_srcfile_neat; break; case 'G': strcpy(a_spec->time_fmt, ZLOG_DEFAULT_TIME_FMT); a_spec->time_cache_index = *time_cache_count; (*time_cache_count)++; a_spec->write_buf = zlog_spec_write_time_UTC; break; case 'H': a_spec->write_buf = zlog_spec_write_hostname; break; case 'k': a_spec->write_buf = zlog_spec_write_ktid; break; case 'L': a_spec->write_buf = zlog_spec_write_srcline; break; case 'm': a_spec->write_buf = zlog_spec_write_usrmsg; break; case 'n': a_spec->write_buf = zlog_spec_write_newline; break; case 'r': a_spec->write_buf = zlog_spec_write_cr; break; case 'p': a_spec->write_buf = zlog_spec_write_pid; break; case 'U': a_spec->write_buf = zlog_spec_write_srcfunc; break; case 'v': a_spec->write_buf = zlog_spec_write_level_lowercase; break; case 'V': a_spec->write_buf = zlog_spec_write_level_uppercase; break; case 't': a_spec->write_buf = zlog_spec_write_tid_hex; break; case 'T': a_spec->write_buf = zlog_spec_write_tid_long; break; case '%': a_spec->write_buf = zlog_spec_write_percent; break; default: zc_error("str[%s] in wrong format, p[%c]", a_spec->str, *p); goto err; } break; default: /* a const string: /home/bb */ *pattern_next = strchr(p, '%'); if (*pattern_next) { a_spec->len = *pattern_next - p; } else { a_spec->len = strlen(p); *pattern_next = p + a_spec->len; } a_spec->write_buf = zlog_spec_write_str; a_spec->gen_msg = zlog_spec_gen_msg_direct; a_spec->gen_path = zlog_spec_gen_path_direct; a_spec->gen_archive_path = zlog_spec_gen_archive_path_direct; } zlog_spec_profile(a_spec, ZC_DEBUG); return a_spec; err: zlog_spec_del(a_spec); return NULL; } zlog-1.2.18/src/spec.h000066400000000000000000000035731460570005000144550ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __zlog_spec_h #define __zlog_spec_h #include "event.h" #include "buf.h" #include "thread.h" typedef struct zlog_spec_s zlog_spec_t; /* write buf, according to each spec's Conversion Characters */ typedef int (*zlog_spec_write_fn) (zlog_spec_t * a_spec, zlog_thread_t * a_thread, zlog_buf_t * a_buf); /* gen a_thread->msg or gen a_thread->path by using write_fn */ typedef int (*zlog_spec_gen_fn) (zlog_spec_t * a_spec, zlog_thread_t * a_thread); struct zlog_spec_s { char *str; int len; char time_fmt[MAXLEN_CFG_LINE + 1]; int time_cache_index; char mdc_key[MAXLEN_PATH + 1]; char print_fmt[MAXLEN_CFG_LINE + 1]; int left_adjust; int left_fill_zeros; size_t max_width; size_t min_width; zlog_spec_write_fn write_buf; zlog_spec_gen_fn gen_msg; zlog_spec_gen_fn gen_path; zlog_spec_gen_fn gen_archive_path; }; zlog_spec_t *zlog_spec_new(char *pattern_start, char **pattern_end, int * time_cache_count); void zlog_spec_del(zlog_spec_t * a_spec); void zlog_spec_profile(zlog_spec_t * a_spec, int flag); #define zlog_spec_gen_msg(a_spec, a_thread) \ a_spec->gen_msg(a_spec, a_thread) #define zlog_spec_gen_path(a_spec, a_thread) \ a_spec->gen_path(a_spec, a_thread) #define zlog_spec_gen_archive_path(a_spec, a_thread) \ a_spec->gen_archive_path(a_spec, a_thread) #endif zlog-1.2.18/src/thread.c000066400000000000000000000116711460570005000147630ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include "zc_defs.h" #include "event.h" #include "buf.h" #include "thread.h" #include "mdc.h" void zlog_thread_profile(zlog_thread_t * a_thread, int flag) { zc_assert(a_thread,); zc_profile(flag, "--thread[%p][%p][%p][%p,%p,%p,%p,%p]--", a_thread, a_thread->mdc, a_thread->event, a_thread->pre_path_buf, a_thread->path_buf, a_thread->archive_path_buf, a_thread->pre_msg_buf, a_thread->msg_buf); zlog_mdc_profile(a_thread->mdc, flag); zlog_event_profile(a_thread->event, flag); zlog_buf_profile(a_thread->pre_path_buf, flag); zlog_buf_profile(a_thread->path_buf, flag); zlog_buf_profile(a_thread->archive_path_buf, flag); zlog_buf_profile(a_thread->pre_msg_buf, flag); zlog_buf_profile(a_thread->msg_buf, flag); return; } /*******************************************************************************/ void zlog_thread_del(zlog_thread_t * a_thread) { zc_assert(a_thread,); if (a_thread->mdc) zlog_mdc_del(a_thread->mdc); if (a_thread->event) zlog_event_del(a_thread->event); if (a_thread->pre_path_buf) zlog_buf_del(a_thread->pre_path_buf); if (a_thread->path_buf) zlog_buf_del(a_thread->path_buf); if (a_thread->archive_path_buf) zlog_buf_del(a_thread->archive_path_buf); if (a_thread->pre_msg_buf) zlog_buf_del(a_thread->pre_msg_buf); if (a_thread->msg_buf) zlog_buf_del(a_thread->msg_buf); zc_debug("zlog_thread_del[%p]", a_thread); free(a_thread); return; } zlog_thread_t *zlog_thread_new(int init_version, size_t buf_size_min, size_t buf_size_max, int time_cache_count) { zlog_thread_t *a_thread; a_thread = calloc(1, sizeof(zlog_thread_t)); if (!a_thread) { zc_error("calloc fail, errno[%d]", errno); return NULL; } a_thread->init_version = init_version; a_thread->mdc = zlog_mdc_new(); if (!a_thread->mdc) { zc_error("zlog_mdc_new fail"); goto err; } a_thread->event = zlog_event_new(time_cache_count); if (!a_thread->event) { zc_error("zlog_event_new fail"); goto err; } a_thread->pre_path_buf = zlog_buf_new(MAXLEN_PATH + 1, MAXLEN_PATH + 1, NULL); if (!a_thread->pre_path_buf) { zc_error("zlog_buf_new fail"); goto err; } a_thread->path_buf = zlog_buf_new(MAXLEN_PATH + 1, MAXLEN_PATH + 1, NULL); if (!a_thread->path_buf) { zc_error("zlog_buf_new fail"); goto err; } a_thread->archive_path_buf = zlog_buf_new(MAXLEN_PATH + 1, MAXLEN_PATH + 1, NULL); if (!a_thread->archive_path_buf) { zc_error("zlog_buf_new fail"); goto err; } a_thread->pre_msg_buf = zlog_buf_new(buf_size_min, buf_size_max, "..." FILE_NEWLINE); if (!a_thread->pre_msg_buf) { zc_error("zlog_buf_new fail"); goto err; } a_thread->msg_buf = zlog_buf_new(buf_size_min, buf_size_max, "..." FILE_NEWLINE); if (!a_thread->msg_buf) { zc_error("zlog_buf_new fail"); goto err; } //zlog_thread_profile(a_thread, ZC_DEBUG); return a_thread; err: zlog_thread_del(a_thread); return NULL; } /*******************************************************************************/ int zlog_thread_rebuild_msg_buf(zlog_thread_t * a_thread, size_t buf_size_min, size_t buf_size_max) { zlog_buf_t *pre_msg_buf_new = NULL; zlog_buf_t *msg_buf_new = NULL; zc_assert(a_thread, -1); if ( (a_thread->msg_buf->size_min == buf_size_min) && (a_thread->msg_buf->size_max == buf_size_max)) { zc_debug("buf size not changed, no need rebuild"); return 0; } pre_msg_buf_new = zlog_buf_new(buf_size_min, buf_size_max, "..." FILE_NEWLINE); if (!pre_msg_buf_new) { zc_error("zlog_buf_new fail"); goto err; } msg_buf_new = zlog_buf_new(buf_size_min, buf_size_max, "..." FILE_NEWLINE); if (!msg_buf_new) { zc_error("zlog_buf_new fail"); goto err; } zlog_buf_del(a_thread->pre_msg_buf); a_thread->pre_msg_buf = pre_msg_buf_new; zlog_buf_del(a_thread->msg_buf); a_thread->msg_buf = msg_buf_new; return 0; err: if (pre_msg_buf_new) zlog_buf_del(pre_msg_buf_new); if (msg_buf_new) zlog_buf_del(msg_buf_new); return -1; } int zlog_thread_rebuild_event(zlog_thread_t * a_thread, int time_cache_count) { zlog_event_t *event_new = NULL; zc_assert(a_thread, -1); event_new = zlog_event_new(time_cache_count); if (!event_new) { zc_error("zlog_event_new fail"); goto err; } zlog_event_del(a_thread->event); a_thread->event = event_new; return 0; err: if (event_new) zlog_event_del(event_new); return -1; } /*******************************************************************************/ zlog-1.2.18/src/thread.h000066400000000000000000000025231460570005000147640ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __zlog_thread_h #define __zlog_thread_h #include "zc_defs.h" #include "event.h" #include "buf.h" #include "mdc.h" typedef struct { int init_version; zlog_mdc_t *mdc; zlog_event_t *event; zlog_buf_t *pre_path_buf; zlog_buf_t *path_buf; zlog_buf_t *archive_path_buf; zlog_buf_t *pre_msg_buf; zlog_buf_t *msg_buf; } zlog_thread_t; void zlog_thread_del(zlog_thread_t * a_thread); void zlog_thread_profile(zlog_thread_t * a_thread, int flag); zlog_thread_t *zlog_thread_new(int init_version, size_t buf_size_min, size_t buf_size_max, int time_cache_count); int zlog_thread_rebuild_msg_buf(zlog_thread_t * a_thread, size_t buf_size_min, size_t buf_size_max); int zlog_thread_rebuild_event(zlog_thread_t * a_thread, int time_cache_count); #endif zlog-1.2.18/src/version.h000066400000000000000000000000361460570005000151770ustar00rootroot00000000000000#define ZLOG_VERSION "1.2.12" zlog-1.2.18/src/zc_arraylist.c000066400000000000000000000063301460570005000162160ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include "zc_defs.h" zc_arraylist_t *zc_arraylist_new(zc_arraylist_del_fn del) { zc_arraylist_t *a_list; a_list = (zc_arraylist_t *) calloc(1, sizeof(zc_arraylist_t)); if (!a_list) { zc_error("calloc fail, errno[%d]", errno); return NULL; } a_list->size = ARRAY_LIST_DEFAULT_SIZE; a_list->len = 0; /* this could be NULL */ a_list->del = del; a_list->array = (void **)calloc(a_list->size, sizeof(void *)); if (!a_list->array) { zc_error("calloc fail, errno[%d]", errno); free(a_list); return NULL; } return a_list; } void zc_arraylist_del(zc_arraylist_t * a_list) { int i; if (!a_list) return; if (a_list->del) { for (i = 0; i < a_list->len; i++) { if (a_list->array[i]) a_list->del(a_list->array[i]); } } if (a_list->array) free(a_list->array); free(a_list); return; } static int zc_arraylist_expand_inner(zc_arraylist_t * a_list, int max) { void *tmp; int new_size; int diff_size; new_size = zc_max(a_list->size * 2, max); tmp = realloc(a_list->array, new_size * sizeof(void *)); if (!tmp) { zc_error("realloc fail, errno[%d]", errno); return -1; } a_list->array = (void **)tmp; diff_size = new_size - a_list->size; if (diff_size) memset(a_list->array + a_list->size, 0x00, diff_size * sizeof(void *)); a_list->size = new_size; return 0; } int zc_arraylist_set(zc_arraylist_t * a_list, int idx, void *data) { if (idx > a_list->size - 1) { if (zc_arraylist_expand_inner(a_list, idx)) { zc_error("expand_internal fail"); return -1; } } if (a_list->array[idx] && a_list->del) a_list->del(a_list->array[idx]); a_list->array[idx] = data; if (a_list->len <= idx) a_list->len = idx + 1; return 0; } int zc_arraylist_add(zc_arraylist_t * a_list, void *data) { return zc_arraylist_set(a_list, a_list->len, data); } /* assum idx < len */ static int zc_arraylist_insert_inner(zc_arraylist_t * a_list, int idx, void *data) { if (a_list->array[idx] == NULL) { a_list->array[idx] = data; return 0; } if (a_list->len > a_list->size - 1) { if (zc_arraylist_expand_inner(a_list, 0)) { zc_error("expand_internal fail"); return -1; } } memmove(a_list->array + idx + 1, a_list->array + idx, (a_list->len - idx) * sizeof(void *)); a_list->array[idx] = data; a_list->len++; return 0; } int zc_arraylist_sortadd(zc_arraylist_t * a_list, zc_arraylist_cmp_fn cmp, void *data) { int i; for (i = 0; i < a_list->len; i++) { if ((*cmp) (a_list->array[i], data) > 0) break; } if (i == a_list->len) return zc_arraylist_add(a_list, data); else return zc_arraylist_insert_inner(a_list, i, data); } zlog-1.2.18/src/zc_arraylist.h000066400000000000000000000030641460570005000162240ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __zc_arraylist_h #define __zc_arraylist_h #define ARRAY_LIST_DEFAULT_SIZE 32 typedef void (*zc_arraylist_del_fn) (void *data); typedef int (*zc_arraylist_cmp_fn) (void *data1, void *data2); /* make zc_arraylist_foreach speed up, so keep struct defination here */ typedef struct { void **array; int len; int size; zc_arraylist_del_fn del; } zc_arraylist_t; zc_arraylist_t *zc_arraylist_new(zc_arraylist_del_fn del); void zc_arraylist_del(zc_arraylist_t * a_list); int zc_arraylist_set(zc_arraylist_t * a_list, int i, void *data); int zc_arraylist_add(zc_arraylist_t * a_list, void *data); int zc_arraylist_sortadd(zc_arraylist_t * a_list, zc_arraylist_cmp_fn cmp, void *data); #define zc_arraylist_len(a_list) (a_list->len) #define zc_arraylist_get(a_list, i) \ ((i >= a_list->len) ? NULL : a_list->array[i]) #define zc_arraylist_foreach(a_list, i, a_unit) \ for(i = 0, a_unit = a_list->array[0]; (i < a_list->len) && (a_unit = a_list->array[i], 1) ; i++) #endif zlog-1.2.18/src/zc_defs.h000066400000000000000000000014011460570005000151240ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __zc_defs_h #define __zc_defs_h #include "zc_profile.h" #include "zc_arraylist.h" #include "zc_hashtable.h" #include "zc_xplatform.h" #include "zc_util.h" #endif zlog-1.2.18/src/zc_hashtable.c000066400000000000000000000150761460570005000161460ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include "zc_defs.h" #include "zc_hashtable.h" struct zc_hashtable_s { size_t nelem; zc_hashtable_entry_t **tab; size_t tab_size; zc_hashtable_hash_fn hash; zc_hashtable_equal_fn equal; zc_hashtable_del_fn key_del; zc_hashtable_del_fn value_del; }; zc_hashtable_t *zc_hashtable_new(size_t a_size, zc_hashtable_hash_fn hash, zc_hashtable_equal_fn equal, zc_hashtable_del_fn key_del, zc_hashtable_del_fn value_del) { zc_hashtable_t *a_table; a_table = calloc(1, sizeof(*a_table)); if (!a_table) { zc_error("calloc fail, errno[%d]", errno); return NULL; } a_table->tab = calloc(a_size, sizeof(*(a_table->tab))); if (!a_table->tab) { zc_error("calloc fail, errno[%d]", errno); free(a_table); return NULL; } a_table->tab_size = a_size; a_table->nelem = 0; a_table->hash = hash; a_table->equal = equal; /* these two could be NULL */ a_table->key_del = key_del; a_table->value_del = value_del; return a_table; } void zc_hashtable_del(zc_hashtable_t * a_table) { size_t i; zc_hashtable_entry_t *p; zc_hashtable_entry_t *q; if (!a_table) { zc_error("a_table[%p] is NULL, just do nothing", a_table); return; } for (i = 0; i < a_table->tab_size; i++) { for (p = (a_table->tab)[i]; p; p = q) { q = p->next; if (a_table->key_del) { a_table->key_del(p->key); } if (a_table->value_del) { a_table->value_del(p->value); } free(p); } } if (a_table->tab) free(a_table->tab); free(a_table); return; } void zc_hashtable_clean(zc_hashtable_t * a_table) { size_t i; zc_hashtable_entry_t *p; zc_hashtable_entry_t *q; for (i = 0; i < a_table->tab_size; i++) { for (p = (a_table->tab)[i]; p; p = q) { q = p->next; if (a_table->key_del) { a_table->key_del(p->key); } if (a_table->value_del) { a_table->value_del(p->value); } free(p); } (a_table->tab)[i] = NULL; } a_table->nelem = 0; return; } static int zc_hashtable_rehash(zc_hashtable_t * a_table) { size_t i; size_t j; size_t tab_size; zc_hashtable_entry_t **tab; zc_hashtable_entry_t *p; zc_hashtable_entry_t *q; tab_size = 2 * a_table->tab_size; tab = calloc(tab_size, sizeof(*tab)); if (!tab) { zc_error("calloc fail, errno[%d]", errno); return -1; } for (i = 0; i < a_table->tab_size; i++) { for (p = (a_table->tab)[i]; p; p = q) { q = p->next; p->next = NULL; p->prev = NULL; j = p->hash_key % tab_size; if (tab[j]) { tab[j]->prev = p; p->next = tab[j]; } tab[j] = p; } } free(a_table->tab); a_table->tab = tab; a_table->tab_size = tab_size; return 0; } zc_hashtable_entry_t *zc_hashtable_get_entry(zc_hashtable_t * a_table, const void *a_key) { unsigned int i; zc_hashtable_entry_t *p; i = a_table->hash(a_key) % a_table->tab_size; for (p = (a_table->tab)[i]; p; p = p->next) { if (a_table->equal(a_key, p->key)) return p; } return NULL; } void *zc_hashtable_get(zc_hashtable_t * a_table, const void *a_key) { unsigned int i; zc_hashtable_entry_t *p; i = a_table->hash(a_key) % a_table->tab_size; for (p = (a_table->tab)[i]; p; p = p->next) { if (a_table->equal(a_key, p->key)) return p->value; } return NULL; } int zc_hashtable_put(zc_hashtable_t * a_table, void *a_key, void *a_value) { int rc = 0; unsigned int i; zc_hashtable_entry_t *p = NULL; i = a_table->hash(a_key) % a_table->tab_size; for (p = (a_table->tab)[i]; p; p = p->next) { if (a_table->equal(a_key, p->key)) break; } if (p) { if (a_table->key_del) { a_table->key_del(p->key); } if (a_table->value_del) { a_table->value_del(p->value); } p->key = a_key; p->value = a_value; return 0; } else { if (a_table->nelem > a_table->tab_size * 1.3) { rc = zc_hashtable_rehash(a_table); if (rc) { zc_error("rehash fail"); return -1; } } p = calloc(1, sizeof(*p)); if (!p) { zc_error("calloc fail, errno[%d]", errno); return -1; } p->hash_key = a_table->hash(a_key); p->key = a_key; p->value = a_value; p->next = NULL; p->prev = NULL; i = p->hash_key % a_table->tab_size; if ((a_table->tab)[i]) { (a_table->tab)[i]->prev = p; p->next = (a_table->tab)[i]; } (a_table->tab)[i] = p; a_table->nelem++; } return 0; } void zc_hashtable_remove(zc_hashtable_t * a_table, const void *a_key) { zc_hashtable_entry_t *p; unsigned int i; if (!a_table || !a_key) { zc_error("a_table[%p] or a_key[%p] is NULL, just do nothing", a_table, a_key); return; } i = a_table->hash(a_key) % a_table->tab_size; for (p = (a_table->tab)[i]; p; p = p->next) { if (a_table->equal(a_key, p->key)) break; } if (!p) { zc_error("p[%p] not found in hashtable", p); return; } if (a_table->key_del) { a_table->key_del(p->key); } if (a_table->value_del) { a_table->value_del(p->value); } if (p->next) { p->next->prev = p->prev; } if (p->prev) { p->prev->next = p->next; } else { unsigned int i; i = p->hash_key % a_table->tab_size; a_table->tab[i] = p->next; } free(p); a_table->nelem--; return; } zc_hashtable_entry_t *zc_hashtable_begin(zc_hashtable_t * a_table) { size_t i; zc_hashtable_entry_t *p; for (i = 0; i < a_table->tab_size; i++) { for (p = (a_table->tab)[i]; p; p = p->next) { if (p) return p; } } return NULL; } zc_hashtable_entry_t *zc_hashtable_next(zc_hashtable_t * a_table, zc_hashtable_entry_t * a_entry) { size_t i; size_t j; if (a_entry->next) return a_entry->next; i = a_entry->hash_key % a_table->tab_size; for (j = i + 1; j < a_table->tab_size; j++) { if ((a_table->tab)[j]) { return (a_table->tab)[j]; } } return NULL; } /*******************************************************************************/ unsigned int zc_hashtable_str_hash(const void *str) { unsigned int h = 5381; const char *p = (const char *)str; while (*p != '\0') h = ((h << 5) + h) + (*p++); /* hash * 33 + c */ return h; } int zc_hashtable_str_equal(const void *key1, const void *key2) { return (STRCMP((const char *)key1, ==, (const char *)key2)); } zlog-1.2.18/src/zc_hashtable.h000066400000000000000000000041301460570005000161400ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __zc_hashtalbe_h #define __zc_hashtalbe_h #include typedef struct zc_hashtable_entry_s { unsigned int hash_key; void *key; void *value; struct zc_hashtable_entry_s *prev; struct zc_hashtable_entry_s *next; } zc_hashtable_entry_t; typedef struct zc_hashtable_s zc_hashtable_t; typedef unsigned int (*zc_hashtable_hash_fn) (const void *key); typedef int (*zc_hashtable_equal_fn) (const void *key1, const void *key2); typedef void (*zc_hashtable_del_fn) (void *kv); zc_hashtable_t *zc_hashtable_new(size_t a_size, zc_hashtable_hash_fn hash_fn, zc_hashtable_equal_fn equal_fn, zc_hashtable_del_fn key_del_fn, zc_hashtable_del_fn value_del_fn); void zc_hashtable_del(zc_hashtable_t * a_table); void zc_hashtable_clean(zc_hashtable_t * a_table); int zc_hashtable_put(zc_hashtable_t * a_table, void *a_key, void *a_value); zc_hashtable_entry_t *zc_hashtable_get_entry(zc_hashtable_t * a_table, const void *a_key); void *zc_hashtable_get(zc_hashtable_t * a_table, const void *a_key); void zc_hashtable_remove(zc_hashtable_t * a_table, const void *a_key); zc_hashtable_entry_t *zc_hashtable_begin(zc_hashtable_t * a_table); zc_hashtable_entry_t *zc_hashtable_next(zc_hashtable_t * a_table, zc_hashtable_entry_t * a_entry); #define zc_hashtable_foreach(a_table, a_entry) \ for(a_entry = zc_hashtable_begin(a_table); a_entry; a_entry = zc_hashtable_next(a_table, a_entry)) unsigned int zc_hashtable_str_hash(const void *str); int zc_hashtable_str_equal(const void *key1, const void *key2); #endif zlog-1.2.18/src/zc_profile.c000066400000000000000000000046501460570005000156470ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "fmacros.h" #include #include #include #include #include #include #include #include #include "zc_profile.h" #include "zc_xplatform.h" static void zc_time(char *time_str, size_t time_str_size) { time_t tt; time(&tt); #ifdef _WIN32 struct tm *local_time; local_time = localtime(&tt); strftime(time_str, time_str_size, "%m-%d %H:%M:%S", local_time); #else struct tm local_time; localtime_r(&tt, &local_time); strftime(time_str, time_str_size, "%m-%d %H:%M:%S", &local_time); #endif return; } int zc_profile_inner(int flag, const char *file, const long line, const char *fmt, ...) { va_list args; char time_str[20 + 1]; FILE *fp = NULL; static char *debug_log = NULL; static char *error_log = NULL; static size_t init_flag = 0; if (!init_flag) { init_flag = 1; debug_log = getenv("ZLOG_PROFILE_DEBUG"); error_log = getenv("ZLOG_PROFILE_ERROR"); } switch (flag) { case ZC_DEBUG: if (debug_log == NULL) return 0; fp = fopen(debug_log, "a"); if (!fp) return -1; zc_time(time_str, sizeof(time_str)); fprintf(fp, "%s DEBUG (%d:%s:%ld) ", time_str, getpid(), file, line); break; case ZC_WARN: if (error_log == NULL) return 0; fp = fopen(error_log, "a"); if (!fp) return -1; zc_time(time_str, sizeof(time_str)); fprintf(fp, "%s WARN (%d:%s:%ld) ", time_str, getpid(), file, line); break; case ZC_ERROR: if (error_log == NULL) return 0; fp = fopen(error_log, "a"); if (!fp) return -1; zc_time(time_str, sizeof(time_str)); fprintf(fp, "%s ERROR (%d:%s:%ld) ", time_str, getpid(), file, line); break; } /* writing file twice(time & msg) is not atomic * may cause cross * but avoid log size limit */ va_start(args, fmt); vfprintf(fp, fmt, args); va_end(args); fprintf(fp, "\n"); fclose(fp); return 0; } zlog-1.2.18/src/zc_profile.h000066400000000000000000000033751460570005000156570ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __zc_profile_h #define __zc_profile_h #include #define EMPTY() #define zc_assert(expr, rv) \ if(!(expr)) { \ zc_error(#expr" is null or 0"); \ return rv; \ } enum zc_profile_flag { ZC_DEBUG = 0, ZC_WARN = 1, ZC_ERROR = 2 }; #if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L #define zc_debug(...) \ zc_profile_inner(ZC_DEBUG, __FILE__, __LINE__, __VA_ARGS__) #define zc_warn(...) \ zc_profile_inner(ZC_WARN, __FILE__, __LINE__, __VA_ARGS__) #define zc_error(...) \ zc_profile_inner(ZC_ERROR, __FILE__, __LINE__, __VA_ARGS__) #define zc_profile(flag, ...) \ zc_profile_inner(flag, __FILE__, __LINE__, __VA_ARGS__) #elif defined __GNUC__ #define zc_debug(fmt, args...) \ zc_profile_inner(ZC_DEBUG, __FILE__, __LINE__, fmt, ## args) #define zc_warn(fmt, args...) \ zc_profile_inner(ZC_WARN, __FILE__, __LINE__, fmt, ## args) #define zc_error(fmt, args...) \ zc_profile_inner(ZC_ERROR, __FILE__, __LINE__, fmt, ## args) #define zc_profile(flag, fmt, args...) \ zc_profile_inner(flag, __FILE__, __LINE__, fmt, ## args) #endif int zc_profile_inner(int flag, const char *file, const long line, const char *fmt, ...); #endif zlog-1.2.18/src/zc_util.c000066400000000000000000000060641460570005000151650ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include "zc_defs.h" size_t zc_parse_byte_size(char *astring) { /* Parse size in bytes depending on the suffix. Valid suffixes are KB, MB and GB */ char *p; char *q; size_t sz; long res; int c, m; zc_assert(astring, 0); /* clear space */ for (p = q = astring; *p != '\0'; p++) { if (isspace(*p)) { continue; } else { *q = *p; q++; } } *q = '\0'; sz = strlen(astring); res = strtol(astring, (char **)NULL, 10); if (res <= 0) return 0; if (astring[sz - 1] == 'B' || astring[sz - 1] == 'b') { c = astring[sz - 2]; m = 1024; } else { c = astring[sz - 1]; m = 1000; } switch (c) { case 'K': case 'k': res *= m; break; case 'M': case 'm': res *= m * m; break; case 'G': case 'g': res *= m * m * m; break; default: if (!isdigit(c)) { zc_error("Wrong suffix parsing " "size in bytes for string [%s], ignoring suffix", astring); } break; } return (res); } /*******************************************************************************/ int zc_str_replace_env(char *str, size_t str_size) { char *p; char *q; char fmt[MAXLEN_CFG_LINE + 1]; char env_key[MAXLEN_CFG_LINE + 1]; char env_value[MAXLEN_CFG_LINE + 1]; int str_len; int env_value_len; int nscan; int nread; str_len = strlen(str); q = str; do { p = strchr(q, '%'); if (!p) { /* can't find more % */ break; } memset(fmt, 0x00, sizeof(fmt)); memset(env_key, 0x00, sizeof(env_key)); memset(env_value, 0x00, sizeof(env_value)); nread = 0; nscan = sscanf(p + 1, "%[.0-9-]%n", fmt + 1, &nread); if (nscan == 1) { fmt[0] = '%'; fmt[nread + 1] = 's'; } else { nread = 0; strcpy(fmt, "%s"); } q = p + 1 + nread; nscan = sscanf(q, "E(%[^)])%n", env_key, &nread); if (nscan == 0) { continue; } q += nread; if (*(q - 1) != ')') { zc_error("in string[%s] can't find match )", p); return -1; } env_value_len = snprintf(env_value, sizeof(env_value), fmt, getenv(env_key)); if (env_value_len < 0 || env_value_len >= sizeof(env_value)) { zc_error("snprintf fail, errno[%d], evn_value_len[%d]", errno, env_value_len); return -1; } str_len = str_len - (q - p) + env_value_len; if (str_len > str_size - 1) { zc_error("repalce env_value[%s] cause overlap", env_value); return -1; } memmove(p + env_value_len, q, strlen(q) + 1); memcpy(p, env_value, env_value_len); } while (1); return 0; } zlog-1.2.18/src/zc_util.h000066400000000000000000000014741460570005000151720ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __zc_util_h #define __zc_util_h size_t zc_parse_byte_size(char *astring); int zc_str_replace_env(char *str, size_t str_size); #define zc_max(a,b) ((a) > (b) ? (a) : (b)) #define zc_min(a,b) ((a) < (b) ? (a) : (b)) #endif zlog-1.2.18/src/zc_xplatform.h000066400000000000000000000036431460570005000162310ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __zc_xplatform_h #define __zc_xplatform_h #include #define ZLOG_INT32_LEN sizeof("-2147483648") - 1 #define ZLOG_INT64_LEN sizeof("-9223372036854775808") - 1 #if ((__GNU__ == 2) && (__GNUC_MINOR__ < 8)) #define ZLOG_MAX_UINT32_VALUE (uint32_t) 0xffffffffLL #else #define ZLOG_MAX_UINT32_VALUE (uint32_t) 0xffffffff #endif #define ZLOG_MAX_INT32_VALUE (uint32_t) 0x7fffffff #define MAXLEN_PATH 1024 #define MAXLEN_CFG_LINE (MAXLEN_PATH * 4) #define MAXLINES_NO 128 #define FILE_NEWLINE "\n" #define FILE_NEWLINE_LEN 1 #include #include #define STRCMP(_a_,_C_,_b_) ( strcmp(_a_,_b_) _C_ 0 ) #define STRNCMP(_a_,_C_,_b_,_n_) ( strncmp(_a_,_b_,_n_) _C_ 0 ) #define STRICMP(_a_,_C_,_b_) ( strcasecmp(_a_,_b_) _C_ 0 ) #define STRNICMP(_a_,_C_,_b_,_n_) ( strncasecmp(_a_,_b_,_n_) _C_ 0 ) #ifdef __APPLE__ #include #endif /* Define zlog_fstat to fstat or fstat64() */ #if defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_6) #define zlog_fstat fstat64 #define zlog_stat stat64 #elif defined(_WIN32) #define zlog_fstat _fstat #define zlog_stat _stat #else #define zlog_fstat fstat #define zlog_stat stat #endif /* Define zlog_fsync to fdatasync() in Linux and fsync() for all the rest */ #ifdef __linux__ #define zlog_fsync fdatasync #else #define zlog_fsync fsync #endif #endif zlog-1.2.18/src/zlog-chk-conf.c000066400000000000000000000031351460570005000161510ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "fmacros.h" #include #include #include #include #include #include "zlog.h" #include "version.h" int main(int argc, char *argv[]) { int rc = 0; int op; int quiet = 0; static const char *help = "usage: zlog-chk-conf [conf files]...\n" "\t-q,\tsuppress non-error message\n" "\t-h,\tshow help message\n" "zlog version: " ZLOG_VERSION "\n"; while((op = getopt(argc, argv, "qhv")) > 0) { if (op == 'h') { fputs(help, stdout); return 0; } else if (op == 'q') { quiet = 1; } } argc -= optind; argv += optind; if (argc == 0) { fputs(help, stdout); return -1; } setenv("ZLOG_PROFILE_ERROR", "/dev/stderr", 1); setenv("ZLOG_CHECK_FORMAT_RULE", "1", 1); while (argc > 0) { rc = zlog_init(*argv); if (rc) { printf("\n---[%s] syntax error, see error message above\n", *argv); exit(2); } else { zlog_fini(); if (!quiet) { printf("--[%s] syntax right\n", *argv); } } argc--; argv++; } exit(0); } zlog-1.2.18/src/zlog.c000066400000000000000000000747271460570005000145020ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "fmacros.h" #include #include #include #include #include #include "conf.h" #include "category_table.h" #include "record_table.h" #include "mdc.h" #include "zc_defs.h" #include "rule.h" #include "version.h" /*******************************************************************************/ extern char *zlog_git_sha1; /*******************************************************************************/ static pthread_rwlock_t zlog_env_lock = PTHREAD_RWLOCK_INITIALIZER; zlog_conf_t *zlog_env_conf; static pthread_key_t zlog_thread_key; static zc_hashtable_t *zlog_env_categories; static zc_hashtable_t *zlog_env_records; static zlog_category_t *zlog_default_category; static size_t zlog_env_reload_conf_count; static int zlog_env_is_init = 0; static int zlog_env_init_version = 0; /*******************************************************************************/ /* inner no need thread-safe */ static void zlog_fini_inner(void) { /* pthread_key_delete(zlog_thread_key); */ /* never use pthread_key_delete, * it will cause other thread can't release zlog_thread_t * after one thread call pthread_key_delete * also key not init will cause a core dump */ if (zlog_env_categories) zlog_category_table_del(zlog_env_categories); zlog_env_categories = NULL; zlog_default_category = NULL; if (zlog_env_records) zlog_record_table_del(zlog_env_records); zlog_env_records = NULL; if (zlog_env_conf) zlog_conf_del(zlog_env_conf); zlog_env_conf = NULL; return; } static void zlog_clean_rest_thread(void) { zlog_thread_t *a_thread; a_thread = pthread_getspecific(zlog_thread_key); if (!a_thread) return; zlog_thread_del(a_thread); return; } static int zlog_init_inner_from_string(const char *config_string) { int rc = 0; /* the 1st time in the whole process do init */ if (zlog_env_init_version == 0) { /* clean up is done by OS when a thread call pthread_exit */ rc = pthread_key_create(&zlog_thread_key, (void (*) (void *)) zlog_thread_del); if (rc) { zc_error("pthread_key_create fail, rc[%d]", rc); goto err; } /* if some thread do not call pthread_exit, like main thread * atexit will clean it */ rc = atexit(zlog_clean_rest_thread); if (rc) { zc_error("atexit fail, rc[%d]", rc); goto err; } zlog_env_init_version++; } /* else maybe after zlog_fini() and need not create pthread_key */ zlog_env_conf = zlog_conf_new_from_string(config_string); if (!zlog_env_conf) { zc_error("zlog_conf_new[%s] fail", config_string); goto err; } zlog_env_categories = zlog_category_table_new(); if (!zlog_env_categories) { zc_error("zlog_category_table_new fail"); goto err; } zlog_env_records = zlog_record_table_new(); if (!zlog_env_records) { zc_error("zlog_record_table_new fail"); goto err; } return 0; err: zlog_fini_inner(); return -1; } static int zlog_init_inner(const char *config) { int rc = 0; /* the 1st time in the whole process do init */ if (zlog_env_init_version == 0) { /* clean up is done by OS when a thread call pthread_exit */ rc = pthread_key_create(&zlog_thread_key, (void (*) (void *)) zlog_thread_del); if (rc) { zc_error("pthread_key_create fail, rc[%d]", rc); goto err; } /* if some thread do not call pthread_exit, like main thread * atexit will clean it */ rc = atexit(zlog_clean_rest_thread); if (rc) { zc_error("atexit fail, rc[%d]", rc); goto err; } zlog_env_init_version++; } /* else maybe after zlog_fini() and need not create pthread_key */ zlog_env_conf = zlog_conf_new(config); if (!zlog_env_conf) { zc_error("zlog_conf_new[%s] fail", config); goto err; } zlog_env_categories = zlog_category_table_new(); if (!zlog_env_categories) { zc_error("zlog_category_table_new fail"); goto err; } zlog_env_records = zlog_record_table_new(); if (!zlog_env_records) { zc_error("zlog_record_table_new fail"); goto err; } return 0; err: zlog_fini_inner(); return -1; } /*******************************************************************************/ int zlog_init(const char *config) { int rc; zc_debug("------zlog_init start------"); zc_debug("------compile time[%s %s], version[%s]------", __DATE__, __TIME__, ZLOG_VERSION); rc = pthread_rwlock_wrlock(&zlog_env_lock); if (rc) { zc_error("pthread_rwlock_wrlock fail, rc[%d]", rc); return -1; } if (zlog_env_is_init) { zc_error("already init, use zlog_reload pls"); goto err; } if (zlog_init_inner(config)) { zc_error("zlog_init_inner[%s] fail", config); goto err; } zlog_env_is_init = 1; zlog_env_init_version++; zc_debug("------zlog_init success end------"); rc = pthread_rwlock_unlock(&zlog_env_lock); if (rc) { zc_error("pthread_rwlock_unlock fail, rc=[%d]", rc); return -1; } return 0; err: zc_error("------zlog_init fail end------"); rc = pthread_rwlock_unlock(&zlog_env_lock); if (rc) { zc_error("pthread_rwlock_unlock fail, rc=[%d]", rc); return -1; } return -1; } int zlog_init_from_string(const char *config_string) { int rc; zc_debug("------zlog_init start------"); zc_debug("------compile time[%s %s], version[%s]------", __DATE__, __TIME__, ZLOG_VERSION); rc = pthread_rwlock_wrlock(&zlog_env_lock); if (rc) { zc_error("pthread_rwlock_wrlock fail, rc[%d]", rc); return -1; } if (zlog_env_is_init) { zc_error("already init, use zlog_reload pls"); goto err; } if (zlog_init_inner_from_string(config_string)) { zc_error("zlog_init_inner[%s] fail", config_string); goto err; } zlog_env_is_init = 1; zlog_env_init_version++; zc_debug("------zlog_init success end------"); rc = pthread_rwlock_unlock(&zlog_env_lock); if (rc) { zc_error("pthread_rwlock_unlock fail, rc=[%d]", rc); return -1; } return 0; err: zc_error("------zlog_init fail end------"); rc = pthread_rwlock_unlock(&zlog_env_lock); if (rc) { zc_error("pthread_rwlock_unlock fail, rc=[%d]", rc); return -1; } return -1; } int dzlog_init(const char *config, const char *cname) { int rc = 0; zc_debug("------dzlog_init start------"); zc_debug("------compile time[%s %s], version[%s]------", __DATE__, __TIME__, ZLOG_VERSION); rc = pthread_rwlock_wrlock(&zlog_env_lock); if (rc) { zc_error("pthread_rwlock_wrlock fail, rc[%d]", rc); return -1; } if (zlog_env_is_init) { zc_error("already init, use zlog_reload pls"); goto err; } if (zlog_init_inner(config)) { zc_error("zlog_init_inner[%s] fail", config); goto err; } zlog_default_category = zlog_category_table_fetch_category( zlog_env_categories, cname, zlog_env_conf->rules); if (!zlog_default_category) { zc_error("zlog_category_table_fetch_category[%s] fail", cname); goto err; } zlog_env_is_init = 1; zlog_env_init_version++; zc_debug("------dzlog_init success end------"); rc = pthread_rwlock_unlock(&zlog_env_lock); if (rc) { zc_error("pthread_rwlock_unlock fail, rc=[%d]", rc); return -1; } return 0; err: zc_error("------dzlog_init fail end------"); rc = pthread_rwlock_unlock(&zlog_env_lock); if (rc) { zc_error("pthread_rwlock_unlock fail, rc=[%d]", rc); return -1; } return -1; } /*******************************************************************************/ int zlog_reload(const char *config) { int rc = 0; int i = 0; zlog_conf_t *new_conf = NULL; zlog_rule_t *a_rule; int c_up = 0; zc_debug("------zlog_reload start------"); rc = pthread_rwlock_wrlock(&zlog_env_lock); if (rc) { zc_error("pthread_rwlock_wrlock fail, rc[%d]", rc); return -1; } if (!zlog_env_is_init) { zc_error("never call zlog_init() or dzlog_init() before"); goto quit; } /* use last conf file */ if (config == NULL) config = zlog_env_conf->file; /* reach reload period */ if (config == (char*)-1) { /* test again, avoid other threads already reloaded */ if (zlog_env_reload_conf_count > zlog_env_conf->reload_conf_period) { config = zlog_env_conf->file; } else { /* do nothing, already done */ goto quit; } } /* reset counter, whether automaticlly or mannually */ zlog_env_reload_conf_count = 0; new_conf = zlog_conf_new(config); if (!new_conf) { zc_error("zlog_conf_new fail"); goto err; } zc_arraylist_foreach(new_conf->rules, i, a_rule) { zlog_rule_set_record(a_rule, zlog_env_records); } if (zlog_category_table_update_rules(zlog_env_categories, new_conf->rules)) { c_up = 0; zc_error("zlog_category_table_update fail"); goto err; } else { c_up = 1; } zlog_env_init_version++; if (c_up) zlog_category_table_commit_rules(zlog_env_categories); zlog_conf_del(zlog_env_conf); zlog_env_conf = new_conf; zc_debug("------zlog_reload success, total init verison[%d] ------", zlog_env_init_version); rc = pthread_rwlock_unlock(&zlog_env_lock); if (rc) { zc_error("pthread_rwlock_unlock fail, rc=[%d]", rc); return -1; } return 0; err: /* fail, roll back everything */ zc_warn("zlog_reload fail, use old conf file, still working"); if (new_conf) zlog_conf_del(new_conf); if (c_up) zlog_category_table_rollback_rules(zlog_env_categories); zc_error("------zlog_reload fail, total init version[%d] ------", zlog_env_init_version); rc = pthread_rwlock_unlock(&zlog_env_lock); if (rc) { zc_error("pthread_rwlock_unlock fail, rc=[%d]", rc); return -1; } return -1; quit: zc_debug("------zlog_reload do nothing------"); rc = pthread_rwlock_unlock(&zlog_env_lock); if (rc) { zc_error("pthread_rwlock_unlock fail, rc=[%d]", rc); return -1; } return 0; } /*******************************************************************************/ int zlog_reload_from_string(const char *conf_string) { int rc = 0; int i = 0; zlog_conf_t *new_conf = NULL; zlog_rule_t *a_rule; int c_up = 0; zc_debug("------zlog_reload start------"); rc = pthread_rwlock_wrlock(&zlog_env_lock); if (rc) { zc_error("pthread_rwlock_wrlock fail, rc[%d]", rc); return -1; } if (!zlog_env_is_init) { zc_error("never call zlog_init() or dzlog_init() before"); goto quit; } if (conf_string == NULL) goto quit; new_conf = zlog_conf_new_from_string(conf_string); if (!new_conf) { zc_error("zlog_conf_new fail"); goto err; } zc_arraylist_foreach(new_conf->rules, i, a_rule) { zlog_rule_set_record(a_rule, zlog_env_records); } if (zlog_category_table_update_rules(zlog_env_categories, new_conf->rules)) { c_up = 0; zc_error("zlog_category_table_update fail"); goto err; } else { c_up = 1; } zlog_env_init_version++; if (c_up) zlog_category_table_commit_rules(zlog_env_categories); zlog_conf_del(zlog_env_conf); zlog_env_conf = new_conf; zc_debug("------zlog_reload success, total init verison[%d] ------", zlog_env_init_version); rc = pthread_rwlock_unlock(&zlog_env_lock); if (rc) { zc_error("pthread_rwlock_unlock fail, rc=[%d]", rc); return -1; } return 0; err: /* fail, roll back everything */ zc_warn("zlog_reload fail, use old conf file, still working"); if (new_conf) zlog_conf_del(new_conf); if (c_up) zlog_category_table_rollback_rules(zlog_env_categories); zc_error("------zlog_reload fail, total init version[%d] ------", zlog_env_init_version); rc = pthread_rwlock_unlock(&zlog_env_lock); if (rc) { zc_error("pthread_rwlock_unlock fail, rc=[%d]", rc); return -1; } return -1; quit: zc_debug("------zlog_reload do nothing------"); rc = pthread_rwlock_unlock(&zlog_env_lock); if (rc) { zc_error("pthread_rwlock_unlock fail, rc=[%d]", rc); return -1; } return 0; } /*******************************************************************************/ void zlog_fini(void) { int rc = 0; zc_debug("------zlog_fini start------"); rc = pthread_rwlock_wrlock(&zlog_env_lock); if (rc) { zc_error("pthread_rwlock_wrlock fail, rc[%d]", rc); return; } if (!zlog_env_is_init) { zc_error("before finish, must zlog_init() or dzlog_init() first"); goto exit; } zlog_fini_inner(); zlog_env_is_init = 0; exit: zc_debug("------zlog_fini end------"); rc = pthread_rwlock_unlock(&zlog_env_lock); if (rc) { zc_error("pthread_rwlock_unlock fail, rc=[%d]", rc); return; } return; } /*******************************************************************************/ zlog_category_t *zlog_get_category(const char *cname) { int rc = 0; zlog_category_t *a_category = NULL; zc_assert(cname, NULL); zc_debug("------zlog_get_category[%s] start------", cname); rc = pthread_rwlock_wrlock(&zlog_env_lock); if (rc) { zc_error("pthread_rwlock_wrlock fail, rc[%d]", rc); return NULL; } if (!zlog_env_is_init) { zc_error("never call zlog_init() or dzlog_init() before"); a_category = NULL; goto err; } a_category = zlog_category_table_fetch_category( zlog_env_categories, cname, zlog_env_conf->rules); if (!a_category) { zc_error("zlog_category_table_fetch_category[%s] fail", cname); goto err; } zc_debug("------zlog_get_category[%s] success, end------ ", cname); rc = pthread_rwlock_unlock(&zlog_env_lock); if (rc) { zc_error("pthread_rwlock_unlock fail, rc=[%d]", rc); return NULL; } return a_category; err: zc_error("------zlog_get_category[%s] fail, end------ ", cname); rc = pthread_rwlock_unlock(&zlog_env_lock); if (rc) { zc_error("pthread_rwlock_unlock fail, rc=[%d]", rc); return NULL; } return NULL; } int dzlog_set_category(const char *cname) { int rc = 0; zc_assert(cname, -1); zc_debug("------dzlog_set_category[%s] start------", cname); rc = pthread_rwlock_wrlock(&zlog_env_lock); if (rc) { zc_error("pthread_rwlock_wrlock fail, rc[%d]", rc); return -1; } if (!zlog_env_is_init) { zc_error("never call zlog_init() or dzlog_init() before"); goto err; } zlog_default_category = zlog_category_table_fetch_category( zlog_env_categories, cname, zlog_env_conf->rules); if (!zlog_default_category) { zc_error("zlog_category_table_fetch_category[%s] fail", cname); goto err; } zc_debug("------dzlog_set_category[%s] end, success------ ", cname); rc = pthread_rwlock_unlock(&zlog_env_lock); if (rc) { zc_error("pthread_rwlock_unlock fail, rc=[%d]", rc); return -1; } return 0; err: zc_error("------dzlog_set_category[%s] end, fail------ ", cname); rc = pthread_rwlock_unlock(&zlog_env_lock); if (rc) { zc_error("pthread_rwlock_unlock fail, rc=[%d]", rc); return -1; } return -1; } /*******************************************************************************/ #define zlog_fetch_thread(a_thread, fail_goto) do { \ int rd = 0; \ a_thread = pthread_getspecific(zlog_thread_key); \ if (!a_thread) { \ a_thread = zlog_thread_new(zlog_env_init_version, \ zlog_env_conf->buf_size_min, zlog_env_conf->buf_size_max, \ zlog_env_conf->time_cache_count); \ if (!a_thread) { \ zc_error("zlog_thread_new fail"); \ goto fail_goto; \ } \ \ rd = pthread_setspecific(zlog_thread_key, a_thread); \ if (rd) { \ zlog_thread_del(a_thread); \ zc_error("pthread_setspecific fail, rd[%d]", rd); \ goto fail_goto; \ } \ } \ \ if (a_thread->init_version != zlog_env_init_version) { \ /* as mdc is still here, so can not easily del and new */ \ rd = zlog_thread_rebuild_msg_buf(a_thread, \ zlog_env_conf->buf_size_min, \ zlog_env_conf->buf_size_max); \ if (rd) { \ zc_error("zlog_thread_resize_msg_buf fail, rd[%d]", rd); \ goto fail_goto; \ } \ \ rd = zlog_thread_rebuild_event(a_thread, zlog_env_conf->time_cache_count); \ if (rd) { \ zc_error("zlog_thread_resize_msg_buf fail, rd[%d]", rd); \ goto fail_goto; \ } \ a_thread->init_version = zlog_env_init_version; \ } \ } while (0) /*******************************************************************************/ int zlog_put_mdc(const char *key, const char *value) { int rc = 0; zlog_thread_t *a_thread; zc_assert(key, -1); zc_assert(value, -1); rc = pthread_rwlock_rdlock(&zlog_env_lock); if (rc) { zc_error("pthread_rwlock_wrlock fail, rc[%d]", rc); return -1; } if (!zlog_env_is_init) { zc_error("never call zlog_init() or dzlog_init() before"); goto err; } zlog_fetch_thread(a_thread, err); if (zlog_mdc_put(a_thread->mdc, key, value)) { zc_error("zlog_mdc_put fail, key[%s], value[%s]", key, value); goto err; } rc = pthread_rwlock_unlock(&zlog_env_lock); if (rc) { zc_error("pthread_rwlock_unlock fail, rc=[%d]", rc); return -1; } return 0; err: rc = pthread_rwlock_unlock(&zlog_env_lock); if (rc) { zc_error("pthread_rwlock_unlock fail, rc=[%d]", rc); return -1; } return -1; } char *zlog_get_mdc(char *key) { int rc = 0; char *value = NULL; zlog_thread_t *a_thread; zc_assert(key, NULL); rc = pthread_rwlock_rdlock(&zlog_env_lock); if (rc) { zc_error("pthread_rwlock_rdlock fail, rc[%d]", rc); return NULL; } if (!zlog_env_is_init) { zc_error("never call zlog_init() or dzlog_init() before"); goto err; } a_thread = pthread_getspecific(zlog_thread_key); if (!a_thread) { zc_error("thread not found, maybe not use zlog_put_mdc before"); goto err; } value = zlog_mdc_get(a_thread->mdc, key); if (!value) { zc_error("key[%s] not found in mdc", key); goto err; } rc = pthread_rwlock_unlock(&zlog_env_lock); if (rc) { zc_error("pthread_rwlock_unlock fail, rc=[%d]", rc); return NULL; } return value; err: rc = pthread_rwlock_unlock(&zlog_env_lock); if (rc) { zc_error("pthread_rwlock_unlock fail, rc=[%d]", rc); return NULL; } return NULL; } void zlog_remove_mdc(char *key) { int rc = 0; zlog_thread_t *a_thread; zc_assert(key, ); rc = pthread_rwlock_rdlock(&zlog_env_lock); if (rc) { zc_error("pthread_rwlock_rdlock fail, rc[%d]", rc); return; } if (!zlog_env_is_init) { zc_error("never call zlog_init() or dzlog_init() before"); goto exit; } a_thread = pthread_getspecific(zlog_thread_key); if (!a_thread) { zc_error("thread not found, maybe not use zlog_put_mdc before"); goto exit; } zlog_mdc_remove(a_thread->mdc, key); exit: rc = pthread_rwlock_unlock(&zlog_env_lock); if (rc) { zc_error("pthread_rwlock_unlock fail, rc=[%d]", rc); return; } return; } void zlog_clean_mdc(void) { int rc = 0; zlog_thread_t *a_thread; rc = pthread_rwlock_rdlock(&zlog_env_lock); if (rc) {; zc_error("pthread_rwlock_rdlock fail, rc[%d]", rc); return; } if (!zlog_env_is_init) { zc_error("never call zlog_init() or dzlog_init() before"); goto exit; } a_thread = pthread_getspecific(zlog_thread_key); if (!a_thread) { zc_error("thread not found, maybe not use zlog_put_mdc before"); goto exit; } zlog_mdc_clean(a_thread->mdc); exit: rc = pthread_rwlock_unlock(&zlog_env_lock); if (rc) { zc_error("pthread_rwlock_unlock fail, rc=[%d]", rc); return; } return; } int zlog_level_switch(zlog_category_t * category, int level) { // This is NOT thread safe. memset(category->level_bitmap, 0x00, sizeof(category->level_bitmap)); category->level_bitmap[level / 8] |= ~(0xFF << (8 - level % 8)); memset(category->level_bitmap + level / 8 + 1, 0xFF, sizeof(category->level_bitmap) - level / 8 - 1); return 0; } /*******************************************************************************/ void vzlog(zlog_category_t * category, const char *file, size_t filelen, const char *func, size_t funclen, long line, int level, const char *format, va_list args) { zlog_thread_t *a_thread; /* The bitmap determination here is not under the protection of rdlock. * It may be changed by other CPU by zlog_reload() halfway. * * Old or strange value may be read here, * but it is safe, the bitmap is valid as long as category exist, * And will be the right value after zlog_reload() * * For speed up, if one log will not be output, * There is no need to aquire rdlock. */ if (zlog_category_needless_level(category, level)) return; pthread_rwlock_rdlock(&zlog_env_lock); if (!zlog_env_is_init) { zc_error("never call zlog_init() or dzlog_init() before"); goto exit; } zlog_fetch_thread(a_thread, exit); zlog_event_set_fmt(a_thread->event, category->name, category->name_len, file, filelen, func, funclen, line, level, format, args); if (zlog_category_output(category, a_thread)) { zc_error("zlog_output fail, srcfile[%s], srcline[%ld]", file, line); goto exit; } if (zlog_env_conf->reload_conf_period && ++zlog_env_reload_conf_count > zlog_env_conf->reload_conf_period ) { /* under the protection of lock read env conf */ goto reload; } exit: pthread_rwlock_unlock(&zlog_env_lock); return; reload: pthread_rwlock_unlock(&zlog_env_lock); /* will be wrlock, so after unlock */ if (zlog_reload((char *)-1)) { zc_error("reach reload-conf-period but zlog_reload fail, zlog-chk-conf [file] see detail"); } return; } void hzlog(zlog_category_t *category, const char *file, size_t filelen, const char *func, size_t funclen, long line, int level, const void *buf, size_t buflen) { zlog_thread_t *a_thread; if (zlog_category_needless_level(category, level)) return; pthread_rwlock_rdlock(&zlog_env_lock); if (!zlog_env_is_init) { zc_error("never call zlog_init() or dzlog_init() before"); goto exit; } zlog_fetch_thread(a_thread, exit); zlog_event_set_hex(a_thread->event, category->name, category->name_len, file, filelen, func, funclen, line, level, buf, buflen); if (zlog_category_output(category, a_thread)) { zc_error("zlog_output fail, srcfile[%s], srcline[%ld]", file, line); goto exit; } if (zlog_env_conf->reload_conf_period && ++zlog_env_reload_conf_count > zlog_env_conf->reload_conf_period ) { /* under the protection of lock read env conf */ goto reload; } exit: pthread_rwlock_unlock(&zlog_env_lock); return; reload: pthread_rwlock_unlock(&zlog_env_lock); /* will be wrlock, so after unlock */ if (zlog_reload((char *)-1)) { zc_error("reach reload-conf-period but zlog_reload fail, zlog-chk-conf [file] see detail"); } return; } /*******************************************************************************/ /* for speed up, copy from vzlog */ void vdzlog(const char *file, size_t filelen, const char *func, size_t funclen, long line, int level, const char *format, va_list args) { zlog_thread_t *a_thread; if (zlog_category_needless_level(zlog_default_category, level)) return; pthread_rwlock_rdlock(&zlog_env_lock); if (!zlog_env_is_init) { zc_error("never call zlog_init() or dzlog_init() before"); goto exit; } /* that's the differnce, must judge default_category in lock */ if (!zlog_default_category) { zc_error("zlog_default_category is null," "dzlog_init() or dzlog_set_cateogry() is not called above"); goto exit; } zlog_fetch_thread(a_thread, exit); zlog_event_set_fmt(a_thread->event, zlog_default_category->name, zlog_default_category->name_len, file, filelen, func, funclen, line, level, format, args); if (zlog_category_output(zlog_default_category, a_thread)) { zc_error("zlog_output fail, srcfile[%s], srcline[%ld]", file, line); goto exit; } if (zlog_env_conf->reload_conf_period && ++zlog_env_reload_conf_count > zlog_env_conf->reload_conf_period ) { /* under the protection of lock read env conf */ goto reload; } exit: pthread_rwlock_unlock(&zlog_env_lock); return; reload: pthread_rwlock_unlock(&zlog_env_lock); /* will be wrlock, so after unlock */ if (zlog_reload((char *)-1)) { zc_error("reach reload-conf-period but zlog_reload fail, zlog-chk-conf [file] see detail"); } return; } void hdzlog(const char *file, size_t filelen, const char *func, size_t funclen, long line, int level, const void *buf, size_t buflen) { zlog_thread_t *a_thread; if (zlog_category_needless_level(zlog_default_category, level)) return; pthread_rwlock_rdlock(&zlog_env_lock); if (!zlog_env_is_init) { zc_error("never call zlog_init() or dzlog_init() before"); goto exit; } /* that's the differnce, must judge default_category in lock */ if (!zlog_default_category) { zc_error("zlog_default_category is null," "dzlog_init() or dzlog_set_cateogry() is not called above"); goto exit; } zlog_fetch_thread(a_thread, exit); zlog_event_set_hex(a_thread->event, zlog_default_category->name, zlog_default_category->name_len, file, filelen, func, funclen, line, level, buf, buflen); if (zlog_category_output(zlog_default_category, a_thread)) { zc_error("zlog_output fail, srcfile[%s], srcline[%ld]", file, line); goto exit; } if (zlog_env_conf->reload_conf_period && ++zlog_env_reload_conf_count > zlog_env_conf->reload_conf_period ) { /* under the protection of lock read env conf */ goto reload; } exit: pthread_rwlock_unlock(&zlog_env_lock); return; reload: pthread_rwlock_unlock(&zlog_env_lock); /* will be wrlock, so after unlock */ if (zlog_reload((char *)-1)) { zc_error("reach reload-conf-period but zlog_reload fail, zlog-chk-conf [file] see detail"); } return; } /*******************************************************************************/ void zlog(zlog_category_t * category, const char *file, size_t filelen, const char *func, size_t funclen, long line, const int level, const char *format, ...) { zlog_thread_t *a_thread; va_list args; if (category && zlog_category_needless_level(category, level)) return; pthread_rwlock_rdlock(&zlog_env_lock); if (!zlog_env_is_init) { zc_error("never call zlog_init() or dzlog_init() before"); goto exit; } zlog_fetch_thread(a_thread, exit); va_start(args, format); zlog_event_set_fmt(a_thread->event, category->name, category->name_len, file, filelen, func, funclen, line, level, format, args); if (zlog_category_output(category, a_thread)) { zc_error("zlog_output fail, srcfile[%s], srcline[%ld]", file, line); va_end(args); goto exit; } va_end(args); if (zlog_env_conf->reload_conf_period && ++zlog_env_reload_conf_count > zlog_env_conf->reload_conf_period ) { /* under the protection of lock read env conf */ goto reload; } exit: pthread_rwlock_unlock(&zlog_env_lock); return; reload: pthread_rwlock_unlock(&zlog_env_lock); /* will be wrlock, so after unlock */ if (zlog_reload((char *)-1)) { zc_error("reach reload-conf-period but zlog_reload fail, zlog-chk-conf [file] see detail"); } return; } /*******************************************************************************/ void dzlog(const char *file, size_t filelen, const char *func, size_t funclen, long line, int level, const char *format, ...) { zlog_thread_t *a_thread; va_list args; pthread_rwlock_rdlock(&zlog_env_lock); if (!zlog_env_is_init) { zc_error("never call zlog_init() or dzlog_init() before"); goto exit; } /* that's the differnce, must judge default_category in lock */ if (!zlog_default_category) { zc_error("zlog_default_category is null," "dzlog_init() or dzlog_set_cateogry() is not called above"); goto exit; } if (zlog_category_needless_level(zlog_default_category, level)) goto exit; zlog_fetch_thread(a_thread, exit); va_start(args, format); zlog_event_set_fmt(a_thread->event, zlog_default_category->name, zlog_default_category->name_len, file, filelen, func, funclen, line, level, format, args); if (zlog_category_output(zlog_default_category, a_thread)) { zc_error("zlog_output fail, srcfile[%s], srcline[%ld]", file, line); va_end(args); goto exit; } va_end(args); if (zlog_env_conf->reload_conf_period && ++zlog_env_reload_conf_count > zlog_env_conf->reload_conf_period ) { /* under the protection of lock read env conf */ goto reload; } exit: pthread_rwlock_unlock(&zlog_env_lock); return; reload: pthread_rwlock_unlock(&zlog_env_lock); /* will be wrlock, so after unlock */ if (zlog_reload((char *)-1)) { zc_error("reach reload-conf-period but zlog_reload fail, zlog-chk-conf [file] see detail"); } return; } /*******************************************************************************/ void zlog_profile(void) { int rc = 0; rc = pthread_rwlock_rdlock(&zlog_env_lock); if (rc) { zc_error("pthread_rwlock_wrlock fail, rc[%d]", rc); return; } zc_warn("------zlog_profile start------ "); zc_warn("is init:[%d]", zlog_env_is_init); zc_warn("init version:[%d]", zlog_env_init_version); zlog_conf_profile(zlog_env_conf, ZC_WARN); zlog_record_table_profile(zlog_env_records, ZC_WARN); zlog_category_table_profile(zlog_env_categories, ZC_WARN); if (zlog_default_category) { zc_warn("-default_category-"); zlog_category_profile(zlog_default_category, ZC_WARN); } zc_warn("------zlog_profile end------ "); rc = pthread_rwlock_unlock(&zlog_env_lock); if (rc) { zc_error("pthread_rwlock_unlock fail, rc=[%d]", rc); return; } return; } /*******************************************************************************/ int zlog_set_record(const char *rname, zlog_record_fn record_output) { int rc = 0; int rd = 0; zlog_rule_t *a_rule; zlog_record_t *a_record; int i = 0; zc_assert(rname, -1); zc_assert(record_output, -1); rd = pthread_rwlock_wrlock(&zlog_env_lock); if (rd) { zc_error("pthread_rwlock_rdlock fail, rd[%d]", rd); return -1; } if (!zlog_env_is_init) { zc_error("never call zlog_init() or dzlog_init() before"); goto zlog_set_record_exit; } a_record = zlog_record_new(rname, record_output); if (!a_record) { rc = -1; zc_error("zlog_record_new fail"); goto zlog_set_record_exit; } rc = zc_hashtable_put(zlog_env_records, a_record->name, a_record); if (rc) { zlog_record_del(a_record); zc_error("zc_hashtable_put fail"); goto zlog_set_record_exit; } zc_arraylist_foreach(zlog_env_conf->rules, i, a_rule) { zlog_rule_set_record(a_rule, zlog_env_records); } zlog_set_record_exit: rd = pthread_rwlock_unlock(&zlog_env_lock); if (rd) { zc_error("pthread_rwlock_unlock fail, rd=[%d]", rd); return -1; } return rc; } /*******************************************************************************/ int zlog_level_enabled(zlog_category_t *category, const int level) { return category && ((zlog_category_needless_level(category, level) == 0)); } const char *zlog_version(void) { return ZLOG_VERSION; } zlog-1.2.18/src/zlog.h000066400000000000000000000260271460570005000144750ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __zlog_h #define __zlog_h #ifdef __cplusplus extern "C" { #endif #include /* for va_list */ #include /* for size_t */ # if defined __GNUC__ # define ZLOG_CHECK_PRINTF(m,n) __attribute__((format(printf,m,n))) # else # define ZLOG_CHECK_PRINTF(m,n) # endif typedef struct zlog_category_s zlog_category_t; int zlog_init(const char *config); int zlog_init_from_string(const char *config_string); int zlog_reload(const char *config); int zlog_reload_from_string(const char *conf_string); void zlog_fini(void); void zlog_profile(void); zlog_category_t *zlog_get_category(const char *cname); int zlog_level_enabled(zlog_category_t *category, const int level); int zlog_put_mdc(const char *key, const char *value); char *zlog_get_mdc(const char *key); void zlog_remove_mdc(const char *key); void zlog_clean_mdc(void); int zlog_level_switch(zlog_category_t * category, int level); int zlog_level_enabled(zlog_category_t * category, int level); void zlog(zlog_category_t * category, const char *file, size_t filelen, const char *func, size_t funclen, long line, int level, const char *format, ...) ZLOG_CHECK_PRINTF(8,9); void vzlog(zlog_category_t * category, const char *file, size_t filelen, const char *func, size_t funclen, long line, int level, const char *format, va_list args); void hzlog(zlog_category_t * category, const char *file, size_t filelen, const char *func, size_t funclen, long line, int level, const void *buf, size_t buflen); int dzlog_init(const char *confpath, const char *cname); int dzlog_set_category(const char *cname); void dzlog(const char *file, size_t filelen, const char *func, size_t funclen, long line, int level, const char *format, ...) ZLOG_CHECK_PRINTF(7,8); void vdzlog(const char *file, size_t filelen, const char *func, size_t funclen, long line, int level, const char *format, va_list args); void hdzlog(const char *file, size_t filelen, const char *func, size_t funclen, long line, int level, const void *buf, size_t buflen); typedef struct zlog_msg_s { char *buf; size_t len; char *path; } zlog_msg_t; typedef int (*zlog_record_fn)(zlog_msg_t *msg); int zlog_set_record(const char *rname, zlog_record_fn record); const char *zlog_version(void); /******* useful macros, can be redefined at user's h file **********/ typedef enum { ZLOG_LEVEL_DEBUG = 20, ZLOG_LEVEL_INFO = 40, ZLOG_LEVEL_NOTICE = 60, ZLOG_LEVEL_WARN = 80, ZLOG_LEVEL_ERROR = 100, ZLOG_LEVEL_FATAL = 120 } zlog_level; #if !defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L # if defined __GNUC__ && __GNUC__ >= 2 # define __func__ __FUNCTION__ # else # define __func__ "" # endif #endif #if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L /* zlog macros */ #define zlog_fatal(cat, ...) \ zlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_FATAL, __VA_ARGS__) #define zlog_error(cat, ...) \ zlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_ERROR, __VA_ARGS__) #define zlog_warn(cat, ...) \ zlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_WARN, __VA_ARGS__) #define zlog_notice(cat, ...) \ zlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_NOTICE, __VA_ARGS__) #define zlog_info(cat, ...) \ zlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_INFO, __VA_ARGS__) #define zlog_debug(cat, ...) \ zlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_DEBUG, __VA_ARGS__) /* dzlog macros */ #define dzlog_fatal(...) \ dzlog(__FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_FATAL, __VA_ARGS__) #define dzlog_error(...) \ dzlog(__FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_ERROR, __VA_ARGS__) #define dzlog_warn(...) \ dzlog(__FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_WARN, __VA_ARGS__) #define dzlog_notice(...) \ dzlog(__FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_NOTICE, __VA_ARGS__) #define dzlog_info(...) \ dzlog(__FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_INFO, __VA_ARGS__) #define dzlog_debug(...) \ dzlog(__FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_DEBUG, __VA_ARGS__) #elif defined __GNUC__ /* zlog macros */ #define zlog_fatal(cat, format, args...) \ zlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_FATAL, format, ##args) #define zlog_error(cat, format, args...) \ zlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_ERROR, format, ##args) #define zlog_warn(cat, format, args...) \ zlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_WARN, format, ##args) #define zlog_notice(cat, format, args...) \ zlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_NOTICE, format, ##args) #define zlog_info(cat, format, args...) \ zlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_INFO, format, ##args) #define zlog_debug(cat, format, args...) \ zlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_DEBUG, format, ##args) /* dzlog macros */ #define dzlog_fatal(format, args...) \ dzlog(__FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_FATAL, format, ##args) #define dzlog_error(format, args...) \ dzlog(__FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_ERROR, format, ##args) #define dzlog_warn(format, args...) \ dzlog(__FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_WARN, format, ##args) #define dzlog_notice(format, args...) \ dzlog(__FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_NOTICE, format, ##args) #define dzlog_info(format, args...) \ dzlog(__FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_INFO, format, ##args) #define dzlog_debug(format, args...) \ dzlog(__FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_DEBUG, format, ##args) #endif /* vzlog macros */ #define vzlog_fatal(cat, format, args) \ vzlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_FATAL, format, args) #define vzlog_error(cat, format, args) \ vzlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_ERROR, format, args) #define vzlog_warn(cat, format, args) \ vzlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_WARN, format, args) #define vzlog_notice(cat, format, args) \ vzlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_NOTICE, format, args) #define vzlog_info(cat, format, args) \ vzlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_INFO, format, args) #define vzlog_debug(cat, format, args) \ vzlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_DEBUG, format, args) /* hzlog macros */ #define hzlog_fatal(cat, buf, buf_len) \ hzlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_FATAL, buf, buf_len) #define hzlog_error(cat, buf, buf_len) \ hzlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_ERROR, buf, buf_len) #define hzlog_warn(cat, buf, buf_len) \ hzlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_WARN, buf, buf_len) #define hzlog_notice(cat, buf, buf_len) \ hzlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_NOTICE, buf, buf_len) #define hzlog_info(cat, buf, buf_len) \ hzlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_INFO, buf, buf_len) #define hzlog_debug(cat, buf, buf_len) \ hzlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_DEBUG, buf, buf_len) /* vdzlog macros */ #define vdzlog_fatal(format, args) \ vdzlog(__FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_FATAL, format, args) #define vdzlog_error(format, args) \ vdzlog(__FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_ERROR, format, args) #define vdzlog_warn(format, args) \ vdzlog(__FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_WARN, format, args) #define vdzlog_notice(format, args) \ vdzlog(__FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_NOTICE, format, args) #define vdzlog_info(format, args) \ vdzlog(__FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_INFO, format, args) #define vdzlog_debug(format, args) \ vdzlog(__FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_DEBUG, format, args) /* hdzlog macros */ #define hdzlog_fatal(buf, buf_len) \ hdzlog(__FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_FATAL, buf, buf_len) #define hdzlog_error(buf, buf_len) \ hdzlog(__FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_ERROR, buf, buf_len) #define hdzlog_warn(buf, buf_len) \ hdzlog(__FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_WARN, buf, buf_len) #define hdzlog_notice(buf, buf_len) \ hdzlog(__FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_NOTICE, buf, buf_len) #define hdzlog_info(buf, buf_len) \ hdzlog(__FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_INFO, buf, buf_len) #define hdzlog_debug(buf, buf_len) \ hdzlog(__FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_DEBUG, buf, buf_len) /* enabled macros */ #define zlog_fatal_enabled(zc) zlog_level_enabled(zc, ZLOG_LEVEL_FATAL) #define zlog_error_enabled(zc) zlog_level_enabled(zc, ZLOG_LEVEL_ERROR) #define zlog_warn_enabled(zc) zlog_level_enabled(zc, ZLOG_LEVEL_WARN) #define zlog_notice_enabled(zc) zlog_level_enabled(zc, ZLOG_LEVEL_NOTICE) #define zlog_info_enabled(zc) zlog_level_enabled(zc, ZLOG_LEVEL_INFO) #define zlog_debug_enabled(zc) zlog_level_enabled(zc, ZLOG_LEVEL_DEBUG) #ifdef __cplusplus } #endif #endif zlog-1.2.18/src/zlog_win.c000066400000000000000000000032161460570005000153400ustar00rootroot00000000000000 #include #include #include #include #include #include "zlog_win.h" #include #include int gethostname_w(char *name, size_t len) { int rc = gethostname(name, len); DWORD newlen = len; if (rc != 0) { rc = GetComputerNameEx(ComputerNameDnsHostname, name, &newlen); if (rc == 0) { sprintf(name, "noname"); } } return 0; } #ifndef strcasecmp int strcasecmp (const char *sz1, const char *sz2) { return stricmp (sz1, sz2); } #endif #ifndef localtime_r struct tm *localtime_r(const time_t *timep, struct tm *result) { struct tm *ret = localtime(timep); if (ret) { memcpy(result, ret, sizeof(struct tm)); } return ret; } #endif int fsync (int fd) { HANDLE h = (HANDLE) _get_osfhandle (fd); DWORD err; if (h == INVALID_HANDLE_VALUE) { errno = EBADF; return -1; } if (!FlushFileBuffers (h)) { /* Translate some Windows errors into rough approximations of Unix * errors. MSDN is useless as usual - in this case it doesn't * document the full range of errors. */ err = GetLastError (); switch (err) { /* eg. Trying to fsync a tty. */ case ERROR_INVALID_HANDLE: errno = EINVAL; break; default: errno = EIO; } return -1; } return 0; } void setenv(const char *name, const char *value) { #ifdef HAVE_SETENV setenv(name, value, 1); #else int len = strlen(value)+1+strlen(value)+1; char *str = malloc(len); sprintf(str, "%s=%s", name, value); putenv(str); #endif } zlog-1.2.18/src/zlog_win.h000066400000000000000000000062731460570005000153530ustar00rootroot00000000000000#ifndef _ZLOG_WIN_H_ #define _ZLOG_WIN_H_ #include #ifndef localtime_r struct tm *localtime_r(const time_t *timep, struct tm *result); #endif #ifndef strcasecmp int strcasecmp (const char *sz1, const char *sz2); #endif int fsync (int fd); void setenv(const char *name, const char *value); int gethostname_w(char *name, size_t len); /* facility codes */ #define LOG_KERN (0<<3) /* kernel messages */ #define LOG_USER (1<<3) /* random user-level messages */ #define LOG_MAIL (2<<3) /* mail system */ #define LOG_DAEMON (3<<3) /* system daemons */ #define LOG_AUTH (4<<3) /* authorization messages */ #define LOG_SYSLOG (5<<3) /* messages generated internally by syslogd */ #define LOG_LPR (6<<3) /* line printer subsystem */ #define LOG_NEWS (7<<3) /* network news subsystem */ #define LOG_UUCP (8<<3) /* UUCP subsystem */ #define LOG_CRON (9<<3) /* clock daemon */ #define LOG_AUTHPRIV (10<<3) /* authorization messages (private) */ /* Facility #10 clashes in DEC UNIX, where */ /* it's defined as LOG_MEGASAFE for AdvFS */ /* event logging. */ #define LOG_FTP (11<<3) /* ftp daemon */ #define LOG_NTP (12<<3) /* NTP subsystem */ #define LOG_SECURITY (13<<3) /* security subsystems (firewalling, etc.) */ #define LOG_CONSOLE (14<<3) /* /dev/console output */ /* other codes through 15 reserved for system use */ #define LOG_LOCAL0 (16<<3) /* reserved for local use */ #define LOG_LOCAL1 (17<<3) /* reserved for local use */ #define LOG_LOCAL2 (18<<3) /* reserved for local use */ #define LOG_LOCAL3 (19<<3) /* reserved for local use */ #define LOG_LOCAL4 (20<<3) /* reserved for local use */ #define LOG_LOCAL5 (21<<3) /* reserved for local use */ #define LOG_LOCAL6 (22<<3) /* reserved for local use */ #define LOG_LOCAL7 (23<<3) /* reserved for local use */ /* * Option flags for openlog. * * LOG_ODELAY no longer does anything. * LOG_NDELAY is the inverse of what it used to be. */ #define LOG_PID 0x01 /* log the pid with each message */ #define LOG_CONS 0x02 /* log on the console if errors in sending */ #define LOG_ODELAY 0x04 /* delay open until first syslog() (default) */ #define LOG_NDELAY 0x08 /* don't delay open */ #define LOG_NOWAIT 0x10 /* don't wait for console forks: DEPRECATED */ #define LOG_PERROR 0x20 /* log to stderr as well */ /* * priorities/facilities are encoded into a single 32-bit quantity, where the * bottom 3 bits are the priority (0-7) and the top 28 bits are the facility * (0-big number). Both the priorities and the facilities map roughly * one-to-one to strings in the syslogd(8) source code. This mapping is * included in this file. * * priorities (these are ordered) */ #define LOG_EMERG 0 /* system is unusable */ #define LOG_ALERT 1 /* action must be taken immediately */ #define LOG_CRIT 2 /* critical conditions */ #define LOG_ERR 3 /* error conditions */ #define LOG_WARNING 4 /* warning conditions */ #define LOG_NOTICE 5 /* normal but significant condition */ #define LOG_INFO 6 /* informational */ #define LOG_DEBUG 7 /* debug-level messages */ #define LOG_PRIMASK 0x07 /* mask to extract priority part (internal) */ /* extract priority */ #define LOG_PRI(p) ((p) & LOG_PRIMASK) #define LOG_MAKEPRI(fac, pri) ((fac) | (pri)) #endifzlog-1.2.18/test/000077500000000000000000000000001460570005000135325ustar00rootroot00000000000000zlog-1.2.18/test/CMakeLists.txt000066400000000000000000000030341460570005000162720ustar00rootroot00000000000000 include_directories("${CMAKE_HOME_DIRECTORY}/src") aux_source_directory(. SRCS) if (WIN32) #message(STATUS ${SRCS}) list(REMOVE_ITEM SRCS ./test_press_syslog.c) list(REMOVE_ITEM SRCS ./test_syslog.c) list(REMOVE_ITEM SRCS ./test_press_write.c) list(REMOVE_ITEM SRCS ./test_press_write2.c) list(REMOVE_ITEM SRCS ./test_press_zlog.c) list(REMOVE_ITEM SRCS ./test_press_zlog2.c) #message(STATUS ${SRCS}) endif () set(not_auto_add_test test_hello test_bitmap test_hex test_leak test_press_write test_press_write2 test_press_zlog test_press_zlog2 test_press_syslog test_syslog test_longlog ) foreach (test_src ${SRCS}) string(REGEX MATCH "^.*/([^/]+)[.]c$" test_name ${test_src}) set(test_name ${CMAKE_MATCH_1}) message(STATUS "${test_name} ${test_src}") add_executable("${test_name}" "${test_src}") target_link_libraries(${test_name} zlog) list(FIND not_auto_add_test ${test_name} not_auto_test) if (not_auto_test EQUAL -1) add_test("${test_name}" "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${test_name}") endif () endforeach (test_src) add_test(test_hello "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test_hello" hello_output 3) add_test(test_longlog "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test_longlog" 2222) add_test(test_bitmap "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test_bitmap" 0xaa55 0x66) file(GLOB CONF_FILES . *.conf) file(COPY ${CONF_FILES} hello_output DESTINATION ${CMAKE_CURRENT_BINARY_DIR} ) zlog-1.2.18/test/fuzzers/000077500000000000000000000000001460570005000152425ustar00rootroot00000000000000zlog-1.2.18/test/fuzzers/zlog_init_fuzzer.c000066400000000000000000000010141460570005000210050ustar00rootroot00000000000000#include #include #include #include "zlog.h" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { char filename[256]; sprintf(filename, "/tmp/libfuzzer.%d", getpid()); FILE *fp = fopen(filename, "wb"); if (!fp) return 0; fwrite(data, size, 1, fp); fclose(fp); int rc = zlog_init(filename); if (rc == 0) { zlog_fini(); } unlink(filename); return 0; } zlog-1.2.18/test/hello_output000066400000000000000000000000001460570005000161660ustar00rootroot00000000000000zlog-1.2.18/test/makefile000077500000000000000000000012351460570005000152360ustar00rootroot00000000000000exe = \ test_tmp \ test_longlog \ test_buf \ test_bitmap \ test_conf \ test_conf2 \ test_hashtable \ test_hello \ test_hex \ test_init \ test_level \ test_leak \ test_mdc \ test_multithread \ test_record \ test_pipe \ test_press_zlog \ test_press_zlog2 \ test_press_write \ test_press_write2 \ test_press_syslog \ test_syslog \ test_default \ test_profile \ test_category \ test_prompt \ test_enabled all : $(exe) $(exe) : %:%.o gcc -O2 -g -o $@ $^ -L../src -lzlog -lpthread -Wl,-rpath ../src .c.o : gcc -O2 -g -Wall -D_GNU_SOURCE -o $@ -c $< -I. -I../src clean : rm -f press.log* *.o $(exe) .PHONY : clean all zlog-1.2.18/test/test_bitmap.c000066400000000000000000000023751460570005000162200ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include "zlog.h" int main(int argc, char** argv) { unsigned char aa[32]; int i, j; if (argc != 3) { printf("useage: test_bitmap i j\n"); exit(1); } dzlog_init(NULL, "AA"); i = atoi(argv[1]); j = atoi(argv[2]); memset(aa, 0x00, sizeof(aa)); /* 32 byte, 256 bit * [11111..1100...00] * i */ aa[i/8] |= ~(0xFF << (8 - i % 8)); memset(aa + i/8 + 1, 0xFF, sizeof(aa) - i/8 - 1); hdzlog_info(aa, sizeof(aa)); dzlog_info("%0x", aa[j/8]); dzlog_info("%0x", aa[j/8] >> 6); /* see j of bits fits */ dzlog_info("%0x", ~((aa[j/8] >> (7 - j % 8)) & 0x01) ); zlog_fini(); return 0; } zlog-1.2.18/test/test_buf.c000066400000000000000000000057221460570005000155170ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "zc_defs.h" #include "buf.h" int main(int argc, char** argv) { zlog_buf_t *a_buf; char *aa; a_buf = zlog_buf_new(10, 20, "ABC"); if (!a_buf) { zc_error("zlog_buf_new fail"); return -1; } aa = "123456789"; zlog_buf_append(a_buf, aa, strlen(aa)); zc_error("a_buf->start[%s]", a_buf->start); fwrite(a_buf->start, zlog_buf_len(a_buf), 1, stdout); zc_error("------------"); aa = "0"; zlog_buf_append(a_buf, aa, strlen(aa)); zc_error("a_buf->start[%s]", a_buf->start); zc_error("------------"); aa = "12345"; zlog_buf_append(a_buf, aa, strlen(aa)); zc_error("a_buf->start[%s]", a_buf->start); zc_error("------------"); aa = "6789"; zlog_buf_append(a_buf, aa, strlen(aa)); zc_error("a_buf->start[%s]", a_buf->start); zc_error("------------"); aa = "0"; zlog_buf_append(a_buf, aa, strlen(aa)); zc_error("a_buf->start[%s]", a_buf->start); zc_error("------------"); aa = "22345"; zlog_buf_append(a_buf, aa, strlen(aa)); zc_error("a_buf->start[%s]", a_buf->start); zc_error("------------"); aa = "abc"; int i,j; for (i = 0; i <= 5; i++) { for (j = 0; j <= 5; j++) { zlog_buf_restart(a_buf); zc_error("left[1],max[%d],min[%d]", i, j); zlog_buf_adjust_append(a_buf, aa, strlen(aa), 1, 0, i, j); zc_error("a_buf->start[%s]", a_buf->start); zc_error("-----"); zlog_buf_restart(a_buf); zc_error("left[0],max[%d],min[%d]", i, j); zlog_buf_adjust_append(a_buf, aa, strlen(aa), 0, 0, i, j); zc_error("a_buf->start[%s]", a_buf->start); zc_error("------------"); } } aa = "1234567890"; zc_error("left[0],max[%d],min[%d]", 15, 5); zlog_buf_adjust_append(a_buf, aa, strlen(aa), 0, 0, 15, 5); zc_error("a_buf->start[%s]", a_buf->start); zc_error("------------"); aa = "1234567890"; zlog_buf_restart(a_buf); zc_error("left[0],max[%d],min[%d]", 25, 5); zlog_buf_adjust_append(a_buf, aa, strlen(aa), 1, 0, 25, 5); zc_error("a_buf->start[%s]", a_buf->start); zc_error("------------"); zlog_buf_restart(a_buf); zc_error("left[0],max[%d],min[%d]", 19, 5); zlog_buf_adjust_append(a_buf, aa, strlen(aa), 0, 0, 19, 5); zc_error("a_buf->start[%s]", a_buf->start); zc_error("------------"); zlog_buf_restart(a_buf); zc_error("left[0],max[%d],min[%d]", 20, 5); zlog_buf_adjust_append(a_buf, aa, strlen(aa), 0, 0, 20, 5); zc_error("a_buf->start[%s]", a_buf->start); zc_error("------------"); zlog_buf_del(a_buf); return 0; } zlog-1.2.18/test/test_category.c000066400000000000000000000021361460570005000165540ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "zlog.h" int main(int argc, char** argv) { int rc; zlog_category_t *zc; rc = zlog_init("test_category.conf"); if (rc) { printf("init failed\n"); return -1; } zc = zlog_get_category("my_cat"); if (!zc) { printf("get cat fail\n"); zlog_fini(); return -2; } zlog_debug(zc, "hello, zlog - debug"); zc = zlog_get_category("my-cat"); if (!zc) { printf("get cat fail\n"); zlog_fini(); return -2; } zlog_info(zc, "hello, zlog - info"); zlog_fini(); return 0; } zlog-1.2.18/test/test_category.conf000066400000000000000000000001261460570005000172540ustar00rootroot00000000000000[global] default format = "%V %v %m%n" [rules] my_cat.* >stdout; my-cat.* >stdout; zlog-1.2.18/test/test_conf.c000066400000000000000000000021351460570005000156630ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include "zlog.h" int main(int argc, char** argv) { int rc; zlog_category_t *zc; rc = zlog_init("test_conf.conf"); if (rc) { printf("init failed, try zlog-chk-conf test_conf.conf for more detail\n"); return -1; } zc = zlog_get_category("my_cat"); if (!zc) { printf("get cat fail\n"); zlog_fini(); return -2; } zlog_info(zc, "hello, zlog"); zlog_fini(); printf("log end\n"); return 0; } zlog-1.2.18/test/test_conf.conf000066400000000000000000000021651460570005000163710ustar00rootroot00000000000000[global] strict init = true buffer min= 1024 buffer max= 2MB rotate lock file = /tmp/zlog.lock default format = "defalut - %d(%F %X.%ms) %-6V (%c:%F:%U:%L) - %m%n" [formats] null = "%n" print = "print - [%-10.3d(%F)]%n" date = "date start%n%d(%a--Wed)%n%d(%A--Wednesday)%n%d(%b--Mar)%n%d(%B--March)%n%d(%c--WedMar211:45:262011)%n%d(%C--20)%n%d(%d--02)%n%d(%D--03/02/11)%n%d(%e--2)%n%d(%F--2011-03-02)%n%d(%g--11)%n%d(%G--2011)%n%d(%h--Mar)%n%d(%H--11)%n%d(%I--11)%n%d(%j--061)%n%d(%k-k)%n%d(%l-l)%n%d(%ms--500)%n%d(%m--03)%n%d(%M--45)%n%d(%us--500730)%n%d(%p--AM)%n%d(%r--11:45:26AM)%n%d(%R--11:45)%n%d(%s--epoch)%n%d(%S--26)%n%d(%t--)%n%d(%T--11:45:26)%n%d(%u--3)%n%d(%U--09)%n%d(%V--09)%n%d(%w--3)%n%d(%W--09)%n%d(%x--03/02/11)%n%d(%X--11:45:26)%n%d(%y--11)%n%d(%Y--2011)%n%d(%z--+0800)%n%d(%Z--CST)%n%d(%%--%)%n%d(%J--%J)%ndate end%n" simple = "simple - %m%n" text = "text - text%n" ms = "ms - %d(%a--Wed)[%d(%ms)]%n" msus = "msus - %d(%ms,%us,%ms,%us)%n" [rules] *.* >stderr; *.* >stderr; null *.* >stderr; print *.* >stderr; date *.* >stderr; simple *.* >stderr; text *.* >stderr; ms *.* >stderr; msus zlog-1.2.18/test/test_conf2.c000066400000000000000000000022331460570005000157440ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include "zlog.h" #include "test_conf2.conf.h" int main(int argc, char** argv) { int rc; zlog_category_t *zc; rc = zlog_init(test_conf2_conf); if (rc) { printf("init failed, save [] in a config file and try zlog-chk-conf for more detail [%s]\n", test_conf2_conf); return -1; } zc = zlog_get_category("my_cat"); if (!zc) { printf("get cat fail\n"); zlog_fini(); return -2; } zlog_info(zc, "hello, zlog"); zlog_fini(); printf("log end\n"); return 0; } zlog-1.2.18/test/test_conf2.conf.h000066400000000000000000000022741460570005000167020ustar00rootroot00000000000000#define test_conf2_conf "[global]\nstrict init = true\nbuffer min= 1024\nbuffer max= 2MB\nrotate lock file = /tmp/zlog.lock\ndefault format = \"defalut - %d(%F %X.%ms) %-6V (%c:%F:%U:%L) - %m%n\"\n\n[formats]\nnull = \"%n\"\nprint = \"print - [%-10.3d(%F)]%n\"\n\ndate = \"date start%n%d(%a--Wed)%n%d(%A--Wednesday)%n%d(%b--Mar)%n%d(%B--March)%n%d(%c--WedMar211:45:262011)%n%d(%C--20)%n%d(%d--02)%n%d(%D--03/02/11)%n%d(%e--2)%n%d(%F--2011-03-02)%n%d(%g--11)%n%d(%G--2011)%n%d(%h--Mar)%n%d(%H--11)%n%d(%I--11)%n%d(%j--061)%n%d(%k-k)%n%d(%l-l)%n%d(%ms--500)%n%d(%m--03)%n%d(%M--45)%n%d(%us--500730)%n%d(%p--AM)%n%d(%r--11:45:26AM)%n%d(%R--11:45)%n%d(%s--epoch)%n%d(%S--26)%n%d(%t--)%n%d(%T--11:45:26)%n%d(%u--3)%n%d(%U--09)%n%d(%V--09)%n%d(%w--3)%n%d(%W--09)%n%d(%x--03/02/11)%n%d(%X--11:45:26)%n%d(%y--11)%n%d(%Y--2011)%n%d(%z--+0800)%n%d(%Z--CST)%n%d(%%--%)%n%d(%J--%J)%ndate end%n\"\n\nsimple = \"simple - %m%n\"\n\ntext = \"text - text%n\"\n\nms = \"ms - %d(%a--Wed)[%d(%ms)]%n\"\n\nmsus = \"msus - %d(%ms,%us,%ms,%us)%n\"\n\n[rules]\n*.* >stderr;\n*.* >stderr; null\n*.* >stderr; print\n*.* >stderr; date\n*.* >stderr; simple\n*.* >stderr; text\n*.* >stderr; ms\n*.* >stderr; msus" zlog-1.2.18/test/test_default.c000066400000000000000000000015111460570005000163570ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "zlog.h" int main(int argc, char** argv) { int rc; rc = dzlog_init("test_default.conf", "my_cat"); if (rc) { printf("init failed\n"); return -1; } dzlog_info("hello, zlog"); zlog_fini(); return 0; } zlog-1.2.18/test/test_default.conf000066400000000000000000000000341460570005000170610ustar00rootroot00000000000000[rules] my_cat.* >stdout zlog-1.2.18/test/test_enabled.c000066400000000000000000000024131460570005000163270ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "test_enabled.h" int main(int argc, char** argv) { int rc; zlog_category_t *zc; rc = zlog_init("test_enabled.conf"); if (rc) { printf("init failed\n"); return -1; } zc = zlog_get_category("my_cat"); if (!zc) { printf("get cat fail\n"); zlog_fini(); return -2; } if (zlog_trace_enabled(zc)) { /* do something heavy to collect data */ zlog_trace(zc, "hello, zlog - trace"); } if (zlog_debug_enabled(zc)) { /* do something heavy to collect data */ zlog_debug(zc, "hello, zlog - debug"); } if (zlog_info_enabled(zc)) { /* do something heavy to collect data */ zlog_info(zc, "hello, zlog - info"); } zlog_fini(); return 0; } zlog-1.2.18/test/test_enabled.conf000066400000000000000000000001461460570005000170330ustar00rootroot00000000000000[global] default format = "%V %v %m%n" [levels] TRACE = 30, LOG_DEBUG [rules] my_cat.TRACE >stdout; zlog-1.2.18/test/test_enabled.h000066400000000000000000000022151460570005000163340ustar00rootroot00000000000000/* * This file is part of the zlog Library. * * Copyright (C) 2018 by Teracom Telemática S/A * * The zlog Library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * The zlog Library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the zlog Library. If not, see . */ #ifndef __test_level_h #define __test_level_h #include "zlog.h" enum { ZLOG_LEVEL_TRACE = 30, /* must equals conf file setting */ }; #define zlog_trace(cat, format, args...) \ zlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_TRACE, format, ##args) #define zlog_trace_enabled(cat) zlog_level_enabled(cat, ZLOG_LEVEL_TRACE) #endif zlog-1.2.18/test/test_hashtable.c000066400000000000000000000031001460570005000166620ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include "zc_profile.c" #include "zc_hashtable.h" #include "zc_hashtable.c" void myfree(void *kv) { } int main(void) { zc_hashtable_t *a_table; zc_hashtable_entry_t *a_entry; a_table = zc_hashtable_new(20, zc_hashtable_str_hash, zc_hashtable_str_equal, myfree, myfree); zc_hashtable_put(a_table, "aaa", "bnbb"); zc_hashtable_put(a_table, "bbb", "bnbb"); zc_hashtable_put(a_table, "ccc", "bnbb"); zc_hashtable_put(a_table, "aaa", "123"); zc_hashtable_foreach(a_table, a_entry) { printf("k[%s],v[%s]\n", (char*)a_entry->key, (char*)a_entry->value); } printf("getv[%s]\n", (char*)zc_hashtable_get(a_table, "ccc")); zc_hashtable_remove(a_table, "ccc"); zc_hashtable_foreach(a_table, a_entry) { printf("k[%s],v[%s]\n", (char*)a_entry->key, (char*)a_entry->value); } zc_hashtable_remove(a_table, NULL); zc_hashtable_del(NULL); zc_hashtable_del(a_table); return 0; } zlog-1.2.18/test/test_hello.c000066400000000000000000000017001460570005000160360ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "zlog.h" int main(int argc, char** argv) { int rc; zlog_category_t *zc; rc = zlog_init("test_hello.conf"); if (rc) { printf("init failed\n"); return -1; } zc = zlog_get_category("my_cat"); if (!zc) { printf("get cat fail\n"); zlog_fini(); return -2; } zlog_info(zc, "hello, zlog"); zlog_fini(); return 0; } zlog-1.2.18/test/test_hello.conf000066400000000000000000000002071460570005000165420ustar00rootroot00000000000000[formats] simple = "%d.%ms %m%n" simple2 = "%d.%us %m%n" [rules] my_cat.* >stderr; my_cat.* >stdout;simple my_cat.* >stdout;simple2 zlog-1.2.18/test/test_hex.c000066400000000000000000000044201460570005000155210ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include "zlog.h" #include "stdlib.h" static int ReadTotalFile( FILE * fp , char ** ptr , long * len ) { long fileLen ; int nret ; char * pStart ; *ptr = NULL; nret = fseek( fp , 0L , SEEK_END ); if( nret ) { return -1; } fileLen = ftell( fp ); if( fileLen < 0 ) { return -2; } if( ( pStart = calloc(1, fileLen+1) ) == NULL ) { return -3; } nret = fseek( fp , 0L , SEEK_SET ); if( nret ) { free( pStart ); return -4; } nret = fread( pStart , fileLen , 1 , fp ); if( ferror( fp ) ) { free( pStart ); return -5; } *ptr = pStart; *len = fileLen; return 0; } int main(int argc, char** argv) { int rc; FILE *fp; char *dmp; long dmp_len = 0; int ntimes; if (argc != 3) { printf("useage: test_hex [file] [ntimes]\n"); exit(1); } fp = fopen(argv[1], "r"); if (!fp) { printf("fopen[%s] fail\n", argv[1]); exit(1); } ntimes = atoi(argv[2]); zlog_category_t *zc; rc = zlog_init("test_hex.conf"); if (rc) { printf("init failed\n"); return -1; } zc = zlog_get_category("my_cat"); if (!zc) { printf("get category failed\n"); zlog_fini(); return -2; } rc = ReadTotalFile(fp, &dmp, &dmp_len); while(ntimes--) hzlog_debug(zc, dmp, dmp_len); fclose(fp); free(dmp); zlog_fini(); printf("hex log end\n"); return 0; } zlog-1.2.18/test/test_hex.conf000066400000000000000000000000301460570005000162150ustar00rootroot00000000000000[rules] *.* "hex.log"; zlog-1.2.18/test/test_init.2.conf000066400000000000000000000002401460570005000165370ustar00rootroot00000000000000[global] buffer min = 2048 buffer max = 4096 default format = "%V %m%n" [levels] TEST = 40, LOG_INFO [formats] simple = "%m%n" [rules] my_cat.* >stderr; zlog-1.2.18/test/test_init.c000066400000000000000000000025601460570005000157030ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include "zlog.h" int main(int argc, char** argv) { int rc; zlog_category_t *zc; rc = zlog_init("test_init.conf"); if (rc) { printf("init fail"); return -2; } zc = zlog_get_category("my_cat"); if (!zc) { printf("zlog_get_category fail\n"); zlog_fini(); return -1; } zlog_info(zc, "before update"); sleep(1); rc = zlog_reload("test_init.2.conf"); if (rc) { printf("update fail\n"); } zlog_info(zc, "after update"); zlog_profile(); zlog_fini(); sleep(1); zlog_init("test_init.conf"); zc = zlog_get_category("my_cat"); if (!zc) { printf("zlog_get_category fail\n"); zlog_fini(); return -1; } zlog_info(zc, "init again"); zlog_fini(); return 0; } zlog-1.2.18/test/test_init.conf000066400000000000000000000002061460570005000164010ustar00rootroot00000000000000[ global ] strict init = true buffer min = 1024 buffer max = 2MB rotate lock file= /tmp/zlog.lock [ rules ] my_cat.* >stderr; zlog-1.2.18/test/test_leak.2.conf000066400000000000000000000001561460570005000165160ustar00rootroot00000000000000[global] strict init = true buffer min = 100 buffer max = 200 default format = "%m%n" [rules ] *.* >stdout; zlog-1.2.18/test/test_leak.c000066400000000000000000000025571460570005000156620ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include "zlog.h" int main(int argc, char** argv) { int rc; int k; int i; if (argc != 2) { printf("test_leak ntime\n"); return -1; } rc = zlog_init("test_leak.conf"); k = atoi(argv[1]); while (k-- > 0) { i = rand(); switch (i % 4) { case 0: rc = dzlog_init("test_leak.conf", "xxx"); dzlog_info("init, rc=[%d]", rc); break; case 1: rc = zlog_reload(NULL); dzlog_info("reload null, rc=[%d]", rc); break; case 2: rc = zlog_reload("test_leak.2.conf"); dzlog_info("reload 2, rc=[%d]", rc); break; case 3: zlog_fini(); printf("fini\n"); // printf("zlog_finish\tj=[%d], rc=[%d]\n", j, rc); break; } } zlog_fini(); return 0; } zlog-1.2.18/test/test_leak.conf000066400000000000000000000003221460570005000163510ustar00rootroot00000000000000[global] strict init = true buffer min = 2048 buffer max = 4096 rotate lock file = /tmp/zlog.lock default format = "%d(%F %T).%ms %-6V (%c:%F:%L) - %m%n" [ levels ] TEST = 40, LOG_INFO [rules ] *.* >stderr; zlog-1.2.18/test/test_level.c000066400000000000000000000020351460570005000160440ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "test_level.h" int main(int argc, char** argv) { int rc; zlog_category_t *zc; rc = zlog_init("test_level.conf"); if (rc) { printf("init failed\n"); return -1; } zc = zlog_get_category("my_cat"); if (!zc) { printf("get cat fail\n"); zlog_fini(); return -2; } zlog_trace(zc, "hello, zlog - trace"); zlog_debug(zc, "hello, zlog - debug"); zlog_info(zc, "hello, zlog - info"); zlog_fini(); return 0; } zlog-1.2.18/test/test_level.conf000066400000000000000000000001461460570005000165500ustar00rootroot00000000000000[global] default format = "%V %v %m%n" [levels] TRACE = 30, LOG_DEBUG [rules] my_cat.TRACE >stdout; zlog-1.2.18/test/test_level.h000066400000000000000000000021211460570005000160450ustar00rootroot00000000000000/* * This file is part of the zlog Library. * * Copyright (C) 2011 by Hardy Simpson * * The zlog Library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * The zlog Library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the zlog Library. If not, see . */ #ifndef __test_level_h #define __test_level_h #include "zlog.h" enum { ZLOG_LEVEL_TRACE = 30, /* must equals conf file setting */ }; #define zlog_trace(cat, format, args...) \ zlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_TRACE, format, ##args) #endif zlog-1.2.18/test/test_longlog.c000066400000000000000000000027751460570005000164110ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "zlog.h" #include #include #define str "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" #define str2 str str #define str4 str2 str2 #define str8 str4 str4 #define str16 str8 str8 #define str32 str16 str16 #define str64 str32 str32 int main(int argc, char** argv) { int i, k; int rc; zlog_category_t *zc; if (argc != 2) { printf("useage: test_longlog [count]\n"); exit(1); } rc = zlog_init("test_longlog.conf"); if (rc) { printf("init failed\n"); return -1; } zc = zlog_get_category("my_cat"); if (!zc) { printf("get cat fail\n"); zlog_fini(); return -2; } k = atoi(argv[1]); while (k-- > 0) { i = rand(); switch (i % 3) { case 0: zlog_info(zc, str32); break; case 1: zlog_info(zc, str64); break; case 2: zlog_info(zc, str16); break; } } zlog_fini(); return 0; } zlog-1.2.18/test/test_longlog.conf000066400000000000000000000002721460570005000171020ustar00rootroot00000000000000[global] strict init = true #buffer min = 1024 #buffer max = 0 rotate lock file = /tmp/zlog.lock [formats] simple = "%d %V %m%n" simple2 = "%d %V %m%n" [rules ] *.* "test_longlog.log" zlog-1.2.18/test/test_mdc.c000066400000000000000000000022341460570005000155010ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include "zlog.h" int main(int argc, char** argv) { int rc; zlog_category_t *zc; rc = zlog_init("test_mdc.conf"); if (rc) { printf("init failed\n"); return -1; } zc = zlog_get_category("my_cat"); if (!zc) { printf("get cat fail\n"); zlog_fini(); return -2; } zlog_info(zc, "1.hello, zlog"); zlog_put_mdc("myname", "Zhang"); zlog_info(zc, "2.hello, zlog"); zlog_put_mdc("myname", "Li"); zlog_info(zc, "3.hello, zlog"); zlog_fini(); return 0; } zlog-1.2.18/test/test_mdc.conf000066400000000000000000000001531460570005000162020ustar00rootroot00000000000000[formats] mdc_format= "%d(%F %X.%ms) %-6V (%c:%F:%L) [%M(myname)] - %m%n" [rules] *.* >stdout; mdc_format zlog-1.2.18/test/test_multithread.c000066400000000000000000000125151460570005000172630ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include "zlog.h" #define CONFIG "test_multithread.conf" #define NB_THREADS 200 #define THREAD_LOOP_DELAY 10000 /* 0.01" */ #define RELOAD_DELAY 10 enum { ZLOG_LEVEL_TRACE = 10, ZLOG_LEVEL_SECURITY = 150, /* must equals conf file setting */ }; #define zlog_trace(cat, format, args...) \ zlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_TRACE, format, ##args) #define zlog_security(cat, format, args...) \ zlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \ ZLOG_LEVEL_SECURITY, format, ##args) struct thread_info { /* Used as argument to thread_start() */ pthread_t thread_id; /* ID returned by pthread_create() */ int thread_num; /* Application-defined thread # */ zlog_category_t *zc; /* The logger category struc address; (All threads will use the same category, so he same address) */ long long int loop; /* Counter incremented to check the thread's health */ }; struct thread_info *tinfo; void intercept(int sig) { int i; printf("\nIntercept signal %d\n\n", sig); signal (sig, SIG_DFL); raise (sig); printf("You can import datas below in a spreadsheat and check if any thread stopped increment the Loop counter during the test.\n\n"); printf("Thread;Loop\n"); for (i=0; izc = zlog_get_category("thrd"); if (!tinfo->zc) { printf("get thrd %d cat fail\n", tinfo->thread_num); } else { while(1) { usleep(THREAD_LOOP_DELAY); zlog_info(tinfo->zc, "%d;%lld", tinfo->thread_num, tinfo->loop++); } } return NULL; } int main(int argc, char** argv) { int rc; zlog_category_t *zc; zlog_category_t *mc; zlog_category_t *hl; int i = 0; struct stat stat_0, stat_1; /* Create the logging directory if not yet ceated */ mkdir("./test_multithread-logs", 0777); if (stat(CONFIG, &stat_0)) { printf("Configuration file not found\n"); return -1; } rc = zlog_init(CONFIG); if (rc) { printf("main init failed\n"); return -2; } zc = zlog_get_category("main"); if (!zc) { printf("main get cat fail\n"); zlog_fini(); return -3; } mc = zlog_get_category("clsn"); if (!mc) { printf("clsn get cat fail\n"); zlog_fini(); return -3; } hl = zlog_get_category("high"); if (!hl) { printf("high get cat fail\n"); zlog_fini(); return -3; } /* Interrupt (ANSI). */ if (signal(SIGINT, intercept) == SIG_IGN ) { zlog_fatal(zc, "Can't caught the signal SIGINT, Interrupt (ANSI)"); signal(SIGINT, SIG_IGN ); return -4; } // start threads tinfo = calloc(NB_THREADS, sizeof(struct thread_info)); for (i=0; i 0) reload = (i % RELOAD_DELAY == 0); if (reload) { zlog_info(zc, "Will reload configuration..."); rc = zlog_reload(CONFIG); if (rc) { printf("main init failed\n"); return -6; } zlog_info(zc, "Configuration reloaded :)"); stat(CONFIG, &stat_0); } } exit(EXIT_SUCCESS); } zlog-1.2.18/test/test_multithread.conf000066400000000000000000000010111460570005000177530ustar00rootroot00000000000000[levels] TRACE = 10, LOG_DEBUG SECURITY = 150, LOG_ALERT [formats] simple = "%d(%m%d%H%M%S).%us %-6c %-10V [%06k] %m%n" security = "%d(%m%d%H%M%S).%us %-6c [%-8V] %m%n" developer = "%d(%m%d%H%M%S).%us %-6c %-10V [%t] %F(%L)/%U() - %m%n" csv = "%d.%ms;%m%n [%08t] [%T] [%06k] " [rules] clsn.ERROR >stdout; developer high.ERROR >stdout; security main.* >stdout; simple main.* "./test_multithread-logs.txt"; simple thrd.* "./test_multithread-logs/threadslog.csv", 20KB * 30 ~ "./test_multithread-logs/threadslog#r.csv"; csv zlog-1.2.18/test/test_pipe.c000066400000000000000000000016741460570005000157020ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "zlog.h" int main(int argc, char** argv) { int rc; zlog_category_t *zc; rc = zlog_init("test_pipe.conf"); if (rc) { printf("init failed\n"); return -1; } zc = zlog_get_category("my_cat"); if (!zc) { printf("get cat fail\n"); zlog_fini(); return -2; } zlog_info(zc, "hello, zlog"); zlog_fini(); return 0; } zlog-1.2.18/test/test_pipe.conf000066400000000000000000000001251460570005000163730ustar00rootroot00000000000000[formats] simple = "%m%n" [rules] my_cat.* |/usr/bin/cronolog test_pipe_%Y%m%d.log; zlog-1.2.18/test/test_press_syslog.c000066400000000000000000000025201460570005000174700ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include int work(long loop_count) { while(loop_count-- > 0) { syslog(LOG_INFO, "loglog"); } return 0; } int test(long process_count, long loop_count) { long i; pid_t pid; for (i = 0; i < process_count; i++) { pid = fork(); if (pid < 0) { printf("fork fail\n"); } else if(pid == 0) { work(loop_count); return 0; } } for (i = 0; i < process_count; i++) { pid = wait(NULL); } return 0; } int main(int argc, char** argv) { if (argc != 3) { fprintf(stderr, "test nprocess nloop"); exit(1); } openlog(NULL, LOG_NDELAY|LOG_NOWAIT|LOG_PID, LOG_LOCAL0); test(atol(argv[1]), atol(argv[2])); return 0; } zlog-1.2.18/test/test_press_write.c000066400000000000000000000037161460570005000173120ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include "zlog.h" int fd; static long loop_count; void * work(void *ptr) { long j = loop_count; static char aa[] = "2012-06-14 20:30:38.481187 INFO 24536:140716226213632:test_press_zlog.c:36 loglog\n"; while(j-- > 0) { // fprintf(fp, "2012-05-16 17:24:58.282603 INFO 22471:test_press_zlog.c:33 loglog\n"); // fwrite(aa, sizeof(aa)-1, 1, fp); write(fd, aa, sizeof(aa)-1); } return 0; } int test(long process_count, long thread_count) { long i; pid_t pid; long j; for (i = 0; i < process_count; i++) { pid = fork(); if (pid < 0) { printf("fork fail\n"); } else if(pid == 0) { pthread_t tid[thread_count]; for (j = 0; j < thread_count; j++) { pthread_create(&(tid[j]), NULL, work, NULL); } for (j = 0; j < thread_count; j++) { pthread_join(tid[j], NULL); } return 0; } } for (i = 0; i < process_count; i++) { pid = wait(NULL); } return 0; } int main(int argc, char** argv) { if (argc != 4) { fprintf(stderr, "test nprocess nthreads nloop\n"); exit(1); } fd = open("press.log", O_CREAT | O_WRONLY | O_APPEND, 0644); //fp = fdopen(fd, "a"); loop_count = atol(argv[3]); test(atol(argv[1]), atol(argv[2])); //fclose(fp); close(fd); return 0; } zlog-1.2.18/test/test_press_write2.c000066400000000000000000000036501460570005000173710ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include "zlog.h" static long loop_count; void * work(void *ptr) { long j = loop_count; static char log[] = "2012-06-14 20:30:38.481187 INFO 24536:140716226213632:test_press_zlog.c:36 loglog\n"; char file[20]; sprintf(file, "press.%ld.log", (long)ptr); int fd; fd = open(file, O_CREAT | O_WRONLY | O_APPEND , 0644); //FILE *fp; while(j-- > 0) { write(fd, log, sizeof(log)-1); //fwrite(log, sizeof(log)-1, 1, fp); } //fclose(fp); close(fd); return 0; } int test(long process_count, long thread_count) { long i; pid_t pid; long j; for (i = 0; i < process_count; i++) { pid = fork(); if (pid < 0) { printf("fork fail\n"); } else if(pid == 0) { pthread_t tid[thread_count]; for (j = 0; j < thread_count; j++) { pthread_create(&(tid[j]), NULL, work, (void*)j); } for (j = 0; j < thread_count; j++) { pthread_join(tid[j], NULL); } return 0; } } for (i = 0; i < process_count; i++) { pid = wait(NULL); } return 0; } int main(int argc, char** argv) { if (argc != 4) { fprintf(stderr, "test nprocess nthreads nloop\n"); exit(1); } loop_count = atol(argv[3]); test(atol(argv[1]), atol(argv[2])); return 0; } zlog-1.2.18/test/test_press_zlog.c000066400000000000000000000034621460570005000171310ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include "zlog.h" static zlog_category_t *zc; static long loop_count; void * work(void *ptr) { long j = loop_count; while(j-- > 0) { zlog_info(zc, "loglog"); } return 0; } int test(long process_count, long thread_count) { long i; pid_t pid; long j; for (i = 0; i < process_count; i++) { pid = fork(); if (pid < 0) { printf("fork fail\n"); } else if(pid == 0) { pthread_t tid[thread_count]; for (j = 0; j < thread_count; j++) { pthread_create(&(tid[j]), NULL, work, NULL); } for (j = 0; j < thread_count; j++) { pthread_join(tid[j], NULL); } return 0; } } for (i = 0; i < process_count; i++) { pid = wait(NULL); } return 0; } int main(int argc, char** argv) { int rc; if (argc != 4) { fprintf(stderr, "test nprocess nthreads nloop\n"); exit(1); } rc = zlog_init("test_press_zlog.conf"); if (rc) { printf("init failed\n"); return 2; } zc = zlog_get_category("my_cat"); if (!zc) { printf("get cat failed\n"); zlog_fini(); return 3; } loop_count = atol(argv[3]); test(atol(argv[1]), atol(argv[2])); zlog_fini(); return 0; } zlog-1.2.18/test/test_press_zlog.conf000066400000000000000000000011261460570005000176270ustar00rootroot00000000000000[global] #default format = "%d(%F %T).%us %-6V %p:%T:%F:%L %m%n" default format = "%d.%us %-6V %p:%T:%F:%L %m%n" [rules] # time ./test_press_zlog 1 10 100000 real user sys #*.* | /usr/bin/cronolog press%Y%m%d.log #1.632s 2.010s 1.100s *.* "press.log" #2.364s 2.090s 2.460s #*.* "press.log",10M #4.644s 2.540s 6.260s #*.* "press%d(%Y%m%d).log" #4.132s 2.910s 5.030s #*.* "press%d(%Y%m%d).log",1M*5 #4.713s 2.740s 6.310s #*.* "press.%d(%F).log",1MB ~ "press.#2r.log"#4.730s 2.690s 6.360s zlog-1.2.18/test/test_press_zlog2.c000066400000000000000000000036051460570005000172120ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include "zlog.h" static long loop_count; void * work(void *ptr) { long j = loop_count; char category[20]; sprintf(category, "cat%ld", (long)ptr); zlog_category_t *zc; zc = zlog_get_category(category); while(j-- > 0) { zlog_info(zc, "loglog"); } return 0; } int test(long process_count, long thread_count) { long i; pid_t pid; long j; for (i = 0; i < process_count; i++) { pid = fork(); if (pid < 0) { printf("fork fail\n"); } else if(pid == 0) { pthread_t tid[thread_count]; for (j = 0; j < thread_count; j++) { pthread_create(&(tid[j]), NULL, work, (void*)j); } for (j = 0; j < thread_count; j++) { pthread_join(tid[j], NULL); } return 0; } } for (i = 0; i < process_count; i++) { pid = wait(NULL); } return 0; } int main(int argc, char** argv) { int rc = 0; if (argc != 4) { fprintf(stderr, "test nprocess nthreads nloop\n"); exit(1); } rc = zlog_init("test_press_zlog2.conf"); if (rc) { printf("init failed\n"); return 2; } loop_count = atol(argv[3]); test(atol(argv[1]), atol(argv[2])); zlog_fini(); return 0; } zlog-1.2.18/test/test_press_zlog2.conf000066400000000000000000000006261460570005000177150ustar00rootroot00000000000000[global] default format = "%d.%us %-6V %p:%T:%F:%L %m%n" [rules] cat0.* "press.0.log" cat1.* "press.1.log" cat2.* "press.2.log" cat3.* "press.3.log" cat4.* "press.4.log" cat5.* "press.5.log" cat6.* "press.6.log" cat7.* "press.7.log" cat8.* "press.8.log" cat9.* "press.9.log" zlog-1.2.18/test/test_profile.c000066400000000000000000000015331460570005000163770ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "zlog.h" int main(int argc, char** argv) { int rc; rc = dzlog_init("test_profile.conf", "my_cat"); if (rc) { printf("init failed\n"); return -1; } dzlog_info("hello, zlog"); zlog_profile(); zlog_fini(); return 0; } zlog-1.2.18/test/test_profile.conf000066400000000000000000000000721460570005000170770ustar00rootroot00000000000000[formats] simple="%m%n" [rules] my_cat.* >stdout; simple zlog-1.2.18/test/test_prompt.c000066400000000000000000000024261460570005000162620ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "zlog.h" #include int main(int argc, char** argv) { int rc; zlog_category_t *zc,*pzc; rc = zlog_init("test_prompt.conf"); if (rc) { printf("init failed\n"); return -1; } zc = zlog_get_category("my_cat"); pzc = zlog_get_category("prompt"); if (!zc || !pzc) { printf("get cat fail\n"); zlog_fini(); return -2; } zlog_debug(zc, "%s%d", "hello, zlog ", 1); zlog_info(zc, "hello, zlog 2"); for (int i =0; i<15;i++){ zlog_info(pzc, "prompt>"); sleep(1); if (! (i%3)) zlog_debug(zc, "dummy log entry %d",i); if (! (i%5)) zlog_info(zc, "hello, zlog %d",i); } zlog_info(zc, "done"); // zlog_profile(); zlog_fini(); return 0; } zlog-1.2.18/test/test_prompt.conf000066400000000000000000000002561460570005000167640ustar00rootroot00000000000000[global] strict init = true buffer min = 1024 buffer max = 0 [formats] simple = "%r%d %V %m%n" prompt = "%r%d(%T)>%m" [rules ] !.* >stdout;simple prompt.* >stdout;prompt zlog-1.2.18/test/test_record.c000066400000000000000000000021321460570005000162110ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "zlog.h" int output(zlog_msg_t *msg) { printf("[mystd]:[%s][%s][%ld]\n", msg->path, msg->buf, (long)msg->len); return 0; } int main(int argc, char** argv) { int rc; zlog_category_t *zc; rc = zlog_init("test_record.conf"); if (rc) { printf("init failed\n"); return -1; } zlog_set_record("myoutput", output); zc = zlog_get_category("my_cat"); if (!zc) { printf("get cat fail\n"); zlog_fini(); return -2; } zlog_info(zc, "hello, zlog"); zlog_fini(); return 0; } zlog-1.2.18/test/test_record.conf000066400000000000000000000001161460570005000167140ustar00rootroot00000000000000[formats] simple = "%m%n" [rules] my_cat.* $myoutput, " mypath %c %d";simple zlog-1.2.18/test/test_syslog.c000066400000000000000000000017621460570005000162630ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "zlog.h" int main(int argc, char** argv) { int rc; zlog_category_t *zc; rc = zlog_init("test_syslog.conf"); if (rc) { printf("init failed\n"); return -1; } zc = zlog_get_category("my_cat"); if (!zc) { printf("get cat fail\n"); zlog_fini(); return -2; } zlog_info(zc, "hello, zlog -- info"); zlog_error(zc, "hello, zlog -- error"); zlog_fini(); return 0; } zlog-1.2.18/test/test_syslog.conf000066400000000000000000000004721460570005000167630ustar00rootroot00000000000000[global] strict init = true buffer min= 1024 buffer max= 2MB rotate lock file = /tmp/zlog.lock default format = "default - %d(%F %X.%ms) %-6V (%c:%F:%U:%L) - %m%n" [formats] null = "%n" print = "print - [%-10.3d(%F)]%n" simple = "simple - %m%n" [rules] *.* >syslog , LOG_LOCAL0 my_cat.* >stdout;simple zlog-1.2.18/test/test_tmp.c000077500000000000000000000021441460570005000155410ustar00rootroot00000000000000/* Copyright (c) Hardy Simpson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "zlog.h" #include int main(int argc, char** argv) { int rc; zlog_category_t *zc; rc = zlog_init("test_tmp.conf"); if (rc) { printf("init failed\n"); return -1; } zc = zlog_get_category("my_cat"); if (!zc) { printf("get cat fail\n"); zlog_fini(); return -2; } zlog_debug(zc, "%s%d", "hello, zlog ", 1); zlog_info(zc, "hello, zlog 2"); sleep(1); zlog_info(zc, "hello, zlog 3"); zlog_debug(zc, "hello, zlog 4"); // zlog_profile(); zlog_fini(); return 0; } zlog-1.2.18/test/test_tmp.conf000066400000000000000000000003221460570005000162350ustar00rootroot00000000000000[global] strict init = true buffer min = 1024 buffer max = 0 rotate lock file = /tmp/zlog.lock [formats] simple = "%d %V %m%n" simple2 = "%d %V %m%n" [rules ] *.=debug >stdout;simple *.=info >stdout;simple2 zlog-1.2.18/test/val.sh000077500000000000000000000002001460570005000146430ustar00rootroot00000000000000unset ZLOG_PROFILE_ERROR unset ZLOG_PROFILE_DEBUG_LOG rm -f press*log valgrind --tool=callgrind ./test_press_zlog 1 10 10000 zlog-1.2.18/tools/000077500000000000000000000000001460570005000137135ustar00rootroot00000000000000zlog-1.2.18/tools/mk_targz.sh000077500000000000000000000007221460570005000160710ustar00rootroot00000000000000#!/bin/sh if [ "$1" = "" ] then echo "Usage: mk_targz.sh " echo "Example: mk_targz.sh 1.2.7" exit 1 fi if [ ! -d .git ] then echo "Must run at git home directory" exit 2 fi HASH=`git show-ref --hash=8 refs/tags/${1}` PREFIX="zlog-${1}-${HASH}/" TARBALL="/tmp/zlog-${1}-${HASH}.tar.gz" git archive --format=tar -v --prefix=$PREFIX $1 | gzip -c > $TARBALL cp ${TARBALL} /tmp/zlog-latest-stable.tar.gz echo "File created: $TARBALL" zlog-1.2.18/zlog.spec000066400000000000000000000015041460570005000144020ustar00rootroot00000000000000Name: zlog Version: 1.2.8 Release: 1 Summary: zlog logger framework License: Apache License, Version 2.0 URL: http://hardysimpson.github.io/zlog/ BuildRequires: gcc make %define _builddir %(echo $PWD) %description AIRTAME Web Service is powering the setup page. %build make clean all %install install -d '%{buildroot}/usr/include' install -d '%{buildroot}/usr/lib' cp src/zlog.h '%{buildroot}/usr/include/zlog.h' cp src/libzlog.a '%{buildroot}/usr/lib/libzlog.a' cp src/libzlog.so.1.2 '%{buildroot}/usr/lib/libzlog.so.1.2' ln -sf /usr/lib/libzlog.so.1.2 '%{buildroot}/usr/lib/libzlog.so.1' ln -sf /usr/lib/libzlog.so.1.2 '%{buildroot}/usr/lib/libzlog.so' %clean %files /usr/include/zlog.h /usr/lib/libzlog.a /usr/lib/libzlog.so /usr/lib/libzlog.so.1 /usr/lib/libzlog.so.1.2