├── .gitignore ├── .travis.yml ├── CMakeLists.txt ├── LICENCE.txt ├── README.md ├── cmake ├── FindLua.cmake ├── dist.cmake └── lua.cmake ├── dist.info ├── doc └── doc.md ├── examples ├── async.lua ├── async_lane.lua ├── async_net.lua ├── async_proxy.lua ├── basic.lua ├── dump.lua ├── email.lua ├── filter.lua ├── multi.lua ├── multithread │ ├── main.lua │ └── myapp │ │ ├── config.lua │ │ └── log.lua ├── net.lua ├── proxy.lua ├── server_lane.lua ├── server_zmq.lua ├── simple.lua ├── simple_format.lua └── syslog.lua ├── lakeconfig.lua ├── lakefile ├── lua ├── log.lua └── log │ ├── formatter │ ├── concat.lua │ ├── default.lua │ ├── format.lua │ ├── mix.lua │ └── pformat.lua │ ├── logformat │ ├── default.lua │ ├── proxy.lua │ ├── proxy │ │ └── pack.lua │ └── syslog.lua │ └── writer │ ├── async │ ├── _private │ │ └── server.lua │ ├── lane.lua │ ├── server │ │ ├── lane.lua │ │ ├── udp.lua │ │ └── zmq.lua │ ├── udp.lua │ └── zmq.lua │ ├── console.lua │ ├── console │ ├── .color.lua.un~ │ └── color.lua │ ├── file.lua │ ├── file │ ├── by_day.lua │ ├── private │ │ └── impl.lua │ └── roll.lua │ ├── filter.lua │ ├── filter │ └── lvl │ │ ├── eq.lua │ │ └── le.lua │ ├── format.lua │ ├── freeswitch.lua │ ├── list.lua │ ├── net │ ├── server │ │ ├── udp.lua │ │ └── zmq.lua │ ├── smtp.lua │ ├── udp.lua │ ├── zmq.lua │ └── zmq │ │ ├── _private │ │ ├── compat.lua │ │ └── impl.lua │ │ ├── pub.lua │ │ ├── push.lua │ │ └── srv │ │ └── pub.lua │ ├── prefix.lua │ ├── stderr.lua │ └── stdout.lua ├── rockspecs ├── lua-log-0.1.0-1.rockspec ├── lua-log-0.1.1-1.rockspec ├── lua-log-0.1.2-1.rockspec ├── lua-log-0.1.3-1.rockspec ├── lua-log-0.1.4-1.rockspec ├── lua-log-0.1.5-1.rockspec ├── lua-log-0.1.6-1.rockspec └── lua-log-scm-0.rockspec ├── spec ├── basic_spec.lua └── utils.lua ├── test ├── test_basic.lua ├── test_file_writer.lua ├── test_zmq_finalizer.lua └── utils.lua └── utils ├── monitor.lua └── proxy_monitor.lua /.gitignore: -------------------------------------------------------------------------------- 1 | *.spec -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | 3 | sudo: false 4 | 5 | addons: 6 | apt: 7 | packages: 8 | - openssl 9 | - libzmq3 10 | - libzmq3-dev 11 | 12 | matrix: 13 | include: 14 | - compiler: ": Lua51" 15 | env: LUA="lua 5.1" 16 | - compiler: ": Lua52" 17 | env: LUA="lua 5.2" 18 | - compiler: ": Lua53" 19 | env: LUA="lua 5.3" 20 | - compiler: ": LuaJIT20" 21 | env: LUA="luajit 2.0" 22 | - compiler: ": LuaJIT21" 23 | env: LUA="luajit 2.1" 24 | 25 | cache: 26 | directories: 27 | - here 28 | - $HOME/.cache/pip 29 | 30 | branches: 31 | only: 32 | - master 33 | 34 | before_install: 35 | - export CC=gcc 36 | - pip install --user hererocks 37 | - hererocks here -r^ --$LUA; 38 | - export PATH=$PATH:$PWD/here/bin 39 | 40 | install: 41 | - luarocks make rockspecs/lua-log-scm-0.rockspec 42 | 43 | before_script: 44 | - luarocks show busted || luarocks install busted 45 | - luarocks show lunitx || luarocks install lunitx 46 | - luarocks show luafilesystem || luarocks install luafilesystem 47 | - luarocks show lua-path || luarocks install lua-path 48 | - luarocks show lzmq || luarocks install lzmq 49 | - luarocks show lua-llthreads2 || luarocks install lua-llthreads2 50 | - luarocks show lanes || luarocks install lanes 51 | - luarocks show luasocket || luarocks install luasocket 52 | - luarocks show lpeg || luarocks install lpeg 53 | 54 | script: 55 | - cd test 56 | - lua -v -e"print(require 'lzmq'.version(true))" 57 | - lunit.sh test_file_writer.lua 58 | - lunit.sh test_basic.lua 59 | - lua test_zmq_finalizer.lua 60 | - cd .. 61 | - busted spec 62 | 63 | notifications: 64 | email: 65 | on_success: change 66 | on_failure: always -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required ( VERSION 2.8 ) 2 | 3 | project ( lua-log NONE ) 4 | include ( cmake/dist.cmake ) 5 | include ( lua ) 6 | 7 | install_lua_module ( log lua/log.lua ) 8 | install_lua_module ( log.formatter.concat lua/log/formatter/concat.lua ) 9 | install_lua_module ( log.formatter.default lua/log/formatter/default.lua ) 10 | install_lua_module ( log.formatter.format lua/log/formatter/format.lua ) 11 | install_lua_module ( log.formatter.pformat lua/log/formatter/pformat.lua ) 12 | install_lua_module ( log.formatter.mix lua/log/formatter/mix.lua ) 13 | install_lua_module ( log.logformat.default lua/log/logformat/default.lua ) 14 | install_lua_module ( log.logformat.proxy lua/log/logformat/proxy.lua ) 15 | install_lua_module ( log.logformat.proxy.pack lua/log/logformat/proxy/pack.lua ) 16 | install_lua_module ( log.logformat.syslog lua/log/logformat/syslog.lua ) 17 | install_lua_module ( log.writer.async._private.server lua/log/writer/async/_private/server.lua ) 18 | install_lua_module ( log.writer.async.lane lua/log/writer/async/lane.lua ) 19 | install_lua_module ( log.writer.async.server.lane lua/log/writer/async/server/lane.lua ) 20 | install_lua_module ( log.writer.async.server.udp lua/log/writer/async/server/udp.lua ) 21 | install_lua_module ( log.writer.async.server.zmq lua/log/writer/async/server/zmq.lua ) 22 | install_lua_module ( log.writer.async.udp lua/log/writer/async/udp.lua ) 23 | install_lua_module ( log.writer.async.zmq lua/log/writer/async/zmq.lua ) 24 | install_lua_module ( log.writer.console lua/log/writer/console.lua ) 25 | install_lua_module ( log.writer.console.color lua/log/writer/console/color.lua ) 26 | install_lua_module ( log.writer.file lua/log/writer/file.lua ) 27 | install_lua_module ( log.writer.file.by_day lua/log/writer/file/by_day.lua ) 28 | install_lua_module ( log.writer.file.private.impl lua/log/writer/file/private/impl.lua ) 29 | install_lua_module ( log.writer.file.roll lua/log/writer/file/roll.lua ) 30 | install_lua_module ( log.writer.filter lua/log/writer/filter.lua ) 31 | install_lua_module ( log.writer.filter.lvl.eq lua/log/writer/filter/lvl/eq.lua ) 32 | install_lua_module ( log.writer.filter.lvl.le lua/log/writer/filter/lvl/le.lua ) 33 | install_lua_module ( log.writer.format lua/log/writer/format.lua ) 34 | install_lua_module ( log.writer.list lua/log/writer/list.lua ) 35 | install_lua_module ( log.writer.net.server.udp lua/log/writer/net/server/udp.lua ) 36 | install_lua_module ( log.writer.net.server.zmq lua/log/writer/net/server/zmq.lua ) 37 | install_lua_module ( log.writer.net.smtp lua/log/writer/net/smtp.lua ) 38 | install_lua_module ( log.writer.net.udp lua/log/writer/net/udp.lua ) 39 | install_lua_module ( log.writer.net.zmq lua/log/writer/net/zmq.lua ) 40 | install_lua_module ( log.writer.net.zmq._private.compat lua/log/writer/net/zmq/_private/compat.lua ) 41 | install_lua_module ( log.writer.net.zmq._private.impl lua/log/writer/net/zmq/_private/impl.lua ) 42 | install_lua_module ( log.writer.net.zmq.pub lua/log/writer/net/zmq/pub.lua ) 43 | install_lua_module ( log.writer.net.zmq.push lua/log/writer/net/zmq/push.lua ) 44 | install_lua_module ( log.writer.net.zmq.srv.pub lua/log/writer/net/zmq/srv/pub.lua ) 45 | install_lua_module ( log.writer.prefix lua/log/writer/prefix.lua ) 46 | install_lua_module ( log.writer.stderr lua/log/writer/stderr.lua ) 47 | install_lua_module ( log.writer.stdout lua/log/writer/stdout.lua ) 48 | 49 | install_data ( README.md LICENCE.txt ) 50 | 51 | install_example ( examples/ ) 52 | 53 | install_test ( test/ spec/ ) 54 | -------------------------------------------------------------------------------- /LICENCE.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) 2013-2016 Alexey Melnichuk. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 14 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 15 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 16 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 17 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 18 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 19 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 21 | OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Logging library for Lua 5.1/5.2 2 | 3 | [![Build Status](https://buildhive.cloudbees.com/job/moteus/job/lua-log/badge/icon)](https://buildhive.cloudbees.com/job/moteus/job/lua-log/) 4 | [![Build Status](https://travis-ci.org/moteus/lua-log.svg?branch=master)](https://travis-ci.org/moteus/lua-log) 5 | [![Licence](http://img.shields.io/badge/Licence-MIT-brightgreen.svg)](LICENCE.txt) 6 | 7 | *** 8 | 9 | ## Usage ## 10 | 11 | ### Write to roll file and to console ### 12 | 13 | ```lua 14 | local LOG = require "log".new( 15 | -- maximum log level 16 | "trace", 17 | 18 | -- Writer 19 | require 'log.writer.list'.new( -- multi writers: 20 | require 'log.writer.console.color'.new(), -- * console color 21 | require 'log.writer.file.roll'.new( -- * roll files 22 | './logs', -- log dir 23 | 'events.log', -- current log name 24 | 10, -- count files 25 | 10*1024*1024 -- max file size in bytes 26 | ) 27 | ), 28 | 29 | -- Formatter 30 | require "log.formatter.concat".new() 31 | ) 32 | 33 | LOG.error("some", "error") 34 | ``` 35 | 36 | ### Write to file from separate thread ### 37 | 38 | ```lua 39 | local LOG = require "log".new( 40 | require "log.writer.async.zmq".new( 41 | 'inproc://async.logger', 42 | function() -- calls from separate thread/Lua state 43 | return require 'log.writer.file.by_day'.new( 44 | './logs', 'events.log', 5000 45 | ) 46 | end 47 | ) 48 | ) 49 | 50 | LOG.error("some error") 51 | ``` 52 | 53 | ### More complex example ### 54 | 55 | ```lua 56 | local host = arg[1] or '127.0.0.1' 57 | local port = arg[2] or 514 58 | 59 | -- this code run in separate thread. 60 | local InitWriter = [[ 61 | return require 'log.writer.list'.new( -- multi writers: 62 | require 'log.writer.console.color'.new(), -- * console color 63 | require 'log.writer.net.zmq'.new('%{zmq_cnn_host}'), -- * zmq pub socket 64 | require "log.writer.format".new( -- * syslog over udp 65 | require "log.logformat.syslog".new("user"), 66 | require 'log.writer.net.udp'.new('%{udp_cnn_host}', %{udp_cnn_port}) 67 | ) 68 | ) 69 | ]] 70 | 71 | -- create async writer and run new work thread. 72 | -- communicate with work thread using zmq library 73 | local writer = require "log.writer.async.zmq".new( 74 | 'inproc://async.logger', 75 | InitWriter:gsub('%%{(.-)}', { 76 | zmq_cnn_host = 'tcp://' .. host .. ':' .. port; 77 | udp_cnn_host = host; 78 | udp_cnn_port = port; 79 | }) 80 | ) 81 | 82 | -- create new logger 83 | local LOG = require"log".new(writer) 84 | 85 | require "socket".sleep(0.5) -- net.zmq need time to connect 86 | 87 | LOG.fatal("can not allocate memory") 88 | LOG.error("file not found") 89 | LOG.warning("cache server is not started") 90 | LOG.info("new message is received") 91 | LOG.notice("message has 2 file") 92 | 93 | print("Press enter ...") io.flush() io.read() 94 | ``` 95 | 96 | ### Sendout logs to separate process/host 97 | 98 | This example show how to send logs in 2 separate destination 99 | with 2 formats. Write to stdout as whell as to remote syslog 100 | server. In fact here no async on Lua side. Application write 101 | to network directly from main thread. But it is possible use 102 | one or more work threads that will do this. 103 | 104 | ```Lua 105 | -- Buld writer with 2 destinations 106 | local writer = require "log.writer.list".new( 107 | require "log.writer.format".new( 108 | -- explicit set logformat to stdout writer 109 | require "log.logformat.default".new(), 110 | require "log.writer.stdout".new() 111 | ), 112 | -- define network writer. 113 | -- This writer has no explicit format so it will 114 | -- use one defined for logger. 115 | require "log.writer.net.udp".new('127.0.0.1', 514) 116 | ) 117 | 118 | local function SYSLOG_NEW(level, ...) 119 | return require "log".new(level, writer, 120 | require "log.formatter.mix".new(), 121 | require "log.logformat.syslog".new(...) 122 | ) 123 | end 124 | 125 | local SYSLOG = { 126 | -- Define first syslog logger with some settings 127 | KERN = SYSLOG_NEW('trace', 'kern'), 128 | 129 | -- Define second syslog logger with other settings 130 | USER = SYSLOG_NEW('trace', 'USER'), 131 | } 132 | 133 | SYSLOG.KERN.emerg ('emergency message') 134 | SYSLOG.USER.alert ('alert message') 135 | ``` 136 | 137 | ## Dependences ## 138 | 139 | ### core ### 140 | * [LuaDate](https://github.com/Tieske/date) 141 | 142 | ### writer.async.udp ### 143 | * [llthreads2](http://github.com/moteus/lua-llthreads2) 144 | * or [llthreads](http://github.com/Neopallium/lua-llthreads) 145 | * writer.net.udp 146 | 147 | ### writer.async.zmq ### 148 | * [llthreads2](http://github.com/moteus/lua-llthreads2) 149 | * or [llthreads](http://github.com/Neopallium/lua-llthreads) 150 | * writer.net.zmq 151 | 152 | ### writer.async.lane ### 153 | * [LuaLanes](https://github.com/LuaLanes/lanes) - This is experemental writer 154 | 155 | ### writer.console.color ### 156 | * ansicolors 157 | * or lua-conio 158 | * or cio (Windows only) 159 | 160 | ### writer.file.by_day ### 161 | * [lfs](http://keplerproject.github.com/luafilesystem) 162 | 163 | ### writer.net.udp ### 164 | * [LuaSocket](http://www.impa.br/~diego/software/luasocket) 165 | 166 | ### writer.net.zmq ### 167 | * [lzmq](http://github.com/moteus/lzmq) 168 | * or [lua-zmq](http://github.com/Neopallium/lua-zmq) 169 | 170 | ### writer.net.smtp ### 171 | * [LuaSocket](http://www.impa.br/~diego/software/luasocket) 172 | * [lua-sendmail](http://github.com/moteus/lua-sendmail) 173 | -------------------------------------------------------------------------------- /cmake/FindLua.cmake: -------------------------------------------------------------------------------- 1 | # Locate Lua library 2 | # This module defines 3 | # LUA_EXECUTABLE, if found 4 | # LUA_FOUND, if false, do not try to link to Lua 5 | # LUA_LIBRARIES 6 | # LUA_INCLUDE_DIR, where to find lua.h 7 | # LUA_VERSION_STRING, the version of Lua found (since CMake 2.8.8) 8 | # 9 | # Note that the expected include convention is 10 | # #include "lua.h" 11 | # and not 12 | # #include 13 | # This is because, the lua location is not standardized and may exist 14 | # in locations other than lua/ 15 | 16 | #============================================================================= 17 | # Copyright 2007-2009 Kitware, Inc. 18 | # Modified to support Lua 5.2 by LuaDist 2012 19 | # 20 | # Distributed under the OSI-approved BSD License (the "License"); 21 | # see accompanying file Copyright.txt for details. 22 | # 23 | # This software is distributed WITHOUT ANY WARRANTY; without even the 24 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 25 | # See the License for more information. 26 | #============================================================================= 27 | # (To distribute this file outside of CMake, substitute the full 28 | # License text for the above reference.) 29 | # 30 | # The required version of Lua can be specified using the 31 | # standard syntax, e.g. FIND_PACKAGE(Lua 5.1) 32 | # Otherwise the module will search for any available Lua implementation 33 | 34 | # Always search for non-versioned lua first (recommended) 35 | SET(_POSSIBLE_LUA_INCLUDE include include/lua) 36 | SET(_POSSIBLE_LUA_EXECUTABLE lua) 37 | SET(_POSSIBLE_LUA_LIBRARY lua) 38 | 39 | # Determine possible naming suffixes (there is no standard for this) 40 | IF(Lua_FIND_VERSION_MAJOR AND Lua_FIND_VERSION_MINOR) 41 | SET(_POSSIBLE_SUFFIXES "${Lua_FIND_VERSION_MAJOR}${Lua_FIND_VERSION_MINOR}" "${Lua_FIND_VERSION_MAJOR}.${Lua_FIND_VERSION_MINOR}" "-${Lua_FIND_VERSION_MAJOR}.${Lua_FIND_VERSION_MINOR}") 42 | ELSE(Lua_FIND_VERSION_MAJOR AND Lua_FIND_VERSION_MINOR) 43 | SET(_POSSIBLE_SUFFIXES "52" "5.2" "-5.2" "51" "5.1" "-5.1") 44 | ENDIF(Lua_FIND_VERSION_MAJOR AND Lua_FIND_VERSION_MINOR) 45 | 46 | # Set up possible search names and locations 47 | FOREACH(_SUFFIX ${_POSSIBLE_SUFFIXES}) 48 | LIST(APPEND _POSSIBLE_LUA_INCLUDE "include/lua${_SUFFIX}") 49 | LIST(APPEND _POSSIBLE_LUA_EXECUTABLE "lua${_SUFFIX}") 50 | LIST(APPEND _POSSIBLE_LUA_LIBRARY "lua${_SUFFIX}") 51 | ENDFOREACH(_SUFFIX) 52 | 53 | # Find the lua executable 54 | FIND_PROGRAM(LUA_EXECUTABLE 55 | NAMES ${_POSSIBLE_LUA_EXECUTABLE} 56 | ) 57 | 58 | # Find the lua header 59 | FIND_PATH(LUA_INCLUDE_DIR lua.h 60 | HINTS 61 | $ENV{LUA_DIR} 62 | PATH_SUFFIXES ${_POSSIBLE_LUA_INCLUDE} 63 | PATHS 64 | ~/Library/Frameworks 65 | /Library/Frameworks 66 | /usr/local 67 | /usr 68 | /sw # Fink 69 | /opt/local # DarwinPorts 70 | /opt/csw # Blastwave 71 | /opt 72 | ) 73 | 74 | # Find the lua library 75 | FIND_LIBRARY(LUA_LIBRARY 76 | NAMES ${_POSSIBLE_LUA_LIBRARY} 77 | HINTS 78 | $ENV{LUA_DIR} 79 | PATH_SUFFIXES lib64 lib 80 | PATHS 81 | ~/Library/Frameworks 82 | /Library/Frameworks 83 | /usr/local 84 | /usr 85 | /sw 86 | /opt/local 87 | /opt/csw 88 | /opt 89 | ) 90 | 91 | IF(LUA_LIBRARY) 92 | # include the math library for Unix 93 | IF(UNIX AND NOT APPLE) 94 | FIND_LIBRARY(LUA_MATH_LIBRARY m) 95 | SET( LUA_LIBRARIES "${LUA_LIBRARY};${LUA_MATH_LIBRARY}" CACHE STRING "Lua Libraries") 96 | # For Windows and Mac, don't need to explicitly include the math library 97 | ELSE(UNIX AND NOT APPLE) 98 | SET( LUA_LIBRARIES "${LUA_LIBRARY}" CACHE STRING "Lua Libraries") 99 | ENDIF(UNIX AND NOT APPLE) 100 | ENDIF(LUA_LIBRARY) 101 | 102 | # Determine Lua version 103 | IF(LUA_INCLUDE_DIR AND EXISTS "${LUA_INCLUDE_DIR}/lua.h") 104 | FILE(STRINGS "${LUA_INCLUDE_DIR}/lua.h" lua_version_str REGEX "^#define[ \t]+LUA_RELEASE[ \t]+\"Lua .+\"") 105 | 106 | STRING(REGEX REPLACE "^#define[ \t]+LUA_RELEASE[ \t]+\"Lua ([^\"]+)\".*" "\\1" LUA_VERSION_STRING "${lua_version_str}") 107 | UNSET(lua_version_str) 108 | ENDIF() 109 | 110 | # Lua 5.2 111 | IF(NOT LUA_VERSION_STRING) 112 | FILE(STRINGS "${LUA_INCLUDE_DIR}/lua.h" lua_version_str REGEX "^#define LUA_VERSION_[A-Z]+[ \t]+\"[0-9]+\"") 113 | STRING(REGEX REPLACE ".*#define LUA_VERSION_MAJOR[ \t]+\"([0-9]+)\".*" "\\1" LUA_VERSION_MAJOR ${lua_version_str}) 114 | STRING(REGEX REPLACE ".*#define LUA_VERSION_MINOR[ \t]+\"([0-9]+)\".*" "\\1" LUA_VERSION_MINOR ${lua_version_str}) 115 | STRING(REGEX REPLACE ".*#define LUA_VERSION_RELEASE[ \t]+\"([0-9]+)\".*" "\\1" LUA_VERSION_RELEASE ${lua_version_str}) 116 | SET(LUA_VERSION_STRING ${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}.${LUA_VERSION_RELEASE}) 117 | ENDIF() 118 | 119 | INCLUDE(FindPackageHandleStandardArgs) 120 | # handle the QUIETLY and REQUIRED arguments and set LUA_FOUND to TRUE if 121 | # all listed variables are TRUE 122 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(Lua 123 | REQUIRED_VARS LUA_LIBRARIES LUA_INCLUDE_DIR 124 | VERSION_VAR LUA_VERSION_STRING) 125 | 126 | MARK_AS_ADVANCED(LUA_INCLUDE_DIR LUA_LIBRARIES LUA_LIBRARY LUA_MATH_LIBRARY LUA_EXECUTABLE) 127 | 128 | -------------------------------------------------------------------------------- /cmake/dist.cmake: -------------------------------------------------------------------------------- 1 | # LuaDist CMake utility library. 2 | # Provides sane project defaults and macros common to LuaDist CMake builds. 3 | # 4 | # Copyright (C) 2007-2012 LuaDist. 5 | # by David Manura, Peter Drahoš 6 | # Redistribution and use of this file is allowed according to the terms of the MIT license. 7 | # For details see the COPYRIGHT file distributed with LuaDist. 8 | # Please note that the package source code is licensed under its own license. 9 | 10 | ## Extract information from dist.info 11 | if ( NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/dist.info ) 12 | message ( FATAL_ERROR 13 | "Missing dist.info file (${CMAKE_CURRENT_SOURCE_DIR}/dist.info)." ) 14 | endif () 15 | file ( READ ${CMAKE_CURRENT_SOURCE_DIR}/dist.info DIST_INFO ) 16 | if ( "${DIST_INFO}" STREQUAL "" ) 17 | message ( FATAL_ERROR "Failed to load dist.info." ) 18 | endif () 19 | # Reads field `name` from dist.info string `DIST_INFO` into variable `var`. 20 | macro ( _parse_dist_field name var ) 21 | string ( REGEX REPLACE ".*${name}[ \t]?=[ \t]?[\"']([^\"']+)[\"'].*" "\\1" 22 | ${var} "${DIST_INFO}" ) 23 | if ( ${var} STREQUAL DIST_INFO ) 24 | message ( FATAL_ERROR "Failed to extract \"${var}\" from dist.info" ) 25 | endif () 26 | endmacro () 27 | # 28 | _parse_dist_field ( name DIST_NAME ) 29 | _parse_dist_field ( version DIST_VERSION ) 30 | _parse_dist_field ( license DIST_LICENSE ) 31 | _parse_dist_field ( author DIST_AUTHOR ) 32 | _parse_dist_field ( maintainer DIST_MAINTAINER ) 33 | _parse_dist_field ( url DIST_URL ) 34 | _parse_dist_field ( desc DIST_DESC ) 35 | message ( "DIST_NAME: ${DIST_NAME}") 36 | message ( "DIST_VERSION: ${DIST_VERSION}") 37 | message ( "DIST_LICENSE: ${DIST_LICENSE}") 38 | message ( "DIST_AUTHOR: ${DIST_AUTHOR}") 39 | message ( "DIST_MAINTAINER: ${DIST_MAINTAINER}") 40 | message ( "DIST_URL: ${DIST_URL}") 41 | message ( "DIST_DESC: ${DIST_DESC}") 42 | string ( REGEX REPLACE ".*depends[ \t]?=[ \t]?[\"']([^\"']+)[\"'].*" "\\1" 43 | DIST_DEPENDS ${DIST_INFO} ) 44 | if ( DIST_DEPENDS STREQUAL DIST_INFO ) 45 | set ( DIST_DEPENDS "" ) 46 | endif () 47 | message ( "DIST_DEPENDS: ${DIST_DEPENDS}") 48 | ## 2DO: Parse DIST_DEPENDS and try to install Dependencies with automatically using externalproject_add 49 | 50 | 51 | ## INSTALL DEFAULTS (Relative to CMAKE_INSTALL_PREFIX) 52 | # Primary paths 53 | set ( INSTALL_BIN bin CACHE PATH "Where to install binaries to." ) 54 | set ( INSTALL_LIB lib CACHE PATH "Where to install libraries to." ) 55 | set ( INSTALL_INC include CACHE PATH "Where to install headers to." ) 56 | set ( INSTALL_ETC etc CACHE PATH "Where to store configuration files" ) 57 | set ( INSTALL_SHARE share CACHE PATH "Directory for shared data." ) 58 | 59 | # Secondary paths 60 | option ( INSTALL_VERSION 61 | "Install runtime libraries and executables with version information." OFF) 62 | set ( INSTALL_DATA ${INSTALL_SHARE}/${DIST_NAME} CACHE PATH 63 | "Directory the package can store documentation, tests or other data in.") 64 | set ( INSTALL_DOC ${INSTALL_DATA}/doc CACHE PATH 65 | "Recommended directory to install documentation into.") 66 | set ( INSTALL_EXAMPLE ${INSTALL_DATA}/example CACHE PATH 67 | "Recommended directory to install examples into.") 68 | set ( INSTALL_TEST ${INSTALL_DATA}/test CACHE PATH 69 | "Recommended directory to install tests into.") 70 | set ( INSTALL_FOO ${INSTALL_DATA}/etc CACHE PATH 71 | "Where to install additional files") 72 | 73 | # Tweaks and other defaults 74 | # Setting CMAKE to use loose block and search for find modules in source directory 75 | set ( CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS true ) 76 | set ( CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH} ) 77 | option ( BUILD_SHARED_LIBS "Build shared libraries" ON ) 78 | 79 | # In MSVC, prevent warnings that can occur when using standard libraries. 80 | if ( MSVC ) 81 | add_definitions ( -D_CRT_SECURE_NO_WARNINGS ) 82 | endif () 83 | 84 | # RPath and relative linking 85 | option ( USE_RPATH "Use relative linking." ON) 86 | if ( USE_RPATH ) 87 | string ( REGEX REPLACE "[^!/]+" ".." UP_DIR ${INSTALL_BIN} ) 88 | set ( CMAKE_SKIP_BUILD_RPATH FALSE CACHE STRING "" FORCE ) 89 | set ( CMAKE_BUILD_WITH_INSTALL_RPATH FALSE CACHE STRING "" FORCE ) 90 | set ( CMAKE_INSTALL_RPATH $ORIGIN/${UP_DIR}/${INSTALL_LIB} 91 | CACHE STRING "" FORCE ) 92 | set ( CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE CACHE STRING "" FORCE ) 93 | set ( CMAKE_INSTALL_NAME_DIR @executable_path/${UP_DIR}/${INSTALL_LIB} 94 | CACHE STRING "" FORCE ) 95 | endif () 96 | 97 | ## MACROS 98 | # Parser macro 99 | macro ( parse_arguments prefix arg_names option_names) 100 | set ( DEFAULT_ARGS ) 101 | foreach ( arg_name ${arg_names} ) 102 | set ( ${prefix}_${arg_name} ) 103 | endforeach () 104 | foreach ( option ${option_names} ) 105 | set ( ${prefix}_${option} FALSE ) 106 | endforeach () 107 | 108 | set ( current_arg_name DEFAULT_ARGS ) 109 | set ( current_arg_list ) 110 | foreach ( arg ${ARGN} ) 111 | set ( larg_names ${arg_names} ) 112 | list ( FIND larg_names "${arg}" is_arg_name ) 113 | if ( is_arg_name GREATER -1 ) 114 | set ( ${prefix}_${current_arg_name} ${current_arg_list} ) 115 | set ( current_arg_name ${arg} ) 116 | set ( current_arg_list ) 117 | else () 118 | set ( loption_names ${option_names} ) 119 | list ( FIND loption_names "${arg}" is_option ) 120 | if ( is_option GREATER -1 ) 121 | set ( ${prefix}_${arg} TRUE ) 122 | else () 123 | set ( current_arg_list ${current_arg_list} ${arg} ) 124 | endif () 125 | endif () 126 | endforeach () 127 | set ( ${prefix}_${current_arg_name} ${current_arg_list} ) 128 | endmacro () 129 | 130 | 131 | # install_executable ( executable_targets ) 132 | # Installs any executables generated using "add_executable". 133 | # USE: install_executable ( lua ) 134 | # NOTE: subdirectories are NOT supported 135 | set ( CPACK_COMPONENT_RUNTIME_DISPLAY_NAME "${DIST_NAME} Runtime" ) 136 | set ( CPACK_COMPONENT_RUNTIME_DESCRIPTION 137 | "Executables and runtime libraries. Installed into ${INSTALL_BIN}." ) 138 | macro ( install_executable ) 139 | foreach ( _file ${ARGN} ) 140 | if ( INSTALL_VERSION ) 141 | set_target_properties ( ${_file} PROPERTIES VERSION ${DIST_VERSION} 142 | SOVERSION ${DIST_VERSION} ) 143 | endif () 144 | install ( TARGETS ${_file} RUNTIME DESTINATION ${INSTALL_BIN} 145 | COMPONENT Runtime ) 146 | endforeach() 147 | endmacro () 148 | 149 | # install_library ( library_targets ) 150 | # Installs any libraries generated using "add_library" into apropriate places. 151 | # USE: install_library ( libexpat ) 152 | # NOTE: subdirectories are NOT supported 153 | set ( CPACK_COMPONENT_LIBRARY_DISPLAY_NAME "${DIST_NAME} Development Libraries" ) 154 | set ( CPACK_COMPONENT_LIBRARY_DESCRIPTION 155 | "Static and import libraries needed for development. Installed into ${INSTALL_LIB} or ${INSTALL_BIN}." ) 156 | macro ( install_library ) 157 | foreach ( _file ${ARGN} ) 158 | if ( INSTALL_VERSION ) 159 | set_target_properties ( ${_file} PROPERTIES VERSION ${DIST_VERSION} 160 | SOVERSION ${DIST_VERSION} ) 161 | endif () 162 | install ( TARGETS ${_file} 163 | RUNTIME DESTINATION ${INSTALL_BIN} COMPONENT Runtime 164 | LIBRARY DESTINATION ${INSTALL_LIB} COMPONENT Runtime 165 | ARCHIVE DESTINATION ${INSTALL_LIB} COMPONENT Library ) 166 | endforeach() 167 | endmacro () 168 | 169 | # helper function for various install_* functions, for PATTERN/REGEX args. 170 | macro ( _complete_install_args ) 171 | if ( NOT("${_ARG_PATTERN}" STREQUAL "") ) 172 | set ( _ARG_PATTERN PATTERN ${_ARG_PATTERN} ) 173 | endif () 174 | if ( NOT("${_ARG_REGEX}" STREQUAL "") ) 175 | set ( _ARG_REGEX REGEX ${_ARG_REGEX} ) 176 | endif () 177 | endmacro () 178 | 179 | # install_header ( files/directories [INTO destination] ) 180 | # Install a directories or files into header destination. 181 | # USE: install_header ( lua.h luaconf.h ) or install_header ( GL ) 182 | # USE: install_header ( mylib.h INTO mylib ) 183 | # For directories, supports optional PATTERN/REGEX arguments like install(). 184 | set ( CPACK_COMPONENT_HEADER_DISPLAY_NAME "${DIST_NAME} Development Headers" ) 185 | set ( CPACK_COMPONENT_HEADER_DESCRIPTION 186 | "Headers needed for development. Installed into ${INSTALL_INC}." ) 187 | macro ( install_header ) 188 | parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) 189 | _complete_install_args() 190 | foreach ( _file ${_ARG_DEFAULT_ARGS} ) 191 | if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) 192 | install ( DIRECTORY ${_file} DESTINATION ${INSTALL_INC}/${_ARG_INTO} 193 | COMPONENT Header ${_ARG_PATTERN} ${_ARG_REGEX} ) 194 | else () 195 | install ( FILES ${_file} DESTINATION ${INSTALL_INC}/${_ARG_INTO} 196 | COMPONENT Header ) 197 | endif () 198 | endforeach() 199 | endmacro () 200 | 201 | # install_data ( files/directories [INTO destination] ) 202 | # This installs additional data files or directories. 203 | # USE: install_data ( extra data.dat ) 204 | # USE: install_data ( image1.png image2.png INTO images ) 205 | # For directories, supports optional PATTERN/REGEX arguments like install(). 206 | set ( CPACK_COMPONENT_DATA_DISPLAY_NAME "${DIST_NAME} Data" ) 207 | set ( CPACK_COMPONENT_DATA_DESCRIPTION 208 | "Application data. Installed into ${INSTALL_DATA}." ) 209 | macro ( install_data ) 210 | parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) 211 | _complete_install_args() 212 | foreach ( _file ${_ARG_DEFAULT_ARGS} ) 213 | if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) 214 | install ( DIRECTORY ${_file} 215 | DESTINATION ${INSTALL_DATA}/${_ARG_INTO} 216 | COMPONENT Data ${_ARG_PATTERN} ${_ARG_REGEX} ) 217 | else () 218 | install ( FILES ${_file} DESTINATION ${INSTALL_DATA}/${_ARG_INTO} 219 | COMPONENT Data ) 220 | endif () 221 | endforeach() 222 | endmacro () 223 | 224 | # INSTALL_DOC ( files/directories [INTO destination] ) 225 | # This installs documentation content 226 | # USE: install_doc ( doc/ doc.pdf ) 227 | # USE: install_doc ( index.html INTO html ) 228 | # For directories, supports optional PATTERN/REGEX arguments like install(). 229 | set ( CPACK_COMPONENT_DOCUMENTATION_DISPLAY_NAME "${DIST_NAME} Documentation" ) 230 | set ( CPACK_COMPONENT_DOCUMENTATION_DESCRIPTION 231 | "Application documentation. Installed into ${INSTALL_DOC}." ) 232 | macro ( install_doc ) 233 | parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) 234 | _complete_install_args() 235 | foreach ( _file ${_ARG_DEFAULT_ARGS} ) 236 | if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) 237 | install ( DIRECTORY ${_file} DESTINATION ${INSTALL_DOC}/${_ARG_INTO} 238 | COMPONENT Documentation ${_ARG_PATTERN} ${_ARG_REGEX} ) 239 | else () 240 | install ( FILES ${_file} DESTINATION ${INSTALL_DOC}/${_ARG_INTO} 241 | COMPONENT Documentation ) 242 | endif () 243 | endforeach() 244 | endmacro () 245 | 246 | # install_example ( files/directories [INTO destination] ) 247 | # This installs additional examples 248 | # USE: install_example ( examples/ exampleA ) 249 | # USE: install_example ( super_example super_data INTO super) 250 | # For directories, supports optional PATTERN/REGEX argument like install(). 251 | set ( CPACK_COMPONENT_EXAMPLE_DISPLAY_NAME "${DIST_NAME} Examples" ) 252 | set ( CPACK_COMPONENT_EXAMPLE_DESCRIPTION 253 | "Examples and their associated data. Installed into ${INSTALL_EXAMPLE}." ) 254 | macro ( install_example ) 255 | parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) 256 | _complete_install_args() 257 | foreach ( _file ${_ARG_DEFAULT_ARGS} ) 258 | if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) 259 | install ( DIRECTORY ${_file} DESTINATION ${INSTALL_EXAMPLE}/${_ARG_INTO} 260 | COMPONENT Example ${_ARG_PATTERN} ${_ARG_REGEX} ) 261 | else () 262 | install ( FILES ${_file} DESTINATION ${INSTALL_EXAMPLE}/${_ARG_INTO} 263 | COMPONENT Example ) 264 | endif () 265 | endforeach() 266 | endmacro () 267 | 268 | # install_test ( files/directories [INTO destination] ) 269 | # This installs tests and test files, DOES NOT EXECUTE TESTS 270 | # USE: install_test ( my_test data.sql ) 271 | # USE: install_test ( feature_x_test INTO x ) 272 | # For directories, supports optional PATTERN/REGEX argument like install(). 273 | set ( CPACK_COMPONENT_TEST_DISPLAY_NAME "${DIST_NAME} Tests" ) 274 | set ( CPACK_COMPONENT_TEST_DESCRIPTION 275 | "Tests and associated data. Installed into ${INSTALL_TEST}." ) 276 | macro ( install_test ) 277 | parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) 278 | _complete_install_args() 279 | foreach ( _file ${_ARG_DEFAULT_ARGS} ) 280 | if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) 281 | install ( DIRECTORY ${_file} DESTINATION ${INSTALL_TEST}/${_ARG_INTO} 282 | COMPONENT Test ${_ARG_PATTERN} ${_ARG_REGEX} ) 283 | else () 284 | install ( FILES ${_file} DESTINATION ${INSTALL_TEST}/${_ARG_INTO} 285 | COMPONENT Test ) 286 | endif () 287 | endforeach() 288 | endmacro () 289 | 290 | # install_foo ( files/directories [INTO destination] ) 291 | # This installs optional or otherwise unneeded content 292 | # USE: install_foo ( etc/ example.doc ) 293 | # USE: install_foo ( icon.png logo.png INTO icons) 294 | # For directories, supports optional PATTERN/REGEX argument like install(). 295 | set ( CPACK_COMPONENT_OTHER_DISPLAY_NAME "${DIST_NAME} Unspecified Content" ) 296 | set ( CPACK_COMPONENT_OTHER_DESCRIPTION 297 | "Other unspecified content. Installed into ${INSTALL_FOO}." ) 298 | macro ( install_foo ) 299 | parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) 300 | _complete_install_args() 301 | foreach ( _file ${_ARG_DEFAULT_ARGS} ) 302 | if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) 303 | install ( DIRECTORY ${_file} DESTINATION ${INSTALL_FOO}/${_ARG_INTO} 304 | COMPONENT Other ${_ARG_PATTERN} ${_ARG_REGEX} ) 305 | else () 306 | install ( FILES ${_file} DESTINATION ${INSTALL_FOO}/${_ARG_INTO} 307 | COMPONENT Other ) 308 | endif () 309 | endforeach() 310 | endmacro () 311 | 312 | ## CTest defaults 313 | 314 | ## CPack defaults 315 | set ( CPACK_GENERATOR "ZIP" ) 316 | set ( CPACK_STRIP_FILES TRUE ) 317 | set ( CPACK_PACKAGE_NAME "${DIST_NAME}" ) 318 | set ( CPACK_PACKAGE_VERSION "${DIST_VERSION}") 319 | set ( CPACK_PACKAGE_VENDOR "LuaDist" ) 320 | set ( CPACK_COMPONENTS_ALL Runtime Library Header Data Documentation Example Other ) 321 | include ( CPack ) 322 | -------------------------------------------------------------------------------- /cmake/lua.cmake: -------------------------------------------------------------------------------- 1 | # LuaDist CMake utility library for Lua. 2 | # 3 | # Copyright (C) 2007-2012 LuaDist. 4 | # by David Manura, Peter Drahos 5 | # Redistribution and use of this file is allowed according to the terms of the MIT license. 6 | # For details see the COPYRIGHT file distributed with LuaDist. 7 | # Please note that the package source code is licensed under its own license. 8 | 9 | set ( INSTALL_LMOD ${INSTALL_LIB}/lua 10 | CACHE PATH "Directory to install Lua modules." ) 11 | set ( INSTALL_CMOD ${INSTALL_LIB}/lua 12 | CACHE PATH "Directory to install Lua binary modules." ) 13 | 14 | option ( LUA_SKIP_WRAPPER 15 | "Do not build and install Lua executable wrappers." OFF ) 16 | option ( LUA_STATIC_MODULE "Build modules for static linking" OFF ) 17 | 18 | # List of (Lua module name, file path) pairs. 19 | # Used internally by add_lua_test. Built by add_lua_module. 20 | set ( _lua_modules ) 21 | 22 | # utility function: appends path `path` to path `basepath`, properly 23 | # handling cases when `path` may be relative or absolute. 24 | macro ( _append_path basepath path result ) 25 | if ( IS_ABSOLUTE "${path}" ) 26 | set ( ${result} "${path}" ) 27 | else () 28 | set ( ${result} "${basepath}/${path}" ) 29 | endif () 30 | endmacro () 31 | 32 | # install_lua_executable ( target source ) 33 | # Automatically generate a binary if srlua package is available 34 | # The application or its source will be placed into /bin 35 | # If the application source did not have .lua suffix then it will be added 36 | # USE: lua_executable ( sputnik src/sputnik.lua ) 37 | macro ( install_lua_executable _name _source ) 38 | get_filename_component ( _source_name ${_source} NAME_WE ) 39 | # Find srlua and glue 40 | find_program( SRLUA_EXECUTABLE NAMES srlua ) 41 | find_program( GLUE_EXECUTABLE NAMES glue ) 42 | # Executable output 43 | set ( _exe ${CMAKE_CURRENT_BINARY_DIR}/${_name}${CMAKE_EXECUTABLE_SUFFIX} ) 44 | if ( NOT SKIP_LUA_WRAPPER AND SRLUA_EXECUTABLE AND GLUE_EXECUTABLE ) 45 | # Generate binary gluing the lua code to srlua, this is a robuust approach for most systems 46 | add_custom_command( 47 | OUTPUT ${_exe} 48 | COMMAND ${GLUE_EXECUTABLE} 49 | ARGS ${SRLUA_EXECUTABLE} ${_source} ${_exe} 50 | DEPENDS ${_source} 51 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 52 | VERBATIM 53 | ) 54 | # Make sure we have a target associated with the binary 55 | add_custom_target(${_name} ALL 56 | DEPENDS ${_exe} 57 | ) 58 | # Install with run permissions 59 | install ( PROGRAMS ${_exe} DESTINATION ${INSTALL_BIN} COMPONENT Runtime) 60 | # Also install source as optional resurce 61 | install ( FILES ${_source} DESTINATION ${INSTALL_FOO} COMPONENT Other ) 62 | else() 63 | # Install into bin as is but without the lua suffix, we assume the executable uses UNIX shebang/hash-bang magic 64 | install ( PROGRAMS ${_source} DESTINATION ${INSTALL_BIN} 65 | RENAME ${_source_name} 66 | COMPONENT Runtime 67 | ) 68 | endif() 69 | endmacro () 70 | 71 | macro ( _lua_module_helper is_install _name ) 72 | parse_arguments ( _MODULE "LINK;ALL_IN_ONE" "" ${ARGN} ) 73 | # _target is CMake-compatible target name for module (e.g. socket_core). 74 | # _module is relative path of target (e.g. socket/core), 75 | # without extension (e.g. .lua/.so/.dll). 76 | # _MODULE_SRC is list of module source files (e.g. .lua and .c files). 77 | # _MODULE_NAMES is list of module names (e.g. socket.core). 78 | if ( _MODULE_ALL_IN_ONE ) 79 | string ( REGEX REPLACE "\\..*" "" _target "${_name}" ) 80 | string ( REGEX REPLACE "\\..*" "" _module "${_name}" ) 81 | set ( _target "${_target}_all_in_one") 82 | set ( _MODULE_SRC ${_MODULE_ALL_IN_ONE} ) 83 | set ( _MODULE_NAMES ${_name} ${_MODULE_DEFAULT_ARGS} ) 84 | else () 85 | string ( REPLACE "." "_" _target "${_name}" ) 86 | string ( REPLACE "." "/" _module "${_name}" ) 87 | set ( _MODULE_SRC ${_MODULE_DEFAULT_ARGS} ) 88 | set ( _MODULE_NAMES ${_name} ) 89 | endif () 90 | if ( NOT _MODULE_SRC ) 91 | message ( FATAL_ERROR "no module sources specified" ) 92 | endif () 93 | list ( GET _MODULE_SRC 0 _first_source ) 94 | 95 | get_filename_component ( _ext ${_first_source} EXT ) 96 | if ( _ext STREQUAL ".lua" ) # Lua source module 97 | list ( LENGTH _MODULE_SRC _len ) 98 | if ( _len GREATER 1 ) 99 | message ( FATAL_ERROR "more than one source file specified" ) 100 | endif () 101 | 102 | set ( _module "${_module}.lua" ) 103 | 104 | get_filename_component ( _module_dir ${_module} PATH ) 105 | get_filename_component ( _module_filename ${_module} NAME ) 106 | _append_path ( "${CMAKE_CURRENT_SOURCE_DIR}" "${_first_source}" _module_path ) 107 | list ( APPEND _lua_modules "${_name}" "${_module_path}" ) 108 | 109 | if ( ${is_install} ) 110 | install ( FILES ${_first_source} DESTINATION ${INSTALL_LMOD}/${_module_dir} 111 | RENAME ${_module_filename} 112 | COMPONENT Runtime 113 | ) 114 | endif () 115 | else () # Lua C binary module 116 | enable_language ( C ) 117 | find_package ( Lua REQUIRED ) 118 | include_directories ( ${LUA_INCLUDE_DIR} ) 119 | 120 | set ( _module "${_module}${CMAKE_SHARED_MODULE_SUFFIX}" ) 121 | 122 | get_filename_component ( _module_dir ${_module} PATH ) 123 | get_filename_component ( _module_filenamebase ${_module} NAME_WE ) 124 | foreach ( _thisname ${_MODULE_NAMES} ) 125 | list ( APPEND _lua_modules "${_thisname}" 126 | "${CMAKE_CURRENT_BINARY_DIR}/\${CMAKE_CFG_INTDIR}/${_module}" ) 127 | endforeach () 128 | 129 | # Static module (not linking to lua) 130 | if ( LUA_STATIC_MODULE ) 131 | add_library( ${_target} STATIC ${_MODULE_SRC}) 132 | target_link_libraries ( ${_target} ${_MODULE_LINK} ) 133 | else () 134 | # Dynamic module 135 | add_library( ${_target} MODULE ${_MODULE_SRC}) 136 | target_link_libraries ( ${_target} ${LUA_LIBRARY} ${_MODULE_LINK} ) 137 | endif () 138 | 139 | set_target_properties ( ${_target} PROPERTIES 140 | ARCHIVE_OUTPUT_DIRECTORY "${_module_dir}" 141 | LIBRARY_OUTPUT_DIRECTORY "${_module_dir}" 142 | PREFIX "" 143 | OUTPUT_NAME "${_module_filenamebase}" ) 144 | if ( ${is_install} ) 145 | install ( TARGETS ${_target} 146 | LIBRARY DESTINATION ${INSTALL_CMOD}/${_module_dir} 147 | COMPONENT Runtime 148 | ARCHIVE DESTINATION ${INSTALL_CMOD}/${_module_dir} 149 | COMPONENT Library ) 150 | endif () 151 | endif () 152 | endmacro () 153 | 154 | # add_lua_module 155 | # Builds a Lua source module into a destination locatable by Lua 156 | # require syntax. 157 | # Binary modules are also supported where this function takes sources and 158 | # libraries to compile separated by LINK keyword. 159 | # USE: add_lua_module ( socket.http src/http.lua ) 160 | # USE2: add_lua_module ( mime.core src/mime.c ) 161 | # USE3: add_lua_module ( socket.core ${SRC_SOCKET} LINK ${LIB_SOCKET} ) 162 | # USE4: add_lua_module ( ssl.context ssl.core ALL_IN_ONE src/context.c src/ssl.c ) 163 | # This form builds an "all-in-one" module (e.g. ssl.so or ssl.dll containing 164 | # both modules ssl.context and ssl.core). The CMake target name will be 165 | # ssl_all_in_one. 166 | # Also sets variable _module_path (relative path where module typically 167 | # would be installed). 168 | macro ( add_lua_module ) 169 | _lua_module_helper ( 0 ${ARGN} ) 170 | endmacro () 171 | 172 | 173 | # install_lua_module 174 | # This is the same as `add_lua_module` but also installs the module. 175 | # USE: install_lua_module ( socket.http src/http.lua ) 176 | # USE2: install_lua_module ( mime.core src/mime.c ) 177 | # USE3: install_lua_module ( socket.core ${SRC_SOCKET} LINK ${LIB_SOCKET} ) 178 | macro ( install_lua_module ) 179 | _lua_module_helper ( 1 ${ARGN} ) 180 | endmacro () 181 | 182 | # Builds string representing Lua table mapping Lua modules names to file 183 | # paths. Used internally. 184 | macro ( _make_module_table _outvar ) 185 | set ( ${_outvar} ) 186 | list ( LENGTH _lua_modules _n ) 187 | if ( ${_n} GREATER 0 ) # avoids cmake complaint 188 | foreach ( _i RANGE 1 ${_n} 2 ) 189 | list ( GET _lua_modules ${_i} _path ) 190 | math ( EXPR _ii ${_i}-1 ) 191 | list ( GET _lua_modules ${_ii} _name ) 192 | set ( ${_outvar} "${_table} ['${_name}'] = '${_path}'\;\n") 193 | endforeach () 194 | endif () 195 | set ( ${_outvar} 196 | "local modules = { 197 | ${_table}}" ) 198 | endmacro () 199 | 200 | # add_lua_test ( _testfile [ WORKING_DIRECTORY _working_dir ] ) 201 | # Runs Lua script `_testfile` under CTest tester. 202 | # Optional named argument `WORKING_DIRECTORY` is current working directory to 203 | # run test under (defaults to ${CMAKE_CURRENT_BINARY_DIR}). 204 | # Both paths, if relative, are relative to ${CMAKE_CURRENT_SOURCE_DIR}. 205 | # Any modules previously defined with install_lua_module are automatically 206 | # preloaded (via package.preload) prior to running the test script. 207 | # Under LuaDist, set test=true in config.lua to enable testing. 208 | # USE: add_lua_test ( test/test1.lua [args...] [WORKING_DIRECTORY dir]) 209 | macro ( add_lua_test _testfile ) 210 | if ( NOT SKIP_TESTING ) 211 | parse_arguments ( _ARG "WORKING_DIRECTORY" "" ${ARGN} ) 212 | include ( CTest ) 213 | find_program ( LUA NAMES lua lua.bat ) 214 | get_filename_component ( TESTFILEABS ${_testfile} ABSOLUTE ) 215 | get_filename_component ( TESTFILENAME ${_testfile} NAME ) 216 | get_filename_component ( TESTFILEBASE ${_testfile} NAME_WE ) 217 | 218 | # Write wrapper script. 219 | # Note: One simple way to allow the script to find modules is 220 | # to just put them in package.preload. 221 | set ( TESTWRAPPER ${CMAKE_CURRENT_BINARY_DIR}/${TESTFILENAME} ) 222 | _make_module_table ( _table ) 223 | set ( TESTWRAPPERSOURCE 224 | "local CMAKE_CFG_INTDIR = ... or '.' 225 | ${_table} 226 | local function preload_modules(modules) 227 | for name, path in pairs(modules) do 228 | if path:match'%.lua' then 229 | package.preload[name] = assert(loadfile(path)) 230 | else 231 | local name = name:gsub('.*%-', '') -- remove any hyphen prefix 232 | local symbol = 'luaopen_' .. name:gsub('%.', '_') 233 | --improve: generalize to support all-in-one loader? 234 | local path = path:gsub('%$%{CMAKE_CFG_INTDIR%}', CMAKE_CFG_INTDIR) 235 | package.preload[name] = assert(package.loadlib(path, symbol)) 236 | end 237 | end 238 | end 239 | preload_modules(modules) 240 | arg[0] = '${TESTFILEABS}' 241 | table.remove(arg, 1) 242 | return assert(loadfile '${TESTFILEABS}')(unpack(arg)) 243 | " ) 244 | if ( _ARG_WORKING_DIRECTORY ) 245 | get_filename_component ( 246 | TESTCURRENTDIRABS ${_ARG_WORKING_DIRECTORY} ABSOLUTE ) 247 | # note: CMake 2.6 (unlike 2.8) lacks WORKING_DIRECTORY parameter. 248 | set ( _pre ${CMAKE_COMMAND} -E chdir "${TESTCURRENTDIRABS}" ) 249 | endif () 250 | file ( WRITE ${TESTWRAPPER} ${TESTWRAPPERSOURCE}) 251 | add_test ( NAME ${TESTFILEBASE} COMMAND ${_pre} ${LUA} 252 | ${TESTWRAPPER} "${CMAKE_CFG_INTDIR}" 253 | ${_ARG_DEFAULT_ARGS} ) 254 | endif () 255 | # see also http://gdcm.svn.sourceforge.net/viewvc/gdcm/Sandbox/CMakeModules/UsePythonTest.cmake 256 | # Note: ${CMAKE_CFG_INTDIR} is a command-line argument to allow proper 257 | # expansion by the native build tool. 258 | endmacro () 259 | 260 | 261 | # Converts Lua source file `_source` to binary string embedded in C source 262 | # file `_target`. Optionally compiles Lua source to byte code (not available 263 | # under LuaJIT2, which doesn't have a bytecode loader). Additionally, Lua 264 | # versions of bin2c [1] and luac [2] may be passed respectively as additional 265 | # arguments. 266 | # 267 | # [1] http://lua-users.org/wiki/BinToCee 268 | # [2] http://lua-users.org/wiki/LuaCompilerInLua 269 | function ( add_lua_bin2c _target _source ) 270 | find_program ( LUA NAMES lua lua.bat ) 271 | execute_process ( COMMAND ${LUA} -e "string.dump(function()end)" 272 | RESULT_VARIABLE _LUA_DUMP_RESULT ERROR_QUIET ) 273 | if ( NOT ${_LUA_DUMP_RESULT} ) 274 | SET ( HAVE_LUA_DUMP true ) 275 | endif () 276 | message ( "-- string.dump=${HAVE_LUA_DUMP}" ) 277 | 278 | if ( ARGV2 ) 279 | get_filename_component ( BIN2C ${ARGV2} ABSOLUTE ) 280 | set ( BIN2C ${LUA} ${BIN2C} ) 281 | else () 282 | find_program ( BIN2C NAMES bin2c bin2c.bat ) 283 | endif () 284 | if ( HAVE_LUA_DUMP ) 285 | if ( ARGV3 ) 286 | get_filename_component ( LUAC ${ARGV3} ABSOLUTE ) 287 | set ( LUAC ${LUA} ${LUAC} ) 288 | else () 289 | find_program ( LUAC NAMES luac luac.bat ) 290 | endif () 291 | endif ( HAVE_LUA_DUMP ) 292 | message ( "-- bin2c=${BIN2C}" ) 293 | message ( "-- luac=${LUAC}" ) 294 | 295 | get_filename_component ( SOURCEABS ${_source} ABSOLUTE ) 296 | if ( HAVE_LUA_DUMP ) 297 | get_filename_component ( SOURCEBASE ${_source} NAME_WE ) 298 | add_custom_command ( 299 | OUTPUT ${_target} DEPENDS ${_source} 300 | COMMAND ${LUAC} -o ${CMAKE_CURRENT_BINARY_DIR}/${SOURCEBASE}.lo 301 | ${SOURCEABS} 302 | COMMAND ${BIN2C} ${CMAKE_CURRENT_BINARY_DIR}/${SOURCEBASE}.lo 303 | ">${_target}" ) 304 | else () 305 | add_custom_command ( 306 | OUTPUT ${_target} DEPENDS ${SOURCEABS} 307 | COMMAND ${BIN2C} ${_source} ">${_target}" ) 308 | endif () 309 | endfunction() 310 | -------------------------------------------------------------------------------- /dist.info: -------------------------------------------------------------------------------- 1 | name = "lua-log" 2 | version = "0.1.6" 3 | 4 | desc = "Asynchronous logging library" 5 | author = "Alexey Melnichuk" 6 | license = "MIT/X11" 7 | url = "https://github.com/moteus/lua-log" 8 | maintainer = "Alexey Melnichuk" 9 | 10 | depends = { 11 | "lua >= 5.1" 12 | } 13 | -------------------------------------------------------------------------------- /doc/doc.md: -------------------------------------------------------------------------------- 1 | 2 | ## Usage ## 3 | ```lua 4 | local host = arg[1] or '127.0.0.1' 5 | local port = arg[2] or 514 6 | 7 | -- this code run in separate thread. 8 | local InitWriter = [[ 9 | return require 'log.writer.list'.new( -- multi writers: 10 | require 'log.writer.console.color'.new(), -- * console color 11 | require 'log.writer.net.zmq'.new('%{zmq_cnn_host}'), -- * zmq pub socket 12 | require "log.writer.format".new( -- * syslog over udp 13 | require "log.logformat.syslog".new("user"), 14 | require 'log.writer.net.udp'.new('%{udp_cnn_host}', %{udp_cnn_port}) 15 | ) 16 | ) 17 | ]] 18 | 19 | -- create async writer and run new work thread. 20 | -- communicate with work thread using zmq library 21 | local writer = require "log.writer.async.zmq".new( 22 | 'inproc://async.logger', 23 | InitWriter:gsub('%%{(.-)}', { 24 | zmq_cnn_host = 'tcp://' .. host .. ':' .. port; 25 | udp_cnn_host = host; 26 | udp_cnn_port = port; 27 | }) 28 | ) 29 | 30 | -- create new logger 31 | local LOG = require"log".new(writer) 32 | 33 | require "socket".sleep(0.5) -- net.zmq need time to connect 34 | 35 | LOG.fatal("can not allocate memory") 36 | LOG.error("file not found") 37 | LOG.warning("cache server is not started") 38 | LOG.info("new message is received") 39 | LOG.notice("message has 2 file") 40 | 41 | print("Press enter ...") io.flush() io.read() 42 | ``` 43 | 44 | ## log library ## 45 | 46 | `LVL` - enum of all supported log levels 47 | 48 | `LVL_NAMES` - array of all names of log levels 49 | 50 | `new([max_lvl,] writer [[,formatter] ,logformat])` create new `logger` object 51 | 52 | ### logger ### 53 | this object provide user interface to logging system 54 | 55 | `lvl` param can be number or string (eg. "error", "ERROR", log.LVL.ERROR) 56 | 57 | * `logger.log(lvl, ...)` - write log message using logger's formatter 58 | * `logger.dump(lvl, formatter, ...)` - write log message using fn as formatter 59 | * `logger.writer()` - return current writer 60 | * `logger.lvl()` - reutrn current log level 61 | * `logger.set_lvl(lvl)` - set current log level 62 | 63 | loggers writer methods have 2 forms: 64 | * `logger.XXX(lvl, ...)` - this writers use formatter setted by constructor(eg. `logger.error`) 65 | * `logger.XXX_dump(lvl, formatter, ...)` - this writers use explicit formatter (eg. `logger.trace_dump`) 66 | 67 | list of writer methods: 68 | * `emerg` / `emerg_dump` 69 | * `alert` / `alert_dump` 70 | * `fatal` / `fatal_dump` 71 | * `error` / `error_dump` 72 | * `warning` / `warning_dump` 73 | * `notice` / `notice_dump` 74 | * `info` / `info_dump` 75 | * `debug` / `debug_dump` 76 | * `trace` / `trace_dump` 77 | 78 | ### formatter ### 79 | convert all parameters passed to one of write methods (error/info/debug etc.) to string 80 | `XXX_dump` methods use explicit formatter. 81 | 82 | supported formatters: 83 | * `default`: use first argument only 84 | * `concat`: use table.concat 85 | * `format`: use string.format 86 | * `mix`: compatable with lualogging library 87 | 88 | ### logformat ### 89 | convert loglevel, datetime and message generated by formatter to string. 90 | logformat is called by writer not logger. 91 | 92 | supported formats: 93 | * `default`: make string like `datetime [LVL_NAME] message` 94 | * `proxy`: serialize args to string to pass them to another thread/process/host 95 | * `syslog`: make valide syslog message 96 | 97 | ### writer ### 98 | write message generated by logformat to storage. 99 | 100 | supported writers: 101 | * `net.*` syncronus writers that pass message across hosts (`udp`, `smtp`, `zmq.pub`, `zmq.push`, `zmq.srv.pub') 102 | * `file.*` syncronus writers that write message to file (`roll`, `by_day`). 103 | all this writers use common `file` writer. 104 | * `async.*` asyncronus writers that pass message to other thread/process/host 105 | this writers can create new thread or use existed. (`udp`,`zmq`,`lane`) 106 | * `async.server.*` this is not writers, but just way to start some syncronus writer in separate thread. 107 | for more detail see implementation of `async.*` writer. 108 | * `list` this writer provide way to pass message to multiple writers 109 | * `format` this writer provide way to set specific logformat for writer 110 | this can be used to combine syslog logformat and net.udp writer. 111 | * `filter.*` this writer provide way to filter some type of message(`lvl.eq`, `lvl.le`) 112 | -------------------------------------------------------------------------------- /examples/async.lua: -------------------------------------------------------------------------------- 1 | local writer = require "log.writer.list".new( 2 | -- write to console from main thread 3 | require "log.writer.console.color".new(), 4 | -- write to file from separate thread 5 | -- require "log.writer.async.udp".new('127.0.0.1', 5555, 6 | require "log.writer.async.zmq".new('inproc://async.logger', 7 | "return require 'log.writer.file.by_day'.new('./logs', 'events.log', 5000)" 8 | ) 9 | ) 10 | 11 | local LOG = require"log".new(writer) 12 | 13 | LOG.fatal("can not allocate memory") 14 | LOG.error("file not found") 15 | LOG.warning("cache server is not started") 16 | LOG.info("new message is received") 17 | LOG.notice("message has 2 file") 18 | 19 | print("Press enter ...") io.flush() io.read() 20 | -------------------------------------------------------------------------------- /examples/async_lane.lua: -------------------------------------------------------------------------------- 1 | local writer = require "log.writer.list".new( 2 | -- write to console from main thread 3 | require "log.writer.console.color".new(), 4 | -- write to file from separate thread 5 | require "log.writer.async.lane".new('channel.log', 6 | "return require 'log.writer.file.by_day'.new('./logs', 'events.log', 5000)" 7 | ) 8 | ) 9 | 10 | local LOG = require"log".new(writer) 11 | LOG.fatal("can not allocate memory") 12 | LOG.error("file not found") 13 | LOG.warning("cache server is not started") 14 | LOG.info("new message is received") 15 | LOG.notice("message has 2 file") 16 | print("Press enter ...") io.flush() io.read() 17 | -------------------------------------------------------------------------------- /examples/async_net.lua: -------------------------------------------------------------------------------- 1 | local host = arg[1] or '127.0.0.1' 2 | local port = arg[2] or 514 3 | 4 | local InitWriter = [[ 5 | return require 'log.writer.list'.new( 6 | require 'log.writer.console'.new() 7 | ,require 'log.writer.net.udp'.new('%{udp_cnn_host}', %{udp_cnn_port}) 8 | ,require 'log.writer.net.zmq'.new('%{zmq_cnn_host}') 9 | ) 10 | ]] 11 | 12 | local writer = require "log.writer.async.zmq".new('inproc://async.logger', 13 | InitWriter:gsub('%%{(.-)}', { 14 | zmq_cnn_host = 'tcp://' .. host .. ':' .. port; 15 | udp_cnn_host = host; 16 | udp_cnn_port = port; 17 | }) 18 | ) 19 | require "socket".sleep(0.5) -- net.zmq need time to connect 20 | 21 | local LOG = require"log".new(nil, writer) 22 | 23 | LOG.fatal("can not allocate memory") 24 | LOG.error("file not found") 25 | LOG.warning("cache server is not started") 26 | LOG.info("new message is received") 27 | LOG.notice("message has 2 file") 28 | 29 | print("Press enter ...") io.flush() io.read() -------------------------------------------------------------------------------- /examples/async_proxy.lua: -------------------------------------------------------------------------------- 1 | local ok, zmq, zthreads 2 | ok, zmq = pcall(require, "lzmq") 3 | if ok then 4 | ok, zthreads = pcall (require, "lzmq.threads") 5 | if not ok then zthreads = nil end 6 | else 7 | zmq = require "zmq" 8 | ok, zthreads = pcall (require, "zmq.threads") 9 | if not ok then zthreads = nil end 10 | end 11 | 12 | local ctx = zmq.init(1) 13 | 14 | -- create log thread 15 | local writer = require "log.writer.async.zmq".new(ctx, 'inproc://async.logger', [[ 16 | return require'log.writer.list'.new( 17 | require 'log.writer.console.color'.new(), 18 | require 'log.writer.file.by_day'.new('./logs', 'events.log', 5000) 19 | ) 20 | ]]) 21 | local LOG = require"log".new(writer) 22 | 23 | -- log from separate thread via proxy 24 | local Thread = [[ 25 | local LOG = require"log".new( 26 | require "log.writer.async.zmq".new('inproc://async.logger') 27 | ) 28 | 29 | LOG.fatal("(Thread) can not allocate memory") 30 | LOG.error("(Thread) file not found") 31 | LOG.warning("(Thread) cache server is not started") 32 | LOG.info("(Thread) new message is received") 33 | LOG.notice("(Thread) message has 2 file") 34 | ]] 35 | 36 | local child_thread = zthreads.run(ctx, Thread) 37 | child_thread:start() 38 | 39 | LOG.fatal("can not allocate memory") 40 | LOG.error("file not found") 41 | LOG.warning("cache server is not started") 42 | LOG.info("new message is received") 43 | LOG.notice("message has 2 file") 44 | child_thread:join() 45 | 46 | print("Press enter ...") io.flush() io.read() 47 | -------------------------------------------------------------------------------- /examples/basic.lua: -------------------------------------------------------------------------------- 1 | local log = require "log".new() 2 | 3 | log.error('Hello') 4 | log.warning(', ') 5 | log.info('world') 6 | log.notice('!!!') 7 | log.debug('Does not displayed') 8 | -------------------------------------------------------------------------------- /examples/dump.lua: -------------------------------------------------------------------------------- 1 | local LOG = require"log".new('DEBUG', 2 | require "log.writer.console.color".new(), 3 | require "log.formatter.mix".new() 4 | ) 5 | 6 | local pretty = require "pl.pretty" 7 | local dump = function(msg, var) return msg .. '\n' .. pretty.write(var) end 8 | 9 | local context = { 10 | private_ = { 11 | srv = '127.0.0.1'; 12 | } 13 | } 14 | 15 | -- this works for all formatters 16 | LOG.debug_dump(dump,"try connect to server ...",context) 17 | 18 | -- this works only for mix formatter 19 | LOG.debug(dump, "try connect to server ...", context) 20 | LOG.debug('%s, %s', 'hello', 'world') 21 | 22 | print("Press enter ...")io.flush() 23 | io.read() -------------------------------------------------------------------------------- /examples/email.lua: -------------------------------------------------------------------------------- 1 | local writer = require "log.writer.net.smtp".new( 2 | 'program@some.mail', 'alex@some.mail', '127.0.0.1', 'test logger' 3 | ) 4 | local LOG = require "log".new(writer) 5 | LOG.fatal('test message') 6 | -------------------------------------------------------------------------------- /examples/filter.lua: -------------------------------------------------------------------------------- 1 | local LOG = require"log".new( 2 | require "log.writer.list".new( 3 | require "log.writer.filter".new('warning', 4 | require "log.writer.console.color".new() 5 | ), 6 | require "log.writer.file.by_day".new('./logs', 'events.log', 5000) 7 | ) 8 | ) 9 | 10 | LOG.fatal("can not allocate memory") 11 | LOG.error("file not found") 12 | LOG.warning("cache server is not started") 13 | LOG.info("new message is received") 14 | LOG.notice("message has 2 file") 15 | 16 | print("Press enter ...")io.flush() 17 | io.read() -------------------------------------------------------------------------------- /examples/multi.lua: -------------------------------------------------------------------------------- 1 | local LOG = require"log".new( 2 | require "log.writer.list".new( 3 | require "log.writer.console".new(), 4 | require "log.writer.file.by_day".new('./logs', 'events.log', 5000) 5 | ) 6 | ) 7 | 8 | LOG.fatal("can not allocate memory") 9 | LOG.error("file not found") 10 | LOG.warning("cache server is not started") 11 | LOG.info("new message is received") 12 | LOG.notice("message has 2 file") 13 | 14 | print("Press enter ...")io.flush() 15 | io.read() -------------------------------------------------------------------------------- /examples/multithread/main.lua: -------------------------------------------------------------------------------- 1 | -- configure application to be able load logger 2 | local config = require "myapp.config" 3 | config.multithread = true 4 | config.main_thread = true 5 | ---------------------------------------------- 6 | 7 | -- here we already can use logger 8 | local log = require "myapp.log" 9 | local zthreads = require "lzmq.threads" 10 | 11 | -- this function requires to configure child thread 12 | local function init_thread(...) 13 | require "myapp.config".multithread = true 14 | return ... 15 | end 16 | 17 | local function create_thread(fn, ...) 18 | return zthreads.xrun({fn, prelude = init_thread}, ...) 19 | end 20 | 21 | -- Real worker thread function. It can be just separate Lua file 22 | local function worker(id) 23 | local config = require "myapp.config" 24 | local log = require "myapp.log" 25 | 26 | local is_child_thread = config.multithread and not config.main_thread 27 | 28 | if is_child_thread then 29 | log.info('run worker #%d in child thread', id) 30 | else 31 | log.info('run worker #%d in main thread', id) 32 | end 33 | 34 | for i = 1, 10 do 35 | log.notice('Hello from worker #%d', id) 36 | end 37 | end 38 | 39 | log.info('application running') 40 | 41 | local thread = create_thread(worker, 2):start() 42 | 43 | worker(1) 44 | 45 | thread:join() 46 | -------------------------------------------------------------------------------- /examples/multithread/myapp/config.lua: -------------------------------------------------------------------------------- 1 | return { 2 | } -------------------------------------------------------------------------------- /examples/multithread/myapp/log.lua: -------------------------------------------------------------------------------- 1 | local config = require "myapp.config" 2 | 3 | 4 | -- config.multithread - indicate that we run application as multithreades 5 | -- so we have to run one backgroud thread and run our writer there. 6 | -- 7 | -- config.main_thread - indicate either it main thread in multithreaded application 8 | -- we need run background thread only once. so we should do this from main thread 9 | -- when it load this library at first time. main thread have to load this module 10 | -- before it crate any child threads. 11 | 12 | -- 13 | -- Note about async writers. If you whant use zmq inproc transport to communicate with 14 | -- backgroud thread you have to share same zmq context among all threads. If you use 15 | -- lzmq.threads module to run your threads it done automatically. 16 | -- If you want use any other thread library easy way just use `ipc` or `tcp` transport 17 | -- Windows system does not support `ipc` transport 18 | local async_writer_type = 'log.writer.async.zmq' 19 | local async_endpoint = 'inproc://async.logger' 20 | 21 | -- if you do not use lzmq.threads/zmq.threads library use this transport 22 | -- local async_endpoint = 'tcp://127.0.0.1:5555' 23 | 24 | -- this function build writer. It can be called from work thread 25 | -- so we can not use any upvalue there. 26 | local function build_writer() 27 | local config = require "myapp.config" 28 | 29 | -- we can use `config` to build writer here. 30 | -- but for example we make just stdout writer. 31 | 32 | local writer = require "log.writer.list".new( 33 | require 'log.writer.stdout'.new() 34 | ) 35 | 36 | return writer 37 | end 38 | 39 | -- configure log writer 40 | local log_writer 41 | if config.multithread then 42 | -- in multithreaded application we use backgroud log writer thread 43 | -- and communicate with this thread via zmq. 44 | 45 | if config.main_thread then 46 | -- create writer and start background thread 47 | log_writer = require(async_writer_type).new(async_endpoint, build_writer) 48 | else 49 | -- create writer and attach it to existed writer thread 50 | log_writer = require(async_writer_type).new(async_endpoint) 51 | end 52 | else 53 | -- this is just single threaded application so we can use writer directly 54 | log_writer = build_writer() 55 | end 56 | 57 | 58 | -- build logger object. 59 | local log = require "log".new(log_writer, require "log.formatter.mix".new()) 60 | 61 | return log -------------------------------------------------------------------------------- /examples/net.lua: -------------------------------------------------------------------------------- 1 | local host = arg[1] or '127.0.0.1' 2 | local port = arg[2] or 514 3 | 4 | local zmq_cnn_host = 'tcp://' .. host .. ':' .. port 5 | local udp_cnn_host = host 6 | local udp_cnn_port = port 7 | 8 | 9 | local LOG = require"log".new( 10 | require "log.writer.list".new( 11 | require "log.writer.console".new() 12 | ,require "log.writer.net.udp".new(udp_cnn_host, udp_cnn_port) 13 | ,require "log.writer.net.zmq".new(zmq_cnn_host) 14 | ) 15 | ) 16 | 17 | require"socket".sleep(1) -- zmq need time for connect 18 | 19 | LOG.fatal("can not allocate memory") 20 | LOG.error("file not found") 21 | LOG.warning("cache server is not started") 22 | LOG.info("new message is received") 23 | LOG.notice("message has 2 file") 24 | 25 | print("Press enter ...")io.flush() 26 | io.read() -------------------------------------------------------------------------------- /examples/proxy.lua: -------------------------------------------------------------------------------- 1 | local host = arg[1] or '127.0.0.1' 2 | local port = arg[2] or 514 3 | 4 | local zmq_cnn_host = 'tcp://' .. host .. ':' .. port 5 | local udp_cnn_host = host 6 | local udp_cnn_port = port 7 | 8 | local LOG = require"log".new( 9 | require "log.writer.list".new( 10 | require "log.writer.async.zmq".new(zmq_cnn_host), 11 | require "log.writer.async.udp".new(udp_cnn_host, udp_cnn_port) 12 | ) 13 | ) 14 | require "socket".sleep(0.5) 15 | 16 | LOG.fatal("can not allocate memory") 17 | LOG.error("file not found") 18 | LOG.warning("cache server is not started") 19 | LOG.info("new message is received") 20 | LOG.notice("message has 2 file") 21 | 22 | -------------------------------------------------------------------------------- /examples/server_lane.lua: -------------------------------------------------------------------------------- 1 | -- Run two writers in separate threads 2 | -- and spread log messages between them 3 | 4 | local Server = require "log.writer.async.server.lane" 5 | 6 | Server.run("inproc://main.logger", 7 | "return require 'log.writer.file.by_day'.new('./logs', 'events1.log', 6000)" 8 | ) 9 | 10 | Server.run("inproc://main.logger", 11 | "return require 'log.writer.file.by_day'.new('./logs', 'events2.log', 6000)" 12 | ) 13 | 14 | local LOG = require"log".new( 15 | require "log.writer.async.lane".new( 16 | "inproc://main.logger" 17 | ) 18 | ) 19 | 20 | for i = 1, 10000 do 21 | LOG.info(i) 22 | end 23 | 24 | 25 | print("Press enter ...") io.flush() io.read() -------------------------------------------------------------------------------- /examples/server_zmq.lua: -------------------------------------------------------------------------------- 1 | -- Run two writers in separate threads 2 | -- and spread log messages between them 3 | 4 | local Server = require "log.writer.async.server.zmq" 5 | 6 | Server.run("inproc://main.logger1", 7 | "return require 'log.writer.file.by_day'.new('./logs', 'events1.log', 6000)" 8 | ) 9 | 10 | Server.run("inproc://main.logger2", 11 | "return require 'log.writer.file.by_day'.new('./logs', 'events2.log', 6000)" 12 | ) 13 | 14 | local LOG = require"log".new( 15 | require "log.writer.async.zmq".new{ 16 | "inproc://main.logger1", 17 | "inproc://main.logger2", 18 | } 19 | ) 20 | 21 | for i = 1, 10000 do 22 | LOG.info(i) 23 | end 24 | 25 | 26 | print("Press enter ...") io.flush() io.read() -------------------------------------------------------------------------------- /examples/simple.lua: -------------------------------------------------------------------------------- 1 | print(require "ansicolors") 2 | local LOG = require"log".new('trace', 3 | require "log.writer.console.color".new() 4 | ) 5 | 6 | LOG.emerg("can not allocate memory") 7 | LOG.alert("can not allocate memory") 8 | LOG.fatal("can not allocate memory") 9 | LOG.error("file not found") 10 | LOG.warning("cache server is not started") 11 | LOG.notice("message has 2 file") 12 | LOG.info("new message is received") 13 | LOG.debug("message has 2 file") 14 | LOG.trace("message has 2 file") 15 | 16 | print("Press enter ...")io.flush() 17 | io.read() -------------------------------------------------------------------------------- /examples/simple_format.lua: -------------------------------------------------------------------------------- 1 | local writer = require "log.writer.console.color".new() 2 | 3 | local LOG = require"log".new(writer, 4 | require "log.formatter.concat".new(' ') 5 | ) 6 | 7 | local LOG_FMT = require"log".new(writer, 8 | require "log.formatter.format".new() 9 | ) 10 | 11 | LOG.info('new', 'message', 'is', 'received') 12 | 13 | LOG_FMT.notice("message has %d %s", 2, 'file') 14 | 15 | print("Press enter ...")io.flush() 16 | io.read() -------------------------------------------------------------------------------- /examples/syslog.lua: -------------------------------------------------------------------------------- 1 | local LogLib = require"log" 2 | local Format = require "log.writer.format" 3 | local SysLog = require "log.logformat.syslog" 4 | 5 | local writer = require "log.writer.list".new( 6 | Format.new( -- explicit set logformat to console 7 | require "log.logformat.default".new(), 8 | require "log.writer.stdout".new() 9 | ), 10 | require "log.writer.net.udp".new('127.0.0.1', 514) 11 | ) 12 | 13 | local LOG_FMT = LogLib.new('trace', writer, 14 | require "log.formatter.format".new(), 15 | SysLog.new('kern') 16 | ) 17 | 18 | local LOG_CON = LogLib.new('trace', writer, 19 | require "log.formatter.concat".new(), 20 | SysLog.new('USER') 21 | ) 22 | 23 | local LOG = LogLib.new('trace', writer, nil, SysLog.new('USER')) 24 | 25 | LOG.emerg ('!! EMERG !!') 26 | LOG_FMT.alert ('!! %-7s !!', 'ALERT' ) 27 | LOG_FMT.fatal ('!! %-7s !!', 'FATAL' ) 28 | LOG_FMT.error ('!! %-7s !!', 'ERROR' ) 29 | LOG_CON.warning ('!!', 'WARNING', '!!') 30 | LOG_FMT.notice ('!! %-7s !!', 'NOTICE' ) 31 | LOG_FMT.info ('!! %-7s !!', 'INFO' ) 32 | LOG_FMT.debug ('!! %-7s !!', 'DEBUG' ) 33 | LOG_FMT.trace ('!! %-7s !!', 'TRACE' ) 34 | -------------------------------------------------------------------------------- /lakeconfig.lua: -------------------------------------------------------------------------------- 1 | local io = require "io" 2 | io.stdout:setvbuf"no" 3 | io.stderr:setvbuf"no" 4 | 5 | function vc_version() 6 | local VER = lake.compiler_version() 7 | MSVC_VER = ({ 8 | [15] = '9'; 9 | [16] = '10'; 10 | })[VER.MAJOR] or '' 11 | return MSVC_VER 12 | end 13 | 14 | if not L then 15 | 16 | local function arkey(t) 17 | assert(type(t) == 'table') 18 | local keys = {} 19 | for k in pairs(t) do 20 | assert(type(k) == 'number') 21 | table.insert(keys, k) 22 | end 23 | table.sort(keys) 24 | return keys 25 | end 26 | 27 | local function ikeys(t) 28 | local keys = arkey(t) 29 | local i = 0 30 | return function() 31 | i = i + 1 32 | local k = keys[i] 33 | if k == nil then return end 34 | return k, t[k] 35 | end 36 | end 37 | 38 | local function expand(arr, t) 39 | if t == nil then return arr end 40 | 41 | if type(t) ~= 'table' then 42 | table.insert(arr, t) 43 | return arr 44 | end 45 | 46 | for _, v in ikeys(t) do 47 | expand(arr, v) 48 | end 49 | 50 | return arr 51 | end 52 | 53 | function L(...) 54 | return expand({}, {...}) 55 | end 56 | 57 | end 58 | 59 | J = J or path.join 60 | 61 | IF = IF or lake.choose or choose 62 | 63 | DIR_SEP = package.config:sub(1,1) 64 | 65 | function prequire(...) 66 | local ok, mod = pcall(require, ...) 67 | if ok then return mod end 68 | end 69 | 70 | function clone(t, o) 71 | o = o or {} 72 | for k, v in pairs(t) do 73 | if o[k] == nil then o[k] = v end 74 | end 75 | return o 76 | end 77 | 78 | function each_join(dir, list) 79 | for i, v in ipairs(list) do 80 | list[i] = path.join(dir, v) 81 | end 82 | return list 83 | end 84 | 85 | function run(file, cwd) 86 | print() 87 | print("run " .. file) 88 | if not TESTING then 89 | if cwd then lake.chdir(cwd) end 90 | local status, code = utils.execute( LUA_RUNNER .. ' ' .. file ) 91 | if cwd then lake.chdir("<") end 92 | print() 93 | return status, code 94 | end 95 | return true, 0 96 | end 97 | 98 | function exec(file, cwd) 99 | print() 100 | print("exec " .. file) 101 | if not TESTING then 102 | if cwd then lake.chdir(cwd) end 103 | local status, code = utils.execute( file ) 104 | if cwd then lake.chdir("<") end 105 | print() 106 | return status, code 107 | end 108 | return true, 0 109 | end 110 | 111 | local TESTS = {} 112 | 113 | function run_test(name, params) 114 | local test_dir = TESTDIR or J(ROOT, 'test') 115 | local cmd = J(test_dir, name) 116 | if params then cmd = cmd .. ' ' .. params end 117 | local ok = run(cmd, test_dir) 118 | 119 | table.insert(TESTS, {cmd = cmd, result = ok}) 120 | 121 | print("TEST " .. name .. (ok and ' - pass!' or ' - fail!')) 122 | end 123 | 124 | function exec_test(name, params) 125 | local test_dir = TESTDIR or J(ROOT, 'test') 126 | local cmd = J(test_dir, name) 127 | if params then cmd = cmd .. ' ' .. params end 128 | local ok = exec(cmd, test_dir) 129 | 130 | table.insert(TESTS, {cmd = cmd, result = ok}) 131 | 132 | print("TEST " .. name .. (ok and ' - pass!' or ' - fail!')) 133 | end 134 | 135 | function test_summary() 136 | local ok = true 137 | print("") 138 | print("------------------------------------") 139 | print("Number of tests:", #TESTS) 140 | for _, t in ipairs(TESTS) do 141 | ok = ok and t.result 142 | print((t.result and ' Pass' or ' Fail') .. " - TEST " .. t.cmd) 143 | end 144 | print("------------------------------------") 145 | print("") 146 | return ok 147 | end 148 | 149 | --[[spawn]] if WINDOWS then 150 | function spawn(file, cwd) 151 | local winapi = prequire "winapi" 152 | if not winapi then 153 | quit('needs winapi for spawn!') 154 | return false 155 | end 156 | 157 | print("spawn " .. file) 158 | if not TESTING then 159 | if cwd then lake.chdir(cwd) end 160 | assert(winapi.shell_exec(nil, LUA_RUNNER, file, cwd)) 161 | if cwd then lake.chdir("<") end 162 | print() 163 | end 164 | return true 165 | end 166 | else 167 | function spawn(file, cwd) 168 | print("spawn " .. file) 169 | if not TESTING then 170 | assert(run(file .. ' &', cwd)) 171 | end 172 | return true 173 | end 174 | end 175 | 176 | function as_bool(v,d) 177 | if v == nil then return not not d end 178 | local n = tonumber(v) 179 | if n == 0 then return false end 180 | if n then return true end 181 | return false 182 | end 183 | 184 | --- set global variables 185 | -- LUA_NEED 186 | -- LUA_DIR 187 | -- LUA_RUNNER 188 | -- ROOT 189 | -- LUADIR 190 | -- LIBDIR 191 | -- TESTDIR 192 | -- DOCDIR 193 | -- DYNAMIC 194 | function INITLAKEFILE() 195 | if LUA_VER == '5.3' then 196 | LUA_NEED = 'lua53' 197 | LUA_DIR = ENV.LUA_DIR_5_3 or ENV.LUA_DIR 198 | LUA_RUNNER = LUA_RUNNER or 'lua53' 199 | elseif LUA_VER == '5.2' then 200 | LUA_NEED = 'lua52' 201 | LUA_DIR = ENV.LUA_DIR_5_2 or ENV.LUA_DIR 202 | LUA_RUNNER = LUA_RUNNER or 'lua52' 203 | elseif LUA_VER == '5.1' then 204 | LUA_NEED = 'lua51' 205 | LUA_DIR = ENV.LUA_DIR 206 | LUA_RUNNER = LUA_RUNNER or 'lua' 207 | else 208 | LUA_NEED = 'lua' 209 | LUA_DIR = ENV.LUA_DIR 210 | LUA_RUNNER = LUA_RUNNER or 'lua' 211 | end 212 | ROOT = ROOT or J( LUA_DIR, 'libs', PROJECT ) 213 | LUADIR = LUADIR or J( ROOT, 'share' ) 214 | LIBDIR = LIBDIR or J( ROOT, 'share' ) 215 | TESTDIR = TESTDIR or J( ROOT, 'test' ) 216 | DOCDIR = DOCDIR or J( ROOT, 'doc' ) 217 | DYNAMIC = as_bool(DYNAMIC, false) 218 | end 219 | 220 | ----------------------- 221 | -- needs -- 222 | ----------------------- 223 | 224 | lake.define_need('lua53', function() 225 | return { 226 | incdir = J(ENV.LUA_DIR_5_3, 'include'); 227 | libdir = J(ENV.LUA_DIR_5_3, 'lib'); 228 | libs = {'lua53'}; 229 | } 230 | end) 231 | 232 | lake.define_need('lua52', function() 233 | return { 234 | incdir = J(ENV.LUA_DIR_5_2, 'include'); 235 | libdir = J(ENV.LUA_DIR_5_2, 'lib'); 236 | libs = {'lua52'}; 237 | } 238 | end) 239 | 240 | lake.define_need('lua51', function() 241 | return { 242 | incdir = J(ENV.LUA_DIR, 'include'); 243 | libdir = J(ENV.LUA_DIR, 'lib'); 244 | libs = {'lua5.1'}; 245 | } 246 | end) 247 | -------------------------------------------------------------------------------- /lakefile: -------------------------------------------------------------------------------- 1 | PROJECT = 'log' 2 | 3 | INITLAKEFILE() 4 | 5 | install = target('install', { 6 | file.group{odir = LUADIR; recurse = true; src = J('lua', '*.*' )}; 7 | file.group{odir = TESTDIR; recurse = true; src = J('test', '*.*' )}; 8 | 9 | file.group{odir = J(ROOT, 'examples');recurse = true; src = J('examples', '*.*')}; 10 | file.group{odir = J(ROOT, 'utils' );recurse = true; src = J('utils', '*.*' )}; 11 | }) 12 | 13 | target('test', install, function() 14 | run_test('test_basic.lua') 15 | run_test('test_file_writer.lua') 16 | run_test('test_zmq_finalizer.lua') 17 | if not test_summary() then 18 | quit("test fail") 19 | end 20 | end) 21 | -------------------------------------------------------------------------------- /lua/log.lua: -------------------------------------------------------------------------------- 1 | --- 2 | -- @module log 3 | -- 4 | 5 | local _COPYRIGHT = "Copyright (C) 2013-2016 Alexey Melnichuk"; 6 | local _VERSION = "0.1.7-dev" 7 | 8 | local table = require "table" 9 | local string = require "string" 10 | local date = require "date" 11 | 12 | local destroy_list = {} 13 | local loggers_list = setmetatable({},{__mode = 'k'}) 14 | local emptyfn = function() end 15 | 16 | local LOG_LVL = { 17 | EMERG = 1; 18 | ALERT = 2; 19 | FATAL = 3; 20 | ERROR = 4; 21 | WARNING = 5; 22 | NOTICE = 6; 23 | INFO = 7; 24 | DEBUG = 8; 25 | TRACE = 9; 26 | } 27 | 28 | local writer_names = {} 29 | local LOG_LVL_NAMES = {} 30 | for k,v in pairs(LOG_LVL) do 31 | LOG_LVL_NAMES[v] = k 32 | writer_names[v] = k:lower() 33 | end 34 | 35 | local LOG_LVL_COUNT = #LOG_LVL_NAMES 36 | 37 | local function lvl2number(lvl) 38 | if type(lvl) == 'number' then return lvl end 39 | if type(lvl) == 'string' then 40 | lvl = lvl:upper() 41 | local n 42 | if lvl == 'NONE' then n = 0 else n = LOG_LVL[ lvl ] end 43 | if not n then return nil, "unknown log level: '" .. lvl .. "'" end 44 | return n 45 | end 46 | 47 | return nil, 'unsupported log leve type: ' .. type(lvl) 48 | end 49 | 50 | local Log = {} 51 | Log._COPYRIGHT = _COPYRIGHT 52 | Log._NAME = "log" 53 | Log._VERSION = _VERSION 54 | Log._LICENSE = "MIT" 55 | 56 | Log.LVL = LOG_LVL 57 | Log.LVL_NAMES = LOG_LVL_NAMES 58 | 59 | Log.lvl2number = lvl2number 60 | 61 | function Log.new(max_lvl, writer, formatter, logformat) 62 | if max_lvl and type(max_lvl) ~= 'number' and type(max_lvl) ~= 'string' then 63 | max_lvl, writer, formatter, logformat = nil, max_lvl, writer, formatter 64 | end 65 | 66 | max_lvl = assert(lvl2number ( max_lvl or LOG_LVL.INFO ) ) 67 | 68 | if not writer then 69 | writer = require"log.writer.stdout".new() 70 | end 71 | 72 | if not formatter then 73 | formatter = require"log.formatter.default".new() 74 | end 75 | 76 | if not logformat then 77 | logformat = require"log.logformat.default".new() 78 | end 79 | 80 | local write = function (lvl, ... ) 81 | local now = date() 82 | writer( logformat, formatter(...), lvl, now ) 83 | end; 84 | 85 | local dump = function(lvl, fn, ...) 86 | local now = date() 87 | writer( logformat, (fn(...) or ''), lvl, now ) 88 | end 89 | 90 | local logger = {} 91 | 92 | function logger.writer() return writer end 93 | 94 | function logger.formatter() return formatter end 95 | 96 | function logger.format() return logformat end 97 | 98 | function logger.lvl() return max_lvl end 99 | 100 | function logger.set_writer(value) 101 | assert(value) 102 | writer, value = value, writer 103 | return value 104 | end 105 | 106 | function logger.set_formatter(value) 107 | assert(value) 108 | formatter, value = value, formatter 109 | return value 110 | end 111 | 112 | function logger.set_format(value) 113 | assert(value) 114 | logformat, value = value, logformat 115 | return value 116 | end 117 | 118 | function logger.log(lvl, ...) 119 | local err lvl, err = lvl2number(lvl) 120 | if not lvl then return nil, err end 121 | return write(lvl, ...) 122 | end 123 | 124 | function logger.dump(lvl, ...) 125 | local err lvl, err = lvl2number(lvl) 126 | if not lvl then return nil, err end 127 | return dump(lvl, ...) 128 | end 129 | 130 | function logger.set_lvl(lvl) 131 | local err lvl, err = lvl2number(lvl) 132 | if not lvl then return nil, err end 133 | max_lvl = lvl 134 | for i = 1, max_lvl do logger[ writer_names[i] ] = function(...) write(i, ...) end end 135 | for i = 1, max_lvl do logger[ writer_names[i] .. '_dump'] = function(...) dump(i, ...) end end 136 | for i = max_lvl+1, LOG_LVL_COUNT do logger[ writer_names[i] ] = emptyfn end 137 | for i = max_lvl+1, LOG_LVL_COUNT do logger[ writer_names[i] .. '_dump'] = emptyfn end 138 | return true 139 | end 140 | 141 | assert(logger.set_lvl(max_lvl)) 142 | 143 | loggers_list[logger] = true; 144 | 145 | return logger 146 | end 147 | 148 | function Log.add_cleanup(fn) 149 | assert(type(fn)=='function') 150 | for k,v in ipairs(destroy_list) do 151 | if v == fn then return end 152 | end 153 | table.insert(destroy_list, 1, fn) 154 | return fn 155 | end 156 | 157 | function Log.remove_cleanup(fn) 158 | for k,v in ipairs(destroy_list) do 159 | if v == fn then 160 | table.remove(destroy_list, k) 161 | break 162 | end 163 | end 164 | end 165 | 166 | function Log.close() 167 | for k,fn in ipairs(destroy_list) do pcall(fn) end 168 | for logger in pairs(loggers_list) do 169 | logger.fotal = emptyfn; 170 | logger.error = emptyfn; 171 | logger.warning = emptyfn; 172 | logger.info = emptyfn; 173 | logger.notice = emptyfn; 174 | logger.debug = emptyfn; 175 | logger.closed = true; 176 | loggers_list[logger] = nil 177 | end 178 | destroy_list = {} 179 | end 180 | 181 | return Log 182 | -------------------------------------------------------------------------------- /lua/log/formatter/concat.lua: -------------------------------------------------------------------------------- 1 | local table = require "table" 2 | 3 | local M = {} 4 | 5 | function M.new(sep) 6 | sep = sep or ' ' 7 | 8 | return function (...) 9 | local argc,argv = select('#', ...), {...} 10 | for i = 1, argc do argv[i] = tostring(argv[i]) end 11 | return (table.concat(argv, sep)) 12 | end 13 | end 14 | 15 | return M 16 | 17 | -------------------------------------------------------------------------------- /lua/log/formatter/default.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | function M.new() 4 | return function(msg) 5 | return msg 6 | end 7 | end 8 | 9 | return M -------------------------------------------------------------------------------- /lua/log/formatter/format.lua: -------------------------------------------------------------------------------- 1 | local string = require "string" 2 | 3 | local M = {} 4 | 5 | function M.new() return string.format end 6 | 7 | return M 8 | -------------------------------------------------------------------------------- /lua/log/formatter/mix.lua: -------------------------------------------------------------------------------- 1 | --- 2 | -- compatiable with lualogging 3 | -- 4 | 5 | local M = {} 6 | 7 | function M.new(default) 8 | if not default then 9 | default = require "log.formatter.format".new() 10 | end 11 | 12 | return function(...) 13 | if type((...)) == 'function' then 14 | return (...)(select(2, ...)) 15 | end 16 | 17 | if select('#', ...) < 2 then 18 | return tostring((...)) 19 | end 20 | 21 | return default(...) 22 | end 23 | end 24 | 25 | return M -------------------------------------------------------------------------------- /lua/log/formatter/pformat.lua: -------------------------------------------------------------------------------- 1 | local lpeg = require "lpeg" 2 | local table = require "table" 3 | local string = require "string" 4 | 5 | local unpack = unpack or table.unpack 6 | 7 | local HAS_A_FORMAT = pcall(string.format, '%a' , 10) 8 | 9 | local P, C, Cs, Ct, Cp, S, R = lpeg.P, lpeg.C, lpeg.Cs, lpeg.Ct, lpeg.Cp, lpeg.S, lpeg.R 10 | 11 | local any = P(1) 12 | local empty = P(0) 13 | 14 | local esc = P'%%' 15 | local flags = S'-+ #0' 16 | local digit = R'09' 17 | 18 | local fsym = S('cdiouxXeEfgGqs' .. (HAS_A_FORMAT and 'aA' or '')) 19 | local width = digit * digit + digit 20 | local precision = P'.' * (digit * digit + digit) 21 | local format = (flags + empty) * (width + empty) * (precision + empty) * (P'.' + empty) 22 | local valid_format = P'%' * format * fsym 23 | local valid_format_capture = Cs(valid_format) 24 | 25 | local any_fsym = any - (flags + digit + P'.') 26 | local any_format = P'%' * (flags + digit + P'.')^0 * any_fsym 27 | 28 | local types = { 29 | c = 'number'; 30 | d = 'number'; 31 | i = 'number'; 32 | o = 'number'; 33 | u = 'number'; 34 | x = 'number'; 35 | X = 'number'; 36 | a = 'number'; 37 | A = 'number'; 38 | e = 'number'; 39 | E = 'number'; 40 | f = 'number'; 41 | g = 'number'; 42 | G = 'number'; 43 | q = 'string'; 44 | s = 'string'; 45 | } 46 | 47 | local function safe_format(protect_only_args, fmt, ...) 48 | local n, args = 0, {...} 49 | 50 | local function fix_fmt(f) 51 | local fmt = valid_format_capture:match(f) 52 | 53 | if not fmt then 54 | if protect_only_args then return end 55 | return '%' .. f 56 | end 57 | 58 | local typ = string.sub(fmt, -1) 59 | 60 | n = n + 1 61 | 62 | if types[typ] ~= type( args[n] ) then 63 | args[n], fmt = tostring(args[n]), '%s' 64 | end 65 | 66 | return fmt 67 | end 68 | 69 | local pattern = Cs((esc + any_format / fix_fmt + any) ^ 0) 70 | fmt = pattern:match(fmt) 71 | 72 | return string.format(fmt, unpack(args, 1, n)) 73 | end 74 | 75 | local function buld_formatter(protect_only_args, no_warning) 76 | return function(...) 77 | local ok, msg = pcall(string.format, ...) 78 | if not ok then 79 | local err = msg 80 | msg = safe_format(protect_only_args, ...) 81 | if not no_warning then 82 | msg = msg .. ' - ' .. 'WARNING: Error formatting log message: ' .. err 83 | end 84 | end 85 | return msg 86 | end 87 | end 88 | 89 | return { 90 | new = buld_formatter 91 | } 92 | -------------------------------------------------------------------------------- /lua/log/logformat/default.lua: -------------------------------------------------------------------------------- 1 | local string = require "string" 2 | local Log = require "log" 3 | 4 | local sformat = string.format 5 | local function date_fmt(now) 6 | local Y, M, D = now:getdate() 7 | return sformat("%.4d-%.2d-%.2d %.2d:%.2d:%.2d", Y, M, D, now:gettime()) 8 | end 9 | 10 | local M = {} 11 | 12 | function M.new() 13 | return function (msg, lvl, now) 14 | return date_fmt(now) .. ' [' .. Log.LVL_NAMES[lvl] .. '] ' .. msg 15 | end 16 | end 17 | 18 | return M -------------------------------------------------------------------------------- /lua/log/logformat/proxy.lua: -------------------------------------------------------------------------------- 1 | local packer = require "log.logformat.proxy.pack" 2 | 3 | local M = {} 4 | 5 | function M.new() 6 | return function (now, lvl, msg) 7 | return packer.pack(now, lvl, msg) 8 | end 9 | end 10 | 11 | return M -------------------------------------------------------------------------------- /lua/log/logformat/proxy/pack.lua: -------------------------------------------------------------------------------- 1 | local Log = require "log" 2 | local date = require "date" 3 | local string = require "string" 4 | local schar = string.char 5 | local sbyte = string.byte 6 | local sformat = string.format 7 | local ssub = string.sub 8 | local tn = tonumber 9 | 10 | local M = {} 11 | 12 | function M.pack(msg, lvl, now) 13 | local Y, M, D = now:getdate() 14 | local h, m, s = now:gettime() 15 | local now_s = sformat("%.4d-%.2d-%.2d %.2d:%.2d:%.2d", Y, M, D, h, m, s) 16 | 17 | return schar(lvl) .. now_s .. msg 18 | end 19 | 20 | function M.unpack(str) 21 | local lvl = sbyte( ssub(str, 1, 1) ) 22 | if not Log.LVL_NAMES[lvl] then return end 23 | local now_s = ssub(str, 2, 20 ) 24 | local Y, M, D = ssub(str, 2, 5 ), ssub(str, 7, 8 ), ssub(str, 10, 11 ) 25 | local h, m, s = ssub(str, 13, 14 ), ssub(str, 16, 17 ), ssub(str, 19, 20 ) 26 | Y, M, D, h, m, s = tn(Y), tn(M), tn(D), tn(h), tn(m), tn(s) 27 | if not (Y and M and D and h and m and s) then return end 28 | 29 | return ssub(str, 21), lvl, date(Y, M, D, h, m, s) 30 | end 31 | 32 | return M -------------------------------------------------------------------------------- /lua/log/logformat/syslog.lua: -------------------------------------------------------------------------------- 1 | local string = require "string" 2 | local math = require "math" 3 | local Log = require "log" 4 | 5 | local mod,floor,ceil,abs,pow = math.fmod,math.floor,math.ceil,math.abs,math.pow 6 | local fmt = string.format 7 | 8 | -- removes the decimal part of a number 9 | local function fix(n) n = tonumber(n) return n and ((n > 0 and floor or ceil)(n)) end 10 | local function lshift(a,b) return a * math.pow(2, b) end 11 | 12 | --[[ RFC5424 13 | 0 Emergency: system is unusable 14 | 1 Alert: action must be taken immediately 15 | 2 Critical: critical conditions 16 | 3 Error: error conditions 17 | 4 Warning: warning conditions 18 | 5 Notice: normal but significant condition 19 | 6 Informational: informational messages 20 | 7 Debug: debug-level messages 21 | --]] 22 | local SEVERITY = { 23 | EMERG = 0; 24 | ALERT = 1; 25 | CRIT = 2; 26 | ERR = 3; 27 | WARNING = 4; 28 | NOTICE = 5; 29 | INFO = 6; 30 | DEBUG = 7; 31 | } 32 | 33 | --[[ RFC5424 34 | 0 kernel messages 35 | 1 user-level messages 36 | 2 mail system 37 | 3 system daemons 38 | 4 security/authorization messages 39 | 5 messages generated internally by syslogd 40 | 6 line printer subsystem 41 | 7 network news subsystem 42 | 8 UUCP subsystem 43 | 9 clock daemon 44 | 10 security/authorization messages 45 | 11 FTP daemon 46 | 12 NTP subsystem 47 | 13 log audit 48 | 14 log alert 49 | 15 clock daemon (note 2) 50 | 16 local use 0 (local0) 51 | 17 local use 1 (local1) 52 | 18 local use 2 (local2) 53 | 19 local use 3 (local3) 54 | 20 local use 4 (local4) 55 | 21 local use 5 (local5) 56 | 22 local use 6 (local6) 57 | 23 local use 7 (local7) 58 | --]] 59 | local FACILITY = { 60 | KERN = lshift (0, 3); 61 | USER = lshift (1, 3); 62 | MAIL = lshift (2, 3); 63 | DAEMON = lshift (3, 3); 64 | AUTH = lshift (4, 3); 65 | SYSLOG = lshift (5, 3); 66 | LPR = lshift (6, 3); 67 | NEWS = lshift (7, 3); 68 | UUCP = lshift (8, 3); 69 | CRON = lshift (9, 3); CLKD = lshift (9, 3); 70 | AUTHPRIV = lshift(10, 3); 71 | FTP = lshift(11, 3); 72 | NTP = lshift(12, 3); 73 | SECURITY = lshift(13, 3); AUDIT = lshift(13, 3); 74 | CONSOLE = lshift(14, 3); ALERT = lshift(14, 3); 75 | CLKD2 = lshift(15, 3); 76 | LOCAL0 = lshift(16, 3); 77 | LOCAL1 = lshift(17, 3); 78 | LOCAL2 = lshift(18, 3); 79 | LOCAL3 = lshift(19, 3); 80 | LOCAL4 = lshift(20, 3); 81 | LOCAL5 = lshift(21, 3); 82 | LOCAL6 = lshift(22, 3); 83 | LOCAL7 = lshift(23, 3); 84 | } 85 | 86 | local LVL2SYSLOG = { 87 | [ Log.LVL.EMERG ] = SEVERITY.EMERG; 88 | [ Log.LVL.ALERT ] = SEVERITY.ALERT; 89 | [ Log.LVL.FATAL ] = SEVERITY.CRIT; 90 | [ Log.LVL.ERROR ] = SEVERITY.ERR; 91 | [ Log.LVL.WARNING ] = SEVERITY.WARNING; 92 | [ Log.LVL.NOTICE ] = SEVERITY.NOTICE; 93 | [ Log.LVL.INFO ] = SEVERITY.INFO; 94 | [ Log.LVL.DEBUG ] = SEVERITY.DEBUG; 95 | [ Log.LVL.TRACE ] = SEVERITY.DEBUG; 96 | } 97 | 98 | local function Date2SysLog(now) 99 | local Y, M, D = now:getdate() 100 | local h, m, s = now:gettime() 101 | 102 | local b = -now:getbias(); 103 | local x = abs(b); 104 | 105 | return fmt("%.4d-%.2d-%.2dT%.2d:%.2d:%.2d%s%.2d:%.2d", Y, M, D, h, m, s, 106 | b < 0 and "-" or "+", fix(x/60), floor(mod(x,60)) 107 | ) 108 | end 109 | 110 | local M = {} 111 | 112 | function M.new(facility, host_name, app_name, procid, msgid) 113 | if not facility then facility = FACILITY.USER 114 | else facility = FACILITY[facility:upper()] end 115 | host_name = host_name or '-' 116 | app_name = app_name or '-' 117 | procid = procid or '-' 118 | msgid = msgid or '-' 119 | 120 | -- HOSTNAME APP-NAME PROCID MSGID 121 | local header =host_name .. ' ' .. app_name .. ' ' .. procid .. ' ' .. msgid 122 | 123 | return function (msg, lvl, now) 124 | local slvl = assert(LVL2SYSLOG[lvl]) 125 | return 126 | -- HEADER 127 | -- PRI VERSION TIMESTAMP 128 | '<' .. slvl + facility .. '>1 ' .. Date2SysLog(now) .. ' ' .. 129 | -- HEADER STRUCTURED-DATA MSG 130 | header .. ' - ' .. msg 131 | 132 | end 133 | end 134 | 135 | return M 136 | -------------------------------------------------------------------------------- /lua/log/writer/async/_private/server.lua: -------------------------------------------------------------------------------- 1 | local function prequire(...) 2 | local ok, mod = pcall(require, ...) 3 | return ok and mod, mod or nil 4 | end 5 | 6 | local llthreads = prequire "llthreads2" 7 | if not llthreads then 8 | llthreads = require "llthreads" 9 | end 10 | 11 | local runstring = function(code, ...) 12 | code = [[do 13 | local string = require "string" 14 | local os = require "os" 15 | local loadstring = loadstring or load 16 | local lua_init = os.getenv("lua_init") 17 | if lua_init and #lua_init > 0 then 18 | if lua_init:sub(1,1) == '@' then dofile((lua_init:sub(2))) 19 | else assert(loadstring(lua_init))() end 20 | end 21 | end;]] .. code 22 | return llthreads.new(code, ...) 23 | end 24 | 25 | local sleep do repeat 26 | local socket = prequire "socket" 27 | if socket then 28 | sleep = function(ms) socket.sleep(ms / 1000) end 29 | break 30 | end 31 | 32 | local ztimer = prequire "lzmq.timer" 33 | if ztimer then 34 | sleep = ztimer.sleep 35 | break 36 | end 37 | 38 | --@todo find another way (use os.execute("sleep " .. ms)) on *nix 39 | 40 | sleep = function() end 41 | break 42 | until true end 43 | 44 | local Worker = [=[ 45 | (function(server, maker, logformat, ...) 46 | local Log = require "log" 47 | local logformat = require(logformat).new() 48 | 49 | local loadstring = loadstring or load 50 | local writer = assert(loadstring(maker))() 51 | 52 | require(server).run(writer, Log.close, logformat, ...) 53 | end)(...) 54 | ]=] 55 | 56 | local function run_server(server, maker, logformat, ...) 57 | if type(maker) == 'function' then maker = string.dump(maker) end 58 | 59 | assert(type(server) == 'string') 60 | assert(type(maker) == 'string') 61 | assert(type(logformat) == 'string') 62 | 63 | local child_thread = assert(runstring(Worker, server, maker, logformat, ...)) 64 | child_thread:start(true, true) 65 | sleep(500) 66 | return 67 | end 68 | 69 | local Z 70 | local function run_zserver(server, maker, logformat, ctx, ...) 71 | Z = Z or require "log.writer.net.zmq._private.compat" 72 | 73 | if type(maker) == 'function' then maker = string.dump(maker) end 74 | 75 | assert(type(server) == 'string') 76 | assert(type(maker) == 'string') 77 | assert(type(logformat) == 'string') 78 | assert(Z.is_ctx(ctx)) 79 | 80 | local zthreads = assert(Z.threads) 81 | local child_thread = assert((zthreads.run or zthreads.runstring)(ctx, Worker, server, maker, logformat, ...)) 82 | child_thread:start(true) 83 | return 84 | end 85 | 86 | local M = {} 87 | 88 | M.run = run_server 89 | M.zrun = run_zserver 90 | 91 | return M -------------------------------------------------------------------------------- /lua/log/writer/async/lane.lua: -------------------------------------------------------------------------------- 1 | local server = require "log.writer.async.server.lane" 2 | local packer = require "log.logformat.proxy.pack" 3 | 4 | local function create_writer(channel, maker) 5 | if maker then 6 | server.run( channel, maker, "log.logformat.default" ) 7 | end 8 | 9 | local queue = server.channel() 10 | local pack = packer.pack 11 | 12 | return function(fmt, ...) 13 | local msg = pack(...) 14 | queue:send(channel, msg) 15 | end 16 | end 17 | 18 | local M = {} 19 | 20 | M.new = create_writer 21 | 22 | return M -------------------------------------------------------------------------------- /lua/log/writer/async/server/lane.lua: -------------------------------------------------------------------------------- 1 | local on_lane_create = function() 2 | local loadstring = loadstring or load 3 | local lua_init = os.getenv("lua_init") 4 | if lua_init and #lua_init > 0 then 5 | if lua_init:sub(1,1) == '@' then dofile(lua_init:sub(2)) 6 | else assert(loadstring(lua_init))() end 7 | end 8 | end 9 | 10 | local LOG = require "log" 11 | local lanes = require "lanes".configure{ 12 | with_timers = false, 13 | on_state_create = on_lane_create, 14 | } 15 | local pack = require "log.logformat.proxy.pack".pack 16 | 17 | local queue 18 | 19 | local function context() 20 | queue = queue or assert(lanes.linda()) 21 | return queue 22 | end 23 | 24 | local function log_thread_fn(maker, logformat, channel) 25 | local log_packer = require "log.logformat.proxy.pack" 26 | local logformat = require (logformat).new() 27 | local unpack = log_packer.unpack 28 | 29 | local loadstring = loadstring or load 30 | local writer = assert(assert(loadstring(maker))()) 31 | while(true)do 32 | local key, val = queue:receive(1.0, channel) 33 | -- print(maker, channel, key, val) 34 | if not (key and val) then key, val = nil, 'timeout' end 35 | if key then 36 | local msg, lvl, now = unpack(val) 37 | if msg and lvl and now then writer(logformat, msg, lvl, now) end 38 | else 39 | if val ~= 'timeout' then 40 | io.stderror:write('lane_logger: ', val) 41 | end 42 | end 43 | end 44 | end 45 | 46 | local function start_log_thread(...) 47 | local fn = assert(lanes.gen("*", log_thread_fn)) 48 | return assert(fn(...)) 49 | end 50 | 51 | local M = {} 52 | 53 | M.run = function(channel, maker, logformat) 54 | logformat = logformat or "log.logformat.default" 55 | context() -- init context 56 | local child_thread = start_log_thread(maker, logformat, channel) 57 | LOG.add_cleanup(function() child_thread:cancel(60) end) 58 | end 59 | 60 | M.channel = context 61 | 62 | return M -------------------------------------------------------------------------------- /lua/log/writer/async/server/udp.lua: -------------------------------------------------------------------------------- 1 | local server = require "log.writer.async._private.server" 2 | 3 | local M = {} 4 | 5 | M.run = function(host, port, maker, logformat) 6 | logformat = logformat or "log.logformat.default" 7 | server.run("log.writer.net.server.udp", maker, logformat, host, port) 8 | end 9 | 10 | return M -------------------------------------------------------------------------------- /lua/log/writer/async/server/zmq.lua: -------------------------------------------------------------------------------- 1 | local Z = require "log.writer.net.zmq._private.compat" 2 | local IMPL = require "log.writer.net.zmq._private.impl" 3 | local server = require "log.writer.async._private.server" 4 | 5 | local zmq, ETERM, zstrerror, zassert, zrecv = Z.zmq, Z.ETERM, Z.strerror, Z.assert, Z.recv 6 | 7 | local function rand_str(n) 8 | math.randomseed(os.time()) 9 | local str = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' 10 | local res = '' 11 | for i = 1,n do 12 | local n = math.random(1, #str) 13 | res = res .. str:sub(n,n) 14 | end 15 | return res 16 | end 17 | 18 | local function create_server(ctx, addr, maker, logformat) 19 | if ctx and not Z.is_ctx(ctx) then 20 | ctx, addr, maker, logformat = nil, ctx, addr, maker 21 | end 22 | logformat = logformat or "log.logformat.default" 23 | 24 | ctx = IMPL.context(ctx) 25 | 26 | if maker then 27 | local addr_sync = 'inproc://' .. rand_str(15) 28 | local skt_sync = zassert(ctx:socket(zmq.PAIR)) 29 | zassert(skt_sync:bind(addr_sync)) 30 | server.zrun( 31 | "log.writer.net.server.zmq", maker, logformat, ctx, 32 | false, 'PULL', addr, addr_sync 33 | ) 34 | zassert(skt_sync:recv()) 35 | skt_sync:close() 36 | end 37 | end 38 | 39 | local M = {} 40 | 41 | M.run = create_server 42 | 43 | M.context = IMPL.context 44 | 45 | return M -------------------------------------------------------------------------------- /lua/log/writer/async/udp.lua: -------------------------------------------------------------------------------- 1 | local server = require "log.writer.async.server.udp" 2 | 3 | local function create_writer(host, port, maker) 4 | if maker then 5 | server.run( host, port, maker, "log.logformat.default" ) 6 | end 7 | 8 | local writer = require "log.writer.format".new( 9 | require "log.logformat.proxy".new(), 10 | require "log.writer.net.udp".new(host, port) 11 | ) 12 | 13 | return writer 14 | end 15 | 16 | local M = {} 17 | 18 | M.new = create_writer 19 | 20 | return M -------------------------------------------------------------------------------- /lua/log/writer/async/zmq.lua: -------------------------------------------------------------------------------- 1 | local Z = require "log.writer.net.zmq._private.compat" 2 | local server = require "log.writer.async.server.zmq" 3 | 4 | local function create_writer(ctx, addr, maker) 5 | if ctx and not Z.is_ctx(ctx) then 6 | ctx, addr, maker = nil, ctx, addr 7 | end 8 | 9 | if maker then 10 | server.run( ctx, addr, maker, "log.logformat.default" ) 11 | end 12 | 13 | return require "log.writer.format".new( 14 | require "log.logformat.proxy".new(), 15 | require "log.writer.net.zmq.push".new(ctx, addr) 16 | ) 17 | end 18 | 19 | local M = {} 20 | 21 | M.new = create_writer 22 | 23 | return M -------------------------------------------------------------------------------- /lua/log/writer/console.lua: -------------------------------------------------------------------------------- 1 | return require "log.writer.stdout" -------------------------------------------------------------------------------- /lua/log/writer/console/.color.lua.un~: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moteus/lua-log/2f07d4174c4caeffdcf7cc54d4dad0e1da2bad9a/lua/log/writer/console/.color.lua.un~ -------------------------------------------------------------------------------- /lua/log/writer/console/color.lua: -------------------------------------------------------------------------------- 1 | local io = require "io" 2 | local os = require "os" 3 | local Log = require "log" 4 | 5 | local make_attr, color_writeln, COLORS 6 | 7 | if not COLORS then -- afx cio 8 | local ok, cio = pcall(require, "cio") 9 | if ok then 10 | COLORS = { 11 | BLACK = 'n'; 12 | BLUE = 'b'; 13 | GREEN = 'g'; 14 | CYAN = 'c'; 15 | RED = 'r'; 16 | MAGENTA = 'm'; 17 | BROWN = 'y'; 18 | LIGHTGRAY = 'w'; 19 | DARKGRAY = 'n+'; 20 | LIGHTBLUE = 'b+'; 21 | LIGHTGREEN = 'g+'; 22 | LIGHTCYAN = 'c+'; 23 | LIGHTRED = 'r+'; 24 | LIGHTMAGENTA = 'm+'; 25 | YELLOW = 'y+'; 26 | WHITE = 'w+'; 27 | } 28 | make_attr = function (F, B) return F .. '/' .. B end 29 | color_writeln = function (attr, text) 30 | cio.textattr(attr) 31 | cio.writeln(text or "") 32 | end 33 | end 34 | end 35 | 36 | if not COLORS then -- conio 37 | local ok, conio = pcall(require, "conio") 38 | if ok then 39 | COLORS = { 40 | BLACK = conio.COLOR_BLACK ; 41 | BLUE = conio.COLOR_BLUE ; 42 | GREEN = conio.COLOR_GREEN ; 43 | CYAN = conio.COLOR_CYAN ; 44 | RED = conio.COLOR_RED ; 45 | MAGENTA = conio.COLOR_MAGENTA ; 46 | BROWN = conio.COLOR_BROWN ; 47 | LIGHTGRAY = conio.COLOR_LIGHTGRAY ; 48 | DARKGRAY = conio.COLOR_DARKGRAY ; 49 | LIGHTBLUE = conio.COLOR_LIGHTBLUE ; 50 | LIGHTGREEN = conio.COLOR_LIGHTGREEN ; 51 | LIGHTCYAN = conio.COLOR_LIGHTCYAN ; 52 | LIGHTRED = conio.COLOR_LIGHTRED ; 53 | LIGHTMAGENTA = conio.COLOR_LIGHTMAGENTA ; 54 | YELLOW = conio.COLOR_YELLOW ; 55 | WHITE = conio.COLOR_WHITE ; 56 | } 57 | make_attr = function (F, B) return {F, B} end 58 | color_writeln = function (attr, text) 59 | conio.textcolor(attr[1]) 60 | conio.textbackground(attr[2]) 61 | conio.cputs((text or "") .. '\n') 62 | end 63 | end 64 | end 65 | 66 | if not COLORS then -- ansicolors 67 | local IS_WINDOWS = (package.config:sub(1,1) == '\\') 68 | if IS_WINDOWS and os.getenv("ANSICON") then 69 | IS_WINDOWS = nil 70 | end 71 | local ok, c 72 | if not IS_WINDOWS then ok, c = pcall(require, "ansicolors") end 73 | if ok and (type(c) == 'table') then 74 | if c.black then -- version above 1.0.2 75 | COLORS = { 76 | BLACK = 1; 77 | BLUE = 2; 78 | GREEN = 3; 79 | CYAN = 4; 80 | RED = 5; 81 | MAGENTA = 6; 82 | BROWN = 7; 83 | LIGHTGRAY = 8; 84 | DARKGRAY = 9; 85 | LIGHTBLUE = 10; 86 | LIGHTGREEN = 11; 87 | LIGHTCYAN = 12; 88 | LIGHTRED = 13; 89 | LIGHTMAGENTA = 14; 90 | YELLOW = 15; 91 | WHITE = 16; 92 | } 93 | local reset = tostring(c.reset) 94 | local fore = { 95 | [ COLORS.BLACK ] = c.black ; 96 | [ COLORS.BLUE ] = c.blue ; 97 | [ COLORS.GREEN ] = c.green ; 98 | [ COLORS.CYAN ] = c.cyan ; 99 | [ COLORS.RED ] = c.red ; 100 | [ COLORS.MAGENTA ] = c.magenta; 101 | [ COLORS.BROWN ] = c.yellow ; 102 | [ COLORS.LIGHTGRAY ] = c.white ; 103 | [ COLORS.DARKGRAY ] = c.black .. c.bright; 104 | [ COLORS.LIGHTBLUE ] = c.blue .. c.bright; 105 | [ COLORS.LIGHTGREEN ] = c.green .. c.bright; 106 | [ COLORS.LIGHTCYAN ] = c.cyan .. c.bright; 107 | [ COLORS.LIGHTRED ] = c.red .. c.bright; 108 | [ COLORS.LIGHTMAGENTA ] = c.magenta .. c.bright; 109 | [ COLORS.YELLOW ] = c.yellow .. c.bright; 110 | [ COLORS.WHITE ] = c.white .. c.bright; 111 | } 112 | 113 | local back = { 114 | [ COLORS.BLACK ] = c.onblack ; 115 | [ COLORS.BLUE ] = c.onblue ; 116 | [ COLORS.GREEN ] = c.ongreen ; 117 | [ COLORS.CYAN ] = c.oncyan ; 118 | [ COLORS.RED ] = c.onred ; 119 | [ COLORS.MAGENTA ] = c.onmagenta; 120 | [ COLORS.BROWN ] = c.onyellow ; 121 | [ COLORS.LIGHTGRAY ] = c.onwhite ; 122 | [ COLORS.DARKGRAY ] = c.onblack .. c.bright; 123 | [ COLORS.LIGHTBLUE ] = c.onblue .. c.bright; 124 | [ COLORS.LIGHTGREEN ] = c.ongreen .. c.bright; 125 | [ COLORS.LIGHTCYAN ] = c.oncyan .. c.bright; 126 | [ COLORS.LIGHTRED ] = c.onred .. c.bright; 127 | [ COLORS.LIGHTMAGENTA ] = c.onmagenta .. c.bright; 128 | [ COLORS.YELLOW ] = c.onyellow .. c.bright; 129 | [ COLORS.WHITE ] = c.onwhite .. c.bright; 130 | } 131 | 132 | make_attr = function (F, B) return fore[F] .. back[B] end 133 | color_writeln = function (attr, text) io.write(attr, text, reset, '\n') end 134 | elseif c.noReset then -- 1.0.2 135 | COLORS = { 136 | BLACK = 'black'; 137 | BLUE = 'blue'; 138 | GREEN = 'green'; 139 | CYAN = 'cyan'; 140 | RED = 'red'; 141 | MAGENTA = 'magenta'; 142 | BROWN = 'yellow'; 143 | LIGHTGRAY = 'white'; 144 | DARKGRAY = 'bright black'; 145 | LIGHTBLUE = 'bright blue'; 146 | LIGHTGREEN = 'bright green'; 147 | LIGHTCYAN = 'bright cyan'; 148 | LIGHTRED = 'bright red'; 149 | LIGHTMAGENTA = 'bright magenta'; 150 | YELLOW = 'bright yellow'; 151 | WHITE = 'bright white'; 152 | } 153 | 154 | make_attr = function (F, B) return c.noReset("%{" .. F .. " " .. B .. "bg}") end 155 | local RESET = c.noReset("%{reset}") 156 | color_writeln = function (attr, text) io.write(attr, (text), RESET, '\n') end 157 | end 158 | end 159 | if ok and (type(c) == 'function') then 160 | COLORS = { 161 | BLACK = 1; 162 | BLUE = 2; 163 | GREEN = 3; 164 | CYAN = 4; 165 | RED = 5; 166 | MAGENTA = 6; 167 | BROWN = 7; 168 | LIGHTGRAY = 8; 169 | DARKGRAY = 9; 170 | LIGHTBLUE = 10; 171 | LIGHTGREEN = 11; 172 | LIGHTCYAN = 12; 173 | LIGHTRED = 13; 174 | LIGHTMAGENTA = 14; 175 | YELLOW = 15; 176 | WHITE = 16; 177 | } 178 | local fore = { 179 | [ COLORS.BLACK ] = "%{black}"; 180 | [ COLORS.BLUE ] = "%{blue}" ; 181 | [ COLORS.GREEN ] = "%{green}" ; 182 | [ COLORS.CYAN ] = "%{cyan}" ; 183 | [ COLORS.RED ] = "%{red}" ; 184 | [ COLORS.MAGENTA ] = "%{magenta}"; 185 | [ COLORS.BROWN ] = "%{yellow}" ; 186 | [ COLORS.LIGHTGRAY ] = "%{white}" ; 187 | [ COLORS.DARKGRAY ] = "%{black}" .. "%{bright}"; 188 | [ COLORS.LIGHTBLUE ] = "%{blue}" .. "%{bright}"; 189 | [ COLORS.LIGHTGREEN ] = "%{green}" .. "%{bright}"; 190 | [ COLORS.LIGHTCYAN ] = "%{cyan}" .. "%{bright}"; 191 | [ COLORS.LIGHTRED ] = "%{red}" .. "%{bright}"; 192 | [ COLORS.LIGHTMAGENTA ] = "%{magenta}" .. "%{bright}"; 193 | [ COLORS.YELLOW ] = "%{yellow}" .. "%{bright}"; 194 | [ COLORS.WHITE ] = "%{white}" .. "%{bright}"; 195 | } 196 | local back = { 197 | [ COLORS.BLACK ] = "%{blackbg}"; 198 | [ COLORS.BLUE ] = "%{bluebg}" ; 199 | [ COLORS.GREEN ] = "%{greenbg}" ; 200 | [ COLORS.CYAN ] = "%{cyanbg}" ; 201 | [ COLORS.RED ] = "%{redbg}" ; 202 | [ COLORS.MAGENTA ] = "%{magentabg}"; 203 | [ COLORS.BROWN ] = "%{yellowbg}" ; 204 | [ COLORS.LIGHTGRAY ] = "%{whitebg}" ; 205 | [ COLORS.DARKGRAY ] = "%{blackbg}" .. "%{bright}"; 206 | [ COLORS.LIGHTBLUE ] = "%{bluebg}" .. "%{bright}"; 207 | [ COLORS.LIGHTGREEN ] = "%{greenbg}" .. "%{bright}"; 208 | [ COLORS.LIGHTCYAN ] = "%{cyanbg}" .. "%{bright}"; 209 | [ COLORS.LIGHTRED ] = "%{redbg}" .. "%{bright}"; 210 | [ COLORS.LIGHTMAGENTA ] = "%{magentabg}" .. "%{bright}"; 211 | [ COLORS.YELLOW ] = "%{yellowbg}" .. "%{bright}"; 212 | [ COLORS.WHITE ] = "%{whitebg}" .. "%{bright}"; 213 | } 214 | make_attr = function (F, B) return fore[F] .. back[B] end 215 | color_writeln = function (attr, text) io.write(c(attr .. text), '\n') end 216 | end 217 | end 218 | 219 | if not COLORS then 220 | local ok, term = pcall(require, "term") 221 | if ok then 222 | COLORS = { 223 | BLACK = 1; 224 | BLUE = 2; 225 | GREEN = 3; 226 | CYAN = 4; 227 | RED = 5; 228 | MAGENTA = 6; 229 | BROWN = 7; 230 | LIGHTGRAY = 8; 231 | DARKGRAY = 9; 232 | LIGHTBLUE = 10; 233 | LIGHTGREEN = 11; 234 | LIGHTCYAN = 12; 235 | LIGHTRED = 13; 236 | LIGHTMAGENTA = 14; 237 | YELLOW = 15; 238 | WHITE = 16; 239 | } 240 | 241 | local c = term.colors 242 | local reset = c.reset 243 | local fore = { 244 | [ COLORS.BLACK ] = c.black ; 245 | [ COLORS.BLUE ] = c.blue ; 246 | [ COLORS.GREEN ] = c.green ; 247 | [ COLORS.CYAN ] = c.cyan ; 248 | [ COLORS.RED ] = c.red ; 249 | [ COLORS.MAGENTA ] = c.magenta; 250 | [ COLORS.BROWN ] = c.yellow ; 251 | [ COLORS.LIGHTGRAY ] = c.white ; 252 | [ COLORS.DARKGRAY ] = c.black .. c.bright; 253 | [ COLORS.LIGHTBLUE ] = c.blue .. c.bright; 254 | [ COLORS.LIGHTGREEN ] = c.green .. c.bright; 255 | [ COLORS.LIGHTCYAN ] = c.cyan .. c.bright; 256 | [ COLORS.LIGHTRED ] = c.red .. c.bright; 257 | [ COLORS.LIGHTMAGENTA ] = c.magenta .. c.bright; 258 | [ COLORS.YELLOW ] = c.yellow .. c.bright; 259 | [ COLORS.WHITE ] = c.white .. c.bright; 260 | } 261 | 262 | local back = { 263 | [ COLORS.BLACK ] = c.onblack ; 264 | [ COLORS.BLUE ] = c.onblue ; 265 | [ COLORS.GREEN ] = c.ongreen ; 266 | [ COLORS.CYAN ] = c.oncyan ; 267 | [ COLORS.RED ] = c.onred ; 268 | [ COLORS.MAGENTA ] = c.onmagenta; 269 | [ COLORS.BROWN ] = c.onyellow ; 270 | [ COLORS.LIGHTGRAY ] = c.onwhite ; 271 | [ COLORS.DARKGRAY ] = c.onblack .. c.bright; 272 | [ COLORS.LIGHTBLUE ] = c.onblue .. c.bright; 273 | [ COLORS.LIGHTGREEN ] = c.ongreen .. c.bright; 274 | [ COLORS.LIGHTCYAN ] = c.oncyan .. c.bright; 275 | [ COLORS.LIGHTRED ] = c.onred .. c.bright; 276 | [ COLORS.LIGHTMAGENTA ] = c.onmagenta .. c.bright; 277 | [ COLORS.YELLOW ] = c.onyellow .. c.bright; 278 | [ COLORS.WHITE ] = c.onwhite .. c.bright; 279 | } 280 | 281 | make_attr = function (F, B) return fore[F] .. back[B] end 282 | color_writeln = function (attr, text) io.write(attr..text..reset, '\n') end 283 | end 284 | end 285 | 286 | if not COLORS then -- fallback to console 287 | return require"log.writer.console" 288 | end 289 | 290 | local colors = { 291 | [Log.LVL.EMERG ] = make_attr(COLORS.WHITE, COLORS.LIGHTRED); 292 | [Log.LVL.ALERT ] = make_attr(COLORS.BLUE, COLORS.LIGHTRED); 293 | [Log.LVL.FATAL ] = make_attr(COLORS.BLACK, COLORS.LIGHTRED); 294 | [Log.LVL.ERROR ] = make_attr(COLORS.LIGHTRED, COLORS.BLACK); 295 | [Log.LVL.WARNING ] = make_attr(COLORS.LIGHTMAGENTA, COLORS.BLACK); 296 | [Log.LVL.NOTICE ] = make_attr(COLORS.LIGHTCYAN, COLORS.BLACK); 297 | [Log.LVL.INFO ] = make_attr(COLORS.WHITE, COLORS.BLACK); 298 | [Log.LVL.DEBUG ] = make_attr(COLORS.YELLOW, COLORS.BLACK); 299 | [Log.LVL.TRACE ] = make_attr(COLORS.LIGHTGREEN, COLORS.BLACK); 300 | } 301 | 302 | local function console_writer(fmt, msg, lvl, now) 303 | color_writeln(colors[lvl], fmt(msg, lvl, now)) 304 | end 305 | 306 | local M = {} 307 | 308 | function M.new() return console_writer end 309 | 310 | return M 311 | -------------------------------------------------------------------------------- /lua/log/writer/file.lua: -------------------------------------------------------------------------------- 1 | local LOG = require "log" 2 | local file_logger = require "log.writer.file.private.impl" 3 | 4 | local M = {} 5 | 6 | function M.new(opt) 7 | local logger = file_logger:new(opt) 8 | LOG.add_cleanup(function() logger:close() end) 9 | 10 | return function(fmt, msg, lvl, now) 11 | logger:write((fmt(msg, lvl, now))) 12 | end 13 | end 14 | 15 | return M -------------------------------------------------------------------------------- /lua/log/writer/file/by_day.lua: -------------------------------------------------------------------------------- 1 | local file = require "log.writer.file" 2 | 3 | local M = {} 4 | 5 | function M.new(log_dir, log_name, max_rows) 6 | return file.new{ 7 | log_dir = log_dir, 8 | log_name = log_name, 9 | max_rows = max_rows, 10 | by_day = true, 11 | close_file = false, 12 | flush_interval = 1, 13 | } 14 | end 15 | 16 | return M 17 | 18 | -------------------------------------------------------------------------------- /lua/log/writer/file/private/impl.lua: -------------------------------------------------------------------------------- 1 | local Log = require "log" 2 | local io = require "io" 3 | local os = require "os" 4 | local string = require "string" 5 | local date = require "date" 6 | local lfs = require "lfs" 7 | 8 | local DIR_SEP = package.config:sub(1,1) 9 | local IS_WINDOWS = DIR_SEP == '\\' 10 | 11 | local function remove_dir_end(str) 12 | return (string.gsub(str, '[\\/]+$', '')) 13 | end 14 | 15 | local function ensure_dir_end(str) 16 | return remove_dir_end(str) .. DIR_SEP 17 | end 18 | 19 | local function path_normolize_sep(P) 20 | return (string.gsub(P, '\\', DIR_SEP):gsub('/', DIR_SEP)) 21 | end 22 | 23 | local function path_fullpath(P) 24 | P = path_normolize_sep(P) 25 | local ch1, ch2 = P:sub(1,1), P:sub(2,2) 26 | if IS_WINDOWS then 27 | if ch1 == DIR_SEP then -- \temp => c:\temp 28 | local cwd = lfs.currentdir() 29 | local disk = cwd:sub(1,2) 30 | P = disk .. P 31 | elseif ch1 == '~' then -- ~\temp 32 | local base = os.getenv('USERPROFILE') or (os.getenv('HOMEDRIVE') .. os.getenv('HOMEPATH')) 33 | P = ((ch2 == DIR_SEP) and remove_dir_end(base) or ensure_dir_end(base)) .. string.sub(P,2) 34 | elseif ch2 ~= ':' then 35 | P = ensure_dir_end(lfs.currentdir()) .. P 36 | end 37 | else 38 | if ch1 == '~' then -- ~/temp 39 | local base = os.getenv('HOME') 40 | P = ((ch2 == DIR_SEP) and remove_dir_end(base) or ensure_dir_end(base)) .. string.sub(P,2) 41 | else 42 | if P:sub(1,1) ~= '/' then 43 | P = ensure_dir_end(lfs.currentdir()) .. P 44 | end 45 | end 46 | end 47 | 48 | P = string.gsub(P, DIR_SEP .. '%.' .. DIR_SEP, DIR_SEP):gsub(DIR_SEP .. DIR_SEP, DIR_SEP) 49 | while true do 50 | local first, last = string.find(P, DIR_SEP .. "[^".. DIR_SEP .. "]+" .. DIR_SEP .. '%.%.' .. DIR_SEP) 51 | if not first then break end 52 | P = string.sub(P, 1, first) .. string.sub(P, last+1) 53 | end 54 | 55 | return P 56 | end 57 | 58 | local function attrib(P, ...) 59 | if IS_WINDOWS then 60 | if #P < 4 and P:sub(2,2) == ':' then 61 | P = ensure_dir_end(P) -- c: => c:\ 62 | else 63 | P = remove_dir_end(P) -- c:\temp\ => c:\temp 64 | end 65 | end 66 | return lfs.attributes(P, ...) 67 | end 68 | 69 | local function path_exists(P) 70 | return attrib(P,'mode') ~= nil and P 71 | end 72 | 73 | local function path_isdir(P) 74 | return attrib(P,'mode') == 'directory' and P 75 | end 76 | 77 | local function path_mkdir(P) 78 | local P = path_fullpath(P) 79 | local p = '' 80 | 81 | for str in string.gmatch(ensure_dir_end(P), '.-' .. DIR_SEP) do 82 | p = p .. str 83 | if path_exists(p) then 84 | if not path_isdir(p) then 85 | return nil, 'can not create ' .. p 86 | end 87 | else 88 | local ok, err = lfs.mkdir(remove_dir_end(p)) 89 | if not ok then return nil, err .. ' ' .. p end 90 | end 91 | end 92 | 93 | return true 94 | end 95 | 96 | local function path_getctime(P) 97 | return attrib(P,'change') 98 | end 99 | 100 | local function path_getmtime(P) 101 | return attrib(P,'modification') 102 | end 103 | 104 | local function path_getatime(P) 105 | return attrib(P,'access') 106 | end 107 | 108 | local function path_getsize(P) 109 | return attrib(P, 'size') 110 | end 111 | 112 | local function path_getrows(P) 113 | local f, err = io.open(P, "r") 114 | if not f then return 0 end 115 | local count = 0 116 | for _ in f:lines() do count = count + 1 end 117 | f:close() 118 | return count 119 | end 120 | 121 | local function path_remove(P) 122 | return os.remove(P) 123 | end 124 | 125 | local function path_rename(from,to) 126 | path_remove(to) 127 | return os.rename(from, to) 128 | end 129 | 130 | local function reset_out(FileName, rewrite) 131 | local END_OF_LINE = '\n' 132 | local FILE_APPEND = 'a' 133 | 134 | if rewrite then 135 | local FILE_REWRITE = 'w+' 136 | local f, err = io.open(FileName , FILE_REWRITE); 137 | if not f then return nil, err end 138 | f:close(); 139 | end 140 | 141 | return function (msg) 142 | local f, err = io.open(FileName, FILE_APPEND) 143 | if not f then return nil, err end 144 | f:write(msg, END_OF_LINE) 145 | f:close() 146 | end 147 | end 148 | 149 | local function make_no_close_reset(flush_interval) 150 | return function (FileName, rewrite) 151 | local END_OF_LINE = '\n' 152 | local FILE_APPEND = 'a' 153 | 154 | if rewrite then 155 | local FILE_REWRITE = 'w+' 156 | local f, err = io.open(FileName, FILE_REWRITE); 157 | if not f then return nil, err end 158 | f:close() 159 | end 160 | 161 | local f, err = io.open(FileName, FILE_APPEND); 162 | if not f then return nil, err end 163 | 164 | local writer 165 | if flush_interval then 166 | local flush_interval, counter = flush_interval, 0 167 | writer = function (msg) 168 | f:write(msg, END_OF_LINE) 169 | counter = counter + 1 170 | if counter >= flush_interval then 171 | f:flush() 172 | counter = 0 173 | end 174 | end 175 | else 176 | writer = function (msg) f:write(msg, END_OF_LINE) end 177 | end 178 | return writer, function() f:close() end 179 | end 180 | end 181 | 182 | local function split_ext(fname) 183 | local s1, s2 = string.match(fname, '([^\\/]*)([.][^.\\/]*)$') 184 | if s1 then return s1, s2 end 185 | s1 = string.match(fname, '([^\\/]+)$') 186 | if s1 then return s1, '' end 187 | end 188 | 189 | local function assert_2(f1, f2, v1, v2) 190 | assert(f1 == v1, string.format( "Expected '%s' got '%s'", tostring(f1), tostring(v1))) 191 | assert(f2 == v2, string.format( "Expected '%s' got '%s'", tostring(f2), tostring(v2))) 192 | end 193 | 194 | assert_2("events", ".log", split_ext("events.log")) 195 | assert_2("events", '', split_ext("events")) 196 | assert_2(nil, nil, split_ext("events\\")) 197 | assert_2('', '.log', split_ext("events\\.log")) 198 | assert_2('log', '', split_ext("events\\log")) 199 | 200 | local file_logger = {} 201 | 202 | local FILE_LOG_DATE_FMT = "%Y%m%d" 203 | local EOL_SIZE = IS_WINDOWS and 2 or 1 204 | 205 | local function get_file_date(fname) 206 | local mdate = path_getmtime(fname) 207 | if mdate then 208 | mdate = date(mdate):tolocal() 209 | else 210 | mdate = date() 211 | end 212 | return mdate:fmt(FILE_LOG_DATE_FMT) 213 | end 214 | 215 | function file_logger:close() 216 | if self.private_.logger and self.private_.logger_close then 217 | self.private_.logger_close() 218 | end 219 | self.private_.logger = nil 220 | self.private_.logger_close = nil 221 | end 222 | 223 | function file_logger:open() 224 | local full_name = self:current_name() 225 | 226 | local logger, err = self.private_.reset_out(full_name) 227 | if not logger then 228 | return nil, string.format("can not create logger for file '%s':", full_name, err) 229 | end 230 | 231 | self.private_.logger = logger 232 | self.private_.logger_close = err 233 | self.private_.log_date = os.date(FILE_LOG_DATE_FMT) 234 | self.private_.log_rows = 0 235 | self.private_.log_size = 0 236 | 237 | return true 238 | end 239 | 240 | function file_logger:current_name() 241 | return self.private_.log_dir .. self.private_.log_name 242 | end 243 | 244 | function file_logger:archive_roll_name(i) 245 | return self.private_.log_dir .. string.format("%s.%.5d.log", self.private_.arc_pfx, i) 246 | end 247 | 248 | function file_logger:archive_date_name(d, i) 249 | return self.private_.log_dir .. string.format("%s.%s.%.5d.log", self.private_.arc_pfx, d, i) 250 | end 251 | 252 | function file_logger:reset_log_by_roll() 253 | self:close() 254 | 255 | local full_name = self:current_name() 256 | local first_name = self:archive_roll_name(1) 257 | 258 | -- we must "free" space for current file 259 | if path_exists(first_name) then 260 | for i = self.private_.roll_count - 1, 1, -1 do 261 | local fname1 = self:archive_roll_name(i) 262 | local fname2 = self:archive_roll_name(i + 1) 263 | path_rename(fname1, fname2) 264 | end 265 | end 266 | 267 | if path_exists(full_name) then 268 | local ok, err = path_rename(full_name, first_name) 269 | if not ok then 270 | return nil, string.format("can not rename '%s' to '%s' : %s", full_name, first_name, err or '') 271 | end 272 | end 273 | 274 | return self:open() 275 | end 276 | 277 | function file_logger:next_date_name(log_date) 278 | local id = self.private_.id 279 | 280 | local fname = self:archive_date_name(log_date, id) 281 | while path_exists(fname) do 282 | id = id + 1 283 | fname = self:archive_date_name(log_date, id) 284 | end 285 | 286 | self.private_.id = id 287 | return fname 288 | end 289 | 290 | function file_logger:reset_log_by_date(log_date) 291 | self:close() 292 | 293 | local full_name = self:current_name() 294 | if path_exists(full_name) then -- previews file 295 | log_date = log_date or get_file_date(full_name) 296 | local next_fname = self:next_date_name(log_date) 297 | local ok, err = path_rename(full_name, next_fname) 298 | if not ok then 299 | return nil, string.format("can not rename '%s' to '%s' : ", full_name, next_fname, err or '') 300 | end 301 | end 302 | 303 | return self:open() 304 | end 305 | 306 | function file_logger:reset_log(...) 307 | if self.private_.roll_count then 308 | return self:reset_log_by_roll(...) 309 | end 310 | return self:reset_log_by_date(...) 311 | end 312 | 313 | function file_logger:check() 314 | if self.private_.by_day then 315 | local now = os.date(FILE_LOG_DATE_FMT) 316 | if self.private_.log_date ~= now then 317 | local ok, err = self:reset_log_by_date(self.private_.log_date) 318 | self.private_.id = 1 319 | return ok, err 320 | end 321 | end 322 | 323 | if self.private_.max_rows and (self.private_.log_rows >= self.private_.max_rows) then 324 | return self:reset_log() 325 | end 326 | 327 | if self.private_.max_size and (self.private_.log_size >= self.private_.max_size) then 328 | return self:reset_log() 329 | end 330 | 331 | return true 332 | end 333 | 334 | function file_logger:write(msg) 335 | local ok, err = self:check() 336 | if not ok then 337 | io.stderr:write("logger error: ", err, '\n') 338 | return 339 | end 340 | 341 | self.private_.logger(msg) 342 | self.private_.log_rows = self.private_.log_rows + 1 343 | self.private_.log_size = self.private_.log_size + #msg + EOL_SIZE 344 | end 345 | 346 | function file_logger:init(opt) 347 | 348 | if(opt.by_day or opt.roll_count)then 349 | assert(not(opt.by_day and opt.roll_count), 350 | "Can not set 'by_day' and 'roll_count' fields at the same time!" 351 | ) 352 | end 353 | assert(opt.log_name, 'field log_name is required') 354 | 355 | local log_dir = path_fullpath(opt.log_dir or '.') 356 | 357 | if path_exists(log_dir) then assert(path_isdir(log_dir)) 358 | else assert(path_mkdir(log_dir)) end 359 | 360 | local log_name, log_ext = string.match(opt.log_name, '([^\\/]+)([.][^.\\/]+)$') 361 | assert(log_name and log_ext) 362 | 363 | log_dir = ensure_dir_end( log_dir ) 364 | local full_name = log_dir .. log_name .. log_ext 365 | local current_size = path_getsize(full_name) 366 | if 0 == current_size then 367 | -- prevent rename zero size logfile 368 | path_remove(full_name) 369 | end 370 | 371 | local flush_interval = opt.flush_interval and assert(tonumber(opt.flush_interval), 'flush_interval must be a number') or 1 372 | self.private_ = { 373 | -- options 374 | log_dir = log_dir; 375 | log_name = log_name .. log_ext; 376 | max_rows = opt.max_rows or math.huge; 377 | max_size = opt.max_size or math.huge; 378 | reset_out = opt.close_file and reset_out or make_no_close_reset(flush_interval); 379 | arc_pfx = opt.archive_prefix or log_name; 380 | roll_count = opt.roll_count and assert(tonumber(opt.roll_count), 'roll_count must be a number'); 381 | by_day = not not opt.by_day; 382 | 383 | -- state 384 | -- log_date = ; -- date when current log file was create 385 | -- log_rows = 0; -- how many lines in current log file 386 | -- log_size = 0; 387 | id = 1; -- numbers of file in current log_date 388 | } 389 | if self.private_.roll_count then 390 | assert(self.private_.roll_count > 0) 391 | end 392 | 393 | local reuse_log = opt.reuse 394 | 395 | if reuse_log and current_size and (current_size > 0) then 396 | self.private_.log_date = get_file_date(full_name) 397 | 398 | if opt.max_rows then 399 | self.private_.log_rows = path_getrows(full_name) or 0 400 | else 401 | self.private_.log_rows = 0 402 | end 403 | 404 | if opt.max_size then 405 | self.private_.log_size = path_getsize(full_name) or 0 406 | else 407 | self.private_.log_size = 0 408 | end 409 | 410 | local logger, err = self.private_.reset_out(full_name) 411 | if not logger then 412 | error(string.format("can not create logger for file '%s':", full_name, err)) 413 | end 414 | 415 | self.private_.logger = logger 416 | self.private_.logger_close = err 417 | 418 | else 419 | assert(self:reset_log()) 420 | end 421 | 422 | return self 423 | end 424 | 425 | function file_logger:new(...) 426 | local o = setmetatable({}, {__index = self}):init(...) 427 | Log.add_cleanup(function() o:close() end) 428 | return o 429 | end 430 | 431 | local function do_profile() 432 | require "profiler".start() 433 | 434 | local logger = file_logger:new{ 435 | log_dir = './logs'; 436 | log_name = "events.log"; 437 | max_rows = 1000; 438 | max_size = 70; 439 | roll_count = 11; 440 | -- by_day = true; 441 | close_file = false; 442 | flush_interval = 1; 443 | reuse = true 444 | } 445 | 446 | for i = 1, 10000 do 447 | local msg = string.format("%5d", i) 448 | logger:write(msg) 449 | end 450 | 451 | logger:close() 452 | end 453 | 454 | return file_logger 455 | -------------------------------------------------------------------------------- /lua/log/writer/file/roll.lua: -------------------------------------------------------------------------------- 1 | local file = require "log.writer.file" 2 | 3 | local M = {} 4 | 5 | function M.new(log_dir, log_name, roll_count, max_size) 6 | return file.new{ 7 | log_dir = log_dir, 8 | log_name = log_name, 9 | max_size = max_size or 10 * 1024 * 1024, 10 | roll_count = assert(roll_count), 11 | close_file = false, 12 | flush_interval = 1, 13 | reuse = true, 14 | } 15 | end 16 | 17 | return M 18 | 19 | -------------------------------------------------------------------------------- /lua/log/writer/filter.lua: -------------------------------------------------------------------------------- 1 | return require "log.writer.filter.lvl.le" -------------------------------------------------------------------------------- /lua/log/writer/filter/lvl/eq.lua: -------------------------------------------------------------------------------- 1 | local Log = require "log" 2 | 3 | local M = {} 4 | 5 | function M.new(max_lvl, writer) 6 | max_lvl = assert(Log.lvl2number(max_lvl)) 7 | return function(fmt, msg, lvl, now) 8 | if lvl == max_lvl then writer(fmt, msg, lvl, now) end 9 | end 10 | end 11 | 12 | return M -------------------------------------------------------------------------------- /lua/log/writer/filter/lvl/le.lua: -------------------------------------------------------------------------------- 1 | local Log = require "log" 2 | 3 | local M = {} 4 | 5 | function M.new(max_lvl, writer) 6 | max_lvl = assert(Log.lvl2number(max_lvl)) 7 | return function(fmt, msg, lvl, now) 8 | if lvl <= max_lvl then writer(fmt, msg, lvl, now) end 9 | end 10 | end 11 | 12 | return M -------------------------------------------------------------------------------- /lua/log/writer/format.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | function M.new(newfmt, writer) 4 | return function(oldfmt, msg, lvl, now) 5 | writer(newfmt, msg, lvl, now) 6 | end 7 | end 8 | 9 | return M 10 | -------------------------------------------------------------------------------- /lua/log/writer/freeswitch.lua: -------------------------------------------------------------------------------- 1 | local log = require "log" 2 | 3 | local freeswitch = assert(freeswitch) 4 | 5 | local FS_LVL = { 6 | console = 0; 7 | alert = 1; 8 | crit = 2; 9 | err = 3; 10 | warning = 4; 11 | notice = 5; 12 | info = 6; 13 | debug = 7; 14 | } 15 | 16 | local LOG2FS = { 17 | [ log.LVL.EMERG ] = FS_LVL.alert; 18 | [ log.LVL.ALERT ] = FS_LVL.alert; 19 | [ log.LVL.FATAL ] = FS_LVL.crit; 20 | [ log.LVL.ERROR ] = FS_LVL.err; 21 | [ log.LVL.WARNING ] = FS_LVL.warning; 22 | [ log.LVL.NOTICE ] = FS_LVL.notice; 23 | [ log.LVL.INFO ] = FS_LVL.info; 24 | [ log.LVL.DEBUG ] = FS_LVL.debug; 25 | [ log.LVL.TRACE ] = FS_LVL.debug; 26 | } 27 | 28 | local M = {} 29 | 30 | function M.new(session) 31 | if session then 32 | return function(fmt, msg, lvl, now) 33 | session:consoleLog( LOG2FS[lvl], msg .. '\n' ) 34 | end 35 | else 36 | return function(fmt, msg, lvl, now) 37 | freeswitch.consoleLog( LOG2FS[lvl], msg .. '\n' ) 38 | end 39 | end 40 | end 41 | 42 | return M 43 | -------------------------------------------------------------------------------- /lua/log/writer/list.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | function M.new(...) 4 | local writers = {...} 5 | return function(...) 6 | for i = 1, #writers do 7 | writers[i](...) 8 | end 9 | end 10 | end 11 | 12 | return M 13 | 14 | -------------------------------------------------------------------------------- /lua/log/writer/net/server/udp.lua: -------------------------------------------------------------------------------- 1 | local socket = require "socket" 2 | local log_packer = require "log.logformat.proxy.pack" 3 | 4 | local _M = {} 5 | 6 | function _M.run(writer, final, logformat, host, port) 7 | local uskt = assert(socket.udp()) 8 | assert(uskt:setsockname(host, port)) 9 | local unpack = log_packer.unpack 10 | 11 | while true do 12 | local msg, err = uskt:receivefrom() 13 | if msg then 14 | local msg, lvl, now = unpack(msg) 15 | if msg and lvl and now then writer(logformat, msg, lvl, now) end 16 | else 17 | if err ~= 'timeout' then 18 | io.stderr:write('log.writer.net.udp.server: ', err, '\n') 19 | end 20 | end 21 | end 22 | 23 | -- @todo 24 | -- if final then final() end 25 | end 26 | 27 | return _M -------------------------------------------------------------------------------- /lua/log/writer/net/server/zmq.lua: -------------------------------------------------------------------------------- 1 | local Z = require "log.writer.net.zmq._private.compat" 2 | local IMPL = require "log.writer.net.zmq._private.impl" 3 | 4 | local zmq, ETERM, zstrerror, zassert, zrecv = Z.zmq, Z.ETERM, Z.strerror, Z.assert, Z.recv 5 | local zerrcode = Z.errcode 6 | 7 | local log_packer = require "log.logformat.proxy.pack" 8 | 9 | local _M = {} 10 | 11 | function _M.run(writer, final, logformat, ctx, stype, address, addr_sync) 12 | -- print(writer, logformat, ctx, stype, address, addr_sync) 13 | local stypes = { 14 | SUB = zmq.SUB; 15 | PULL = zmq.PULL; 16 | } 17 | stype = assert(stypes[stype], 'Unsupported socket type') 18 | 19 | ctx = IMPL.context(ctx) 20 | 21 | local skt = zassert(ctx:socket(stype)) 22 | zassert(skt:bind(address)) 23 | 24 | if addr_sync then 25 | local skt_sync = zassert(ctx:socket(zmq.PAIR)) 26 | zassert(skt_sync:connect(addr_sync)) 27 | skt_sync:send("") 28 | skt_sync:close() 29 | end 30 | 31 | local unpack = log_packer.unpack 32 | 33 | while(true)do 34 | local msg, err = zrecv(skt) 35 | if msg then 36 | local msg, lvl, now = unpack(msg) 37 | if msg and lvl and now then writer(logformat, msg, lvl, now) end 38 | else 39 | if zerrcode(err) == ETERM then break end 40 | io.stderr:write('log.writer.net.zmq.server: ', tostring(err), zstrerror(err), '\n') 41 | end 42 | end 43 | 44 | if final then final() end 45 | 46 | skt:close() 47 | end 48 | 49 | return _M -------------------------------------------------------------------------------- /lua/log/writer/net/smtp.lua: -------------------------------------------------------------------------------- 1 | local Log = require "log" 2 | local sendmail = require "sendmail" 3 | 4 | local M = {} 5 | 6 | function M.new(from, to, server, subject) 7 | assert(to, "'to' parameter is required") 8 | assert(from, "'from' parameter is required") 9 | assert(server, "'server' parameter is required") 10 | 11 | subject = subject or '' 12 | 13 | return function(fmt, msg, lvl, now) 14 | msg = fmt(msg, lvl, now) 15 | sendmail(from, to, server, { 16 | subject = now:fmt("%F %T") .. ' [' .. Log.LVL_NAMES[lvl] .. '] ' .. subject; 17 | file = { 18 | name = 'message.txt'; 19 | data = msg; 20 | }; 21 | text = msg; 22 | }) 23 | end 24 | end 25 | 26 | return M -------------------------------------------------------------------------------- /lua/log/writer/net/udp.lua: -------------------------------------------------------------------------------- 1 | local socket = require("socket") 2 | 3 | local function create_socket(host, port, timeout) 4 | local skt = assert(socket.udp()) 5 | assert(skt:settimeout(timeout or 0.1)) 6 | assert(skt:setpeername(host, port)) 7 | return skt 8 | end 9 | 10 | local M = {} 11 | 12 | function M.new(host, port, timeout) 13 | local skt = create_socket(host, port, timeout) 14 | return function(fmt, ...) skt:send((fmt( ... ))) end 15 | end 16 | 17 | return M -------------------------------------------------------------------------------- /lua/log/writer/net/zmq.lua: -------------------------------------------------------------------------------- 1 | return require"log.writer.net.zmq.pub" -------------------------------------------------------------------------------- /lua/log/writer/net/zmq/_private/compat.lua: -------------------------------------------------------------------------------- 1 | local function prequire(...) 2 | local ok, mod = pcall(require, ...) 3 | return ok and mod, mod or nil 4 | end 5 | 6 | local zmq, zthreads, zpoller 7 | local zstrerror, zassert, ETERM 8 | local zconnect, zbind 9 | local zrecv_all, zrecv 10 | local zerrcode 11 | 12 | 13 | local function has_member(t, key) 14 | local ok, has 15 | if type(key) == "table" then 16 | ok, has = pcall(function() 17 | for _, k in ipairs(key) do 18 | if nil == t[k] then return false end 19 | end 20 | return true 21 | end) 22 | else 23 | ok, has = pcall(function() 24 | return nil ~= t[key] 25 | end) 26 | end 27 | return ok and has 28 | end 29 | 30 | local function is_ctx(ctx) 31 | local tname = type(ctx) 32 | if (tname ~= 'table') and (tname ~= 'userdata') then 33 | return false 34 | end 35 | return has_member(ctx, { 36 | 'socket', 'term' 37 | }) 38 | end 39 | 40 | zmq = prequire "lzmq" 41 | if zmq then 42 | zpoller = prequire "lzmq.poller" 43 | zthreads = prequire "lzmq.threads" 44 | ETERM = zmq.errors.ETERM 45 | zstrerror = function(err) 46 | if type(err) == "number" then return zmq.strerror(err) end 47 | if type(err) == "string" then return err end 48 | return err:msg() 49 | end 50 | zerrcode = function(err) 51 | if type(err) == "number" then return err end 52 | if type(err) == "string" then return err end -- @todo extract no from string 53 | return err:no() 54 | end 55 | zassert = zmq.assert 56 | zrecv_all = function(skt) return skt:recv_all() end 57 | zconnect = function(skt, addr) return skt:connect(addr) end 58 | zbind = function(skt, addr) return skt:bind(addr) end 59 | else 60 | zmq = require "zmq" 61 | zpoller = require "zmq.poller" 62 | zthreads = prequire "zmq.threads" 63 | ETERM = 'closed' 64 | zstrerror = function(err) return err end 65 | zerrcode = function(err) return err end 66 | zassert = assert 67 | zrecv_all = function(skt) 68 | local t = {} 69 | local r, err = skt:recv() 70 | if not r then return nil, err end 71 | table.insert(t,r) 72 | while skt:rcvmore() == 1 do 73 | r, err = skt:recv() 74 | if not r then return nil, err, t end 75 | table.insert(t,r) 76 | end 77 | return t 78 | end 79 | zconnect = function(skt, addr) 80 | if type(addr) == 'table' then 81 | for i,a in ipairs(addr) do 82 | local ok, err = skt:connect(a) 83 | if not ok then return nil, err, i end 84 | end 85 | return true 86 | end 87 | return skt:connect(addr) 88 | end 89 | zbind = function(skt, addr) 90 | if type(addr) == 'table' then 91 | for i,a in ipairs(addr) do 92 | local ok, err = skt:bind(a) 93 | if not ok then return nil, err, i end 94 | end 95 | return true 96 | end 97 | return skt:bind(addr) 98 | end 99 | end 100 | 101 | zrecv = function(skt) 102 | local ok, err, t = zrecv_all(skt) 103 | if not ok then 104 | if t and t[1] then return t[1] end 105 | return nil, err 106 | end 107 | return ok[1] 108 | end 109 | 110 | return { 111 | zmq = zmq; 112 | threads = zthreads; 113 | poller = zpoller; 114 | connect = zconnect; 115 | bind = zbind; 116 | recv_all = zrecv_all; 117 | recv = zrecv; 118 | strerror = zstrerror; 119 | errcode = zerrcode; 120 | assert = zassert; 121 | ETERM = ETERM; 122 | is_ctx = is_ctx; 123 | } -------------------------------------------------------------------------------- /lua/log/writer/net/zmq/_private/impl.lua: -------------------------------------------------------------------------------- 1 | local Log = require "log" 2 | local Z = require "log.writer.net.zmq._private.compat" 3 | 4 | local zmq, zthreads = Z.zmq, Z.threads 5 | local zstrerror, zassert = Z.strerror, Z.assert 6 | local ETERM = Z.ETERM 7 | local zconnect, zbind = Z.connect, Z.bind 8 | 9 | local log_ctx 10 | 11 | local function context(ctx) 12 | -- we have to use same context for all writers 13 | if ctx and log_ctx then assert(ctx == log_ctx) end 14 | 15 | if log_ctx then return log_ctx end 16 | 17 | log_ctx = ctx or (zthreads and zthreads.get_parent_ctx()) or zassert(zmq.init(1)) 18 | 19 | return log_ctx 20 | end 21 | 22 | local function socket(ctx, stype, is_srv, addr, timeout) 23 | local stypes = { 24 | PUSH = zmq.PUSH; 25 | PUB = zmq.PUB; 26 | } 27 | stype = assert(stypes[stype], 'Unsupported socket type') 28 | timeout = timeout or 100 29 | ctx = context(ctx) 30 | 31 | local skt = ctx:socket(stype) 32 | if ctx.autoclose then ctx:autoclose(skt) end 33 | skt:set_sndtimeo(timeout) 34 | skt:set_linger(timeout) 35 | if is_srv then zassert(zbind(skt, addr)) 36 | else zassert(zconnect(skt, addr)) end 37 | if not ctx.autoclose then 38 | Log.add_cleanup(function() skt:close() end) 39 | end 40 | return skt 41 | end 42 | 43 | local function init(stype, is_srv) 44 | local M = {} 45 | 46 | function M.new(ctx, addr, timeout) 47 | if ctx and not Z.is_ctx(ctx) then 48 | ctx, addr, timeout = nil, ctx, addr 49 | end 50 | 51 | local skt = socket(ctx, stype, is_srv, addr, timeout) 52 | return function(fmt, ...) skt:send((fmt(...))) end 53 | end 54 | 55 | return M 56 | end 57 | 58 | return { 59 | init = init; 60 | context = context; 61 | } -------------------------------------------------------------------------------- /lua/log/writer/net/zmq/pub.lua: -------------------------------------------------------------------------------- 1 | return require "log.writer.net.zmq._private.impl".init('PUB', false) -------------------------------------------------------------------------------- /lua/log/writer/net/zmq/push.lua: -------------------------------------------------------------------------------- 1 | return require "log.writer.net.zmq._private.impl".init('PUSH', false) -------------------------------------------------------------------------------- /lua/log/writer/net/zmq/srv/pub.lua: -------------------------------------------------------------------------------- 1 | return require "log.writer.net.zmq._private.impl".init('PUB', true) -------------------------------------------------------------------------------- /lua/log/writer/prefix.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | function M.new(prefix, writer) 4 | return function(fmt, msg, lvl, now) 5 | writer(fmt, prefix .. msg, lvl, now) 6 | end 7 | end 8 | 9 | return M 10 | -------------------------------------------------------------------------------- /lua/log/writer/stderr.lua: -------------------------------------------------------------------------------- 1 | local io = require "io" 2 | 3 | local M = {} 4 | 5 | function M.new() 6 | return function(fmt, ...) io.stderr:write(fmt(...),'\n') end 7 | end 8 | 9 | return M -------------------------------------------------------------------------------- /lua/log/writer/stdout.lua: -------------------------------------------------------------------------------- 1 | local io = require "io" 2 | 3 | local M = {} 4 | 5 | function M.new() 6 | return function (fmt,...) io.stdout:write(fmt(...),'\n') end 7 | end 8 | 9 | return M -------------------------------------------------------------------------------- /rockspecs/lua-log-0.1.0-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-log" 2 | version = "0.1.0-1" 3 | source = { 4 | url = "https://github.com/moteus/lua-log/archive/v0.1.0.zip", 5 | dir = "lua-log-0.1.0", 6 | } 7 | 8 | description = { 9 | summary = "Asynchronous logging library", 10 | detailed = [[ 11 | ]], 12 | homepage = "https://github.com/moteus/lua-log", 13 | -- license = "" 14 | } 15 | 16 | dependencies = { 17 | "lua >= 5.1", 18 | "date >= 2.0", 19 | } 20 | 21 | build = { 22 | type = "builtin", 23 | copy_directories = { 24 | "examples", 25 | "utils", 26 | }, 27 | modules = { 28 | ["log" ] = "lua/log.lua", 29 | ["log.formatter.concat" ] = "lua/log/formatter/concat.lua", 30 | ["log.formatter.format" ] = "lua/log/formatter/format.lua", 31 | ["log.logformat.default" ] = "lua/log/logformat/default.lua", 32 | ["log.logformat.proxy" ] = "lua/log/logformat/proxy.lua", 33 | ["log.logformat.proxy.pack" ] = "lua/log/logformat/proxy/pack.lua", 34 | ["log.logformat.syslog" ] = "lua/log/logformat/syslog.lua", 35 | ["log.writer.async.udp" ] = "lua/log/writer/async/udp.lua", 36 | ["log.writer.async.zmq" ] = "lua/log/writer/async/zmq.lua", 37 | ["log.writer.console" ] = "lua/log/writer/console.lua", 38 | ["log.writer.console.color" ] = "lua/log/writer/console/color.lua", 39 | ["log.writer.file" ] = "lua/log/writer/file.lua", 40 | ["log.writer.file.by_day" ] = "lua/log/writer/file/by_day.lua", 41 | ["log.writer.file.private.impl"] = "lua/log/writer/file/private/impl.lua", 42 | ["log.writer.file.roll" ] = "lua/log/writer/file/roll.lua", 43 | ["log.writer.filter" ] = "lua/log/writer/filter.lua", 44 | ["log.writer.filter.lvl.eq" ] = "lua/log/writer/filter/lvl/eq.lua", 45 | ["log.writer.filter.lvl.le" ] = "lua/log/writer/filter/lvl/le.lua", 46 | ["log.writer.format" ] = "lua/log/writer/format.lua", 47 | ["log.writer.list" ] = "lua/log/writer/list.lua", 48 | ["log.writer.net.smtp" ] = "lua/log/writer/net/smtp.lua", 49 | ["log.writer.net.udp" ] = "lua/log/writer/net/udp.lua", 50 | ["log.writer.net.zmq" ] = "lua/log/writer/net/zmq.lua", 51 | ["log.writer.net.zmq._private" ] = "lua/log/writer/net/zmq/_private.lua", 52 | ["log.writer.net.zmq.pub" ] = "lua/log/writer/net/zmq/pub.lua", 53 | ["log.writer.net.zmq.push" ] = "lua/log/writer/net/zmq/push.lua", 54 | ["log.writer.net.zmq.srv.pub" ] = "lua/log/writer/net/zmq/srv/pub.lua", 55 | ["log.writer.stderr" ] = "lua/log/writer/stderr.lua", 56 | ["log.writer.stdout" ] = "lua/log/writer/stdout.lua", 57 | } 58 | } 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /rockspecs/lua-log-0.1.1-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-log" 2 | version = "0.1.1-1" 3 | source = { 4 | url = "https://github.com/moteus/lua-log/archive/v0.1.1.zip", 5 | dir = "lua-log-0.1.1", 6 | } 7 | 8 | description = { 9 | summary = "Asynchronous logging library", 10 | detailed = [[ 11 | ]], 12 | homepage = "https://github.com/moteus/lua-log", 13 | -- license = "" 14 | } 15 | 16 | dependencies = { 17 | "lua >= 5.1", 18 | "date >= 2.0", 19 | } 20 | 21 | build = { 22 | type = "builtin", 23 | copy_directories = { 24 | "examples", 25 | "utils", 26 | }, 27 | modules = { 28 | ["log" ] = "lua/log.lua", 29 | ["log.formatter.concat" ] = "lua/log/formatter/concat.lua", 30 | ["log.formatter.format" ] = "lua/log/formatter/format.lua", 31 | ["log.logformat.default" ] = "lua/log/logformat/default.lua", 32 | ["log.logformat.proxy" ] = "lua/log/logformat/proxy.lua", 33 | ["log.logformat.proxy.pack" ] = "lua/log/logformat/proxy/pack.lua", 34 | ["log.logformat.syslog" ] = "lua/log/logformat/syslog.lua", 35 | ["log.writer.async.udp" ] = "lua/log/writer/async/udp.lua", 36 | ["log.writer.async.zmq" ] = "lua/log/writer/async/zmq.lua", 37 | ["log.writer.console" ] = "lua/log/writer/console.lua", 38 | ["log.writer.console.color" ] = "lua/log/writer/console/color.lua", 39 | ["log.writer.file" ] = "lua/log/writer/file.lua", 40 | ["log.writer.file.by_day" ] = "lua/log/writer/file/by_day.lua", 41 | ["log.writer.file.private.impl"] = "lua/log/writer/file/private/impl.lua", 42 | ["log.writer.file.roll" ] = "lua/log/writer/file/roll.lua", 43 | ["log.writer.filter" ] = "lua/log/writer/filter.lua", 44 | ["log.writer.filter.lvl.eq" ] = "lua/log/writer/filter/lvl/eq.lua", 45 | ["log.writer.filter.lvl.le" ] = "lua/log/writer/filter/lvl/le.lua", 46 | ["log.writer.format" ] = "lua/log/writer/format.lua", 47 | ["log.writer.list" ] = "lua/log/writer/list.lua", 48 | ["log.writer.net.smtp" ] = "lua/log/writer/net/smtp.lua", 49 | ["log.writer.net.udp" ] = "lua/log/writer/net/udp.lua", 50 | ["log.writer.net.zmq" ] = "lua/log/writer/net/zmq.lua", 51 | ["log.writer.net.zmq._private" ] = "lua/log/writer/net/zmq/_private.lua", 52 | ["log.writer.net.zmq.pub" ] = "lua/log/writer/net/zmq/pub.lua", 53 | ["log.writer.net.zmq.push" ] = "lua/log/writer/net/zmq/push.lua", 54 | ["log.writer.net.zmq.srv.pub" ] = "lua/log/writer/net/zmq/srv/pub.lua", 55 | ["log.writer.stderr" ] = "lua/log/writer/stderr.lua", 56 | ["log.writer.stdout" ] = "lua/log/writer/stdout.lua", 57 | } 58 | } 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /rockspecs/lua-log-0.1.2-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-log" 2 | version = "0.1.2-1" 3 | source = { 4 | url = "https://github.com/moteus/lua-log/archive/v0.1.2.zip", 5 | dir = "lua-log-0.1.2", 6 | } 7 | 8 | description = { 9 | summary = "Asynchronous logging library", 10 | detailed = [[ 11 | ]], 12 | homepage = "https://github.com/moteus/lua-log", 13 | license = "MIT/X11", 14 | } 15 | 16 | dependencies = { 17 | "lua >= 5.1", 18 | "date >= 2.0", 19 | -- "llthread >= 1.2", 20 | -- "luasocket >= 2.0.1", 21 | -- "lzmq >= 0.1.0", 22 | -- "sendmail >= 0.1.0", 23 | -- "lanes >= 3.5", 24 | -- "ansicolors >= 1.0", 25 | } 26 | 27 | build = { 28 | type = "builtin", 29 | copy_directories = { 30 | "examples", 31 | "utils", 32 | "doc", 33 | }, 34 | modules = { 35 | ["log" ] = "lua/log.lua", 36 | ["log.formatter.concat" ] = "lua/log/formatter/concat.lua", 37 | ["log.formatter.format" ] = "lua/log/formatter/format.lua", 38 | ["log.formatter.mix" ] = "lua/log/formatter/mix.lua", 39 | ["log.logformat.default" ] = "lua/log/logformat/default.lua", 40 | ["log.logformat.proxy" ] = "lua/log/logformat/proxy.lua", 41 | ["log.logformat.proxy.pack" ] = "lua/log/logformat/proxy/pack.lua", 42 | ["log.logformat.syslog" ] = "lua/log/logformat/syslog.lua", 43 | ["log.writer.async._private.server" ] = "lua/log/writer/async/_private/server.lua", 44 | ["log.writer.async.lane" ] = "lua/log/writer/async/lane.lua", 45 | ["log.writer.async.server.lane"] = "lua/log/writer/async/server/lane.lua", 46 | ["log.writer.async.server.udp" ] = "lua/log/writer/async/server/udp.lua", 47 | ["log.writer.async.server.zmq" ] = "lua/log/writer/async/server/zmq.lua", 48 | ["log.writer.async.udp" ] = "lua/log/writer/async/udp.lua", 49 | ["log.writer.async.zmq" ] = "lua/log/writer/async/zmq.lua", 50 | ["log.writer.console" ] = "lua/log/writer/console.lua", 51 | ["log.writer.console.color" ] = "lua/log/writer/console/color.lua", 52 | ["log.writer.file" ] = "lua/log/writer/file.lua", 53 | ["log.writer.file.by_day" ] = "lua/log/writer/file/by_day.lua", 54 | ["log.writer.file.private.impl"] = "lua/log/writer/file/private/impl.lua", 55 | ["log.writer.file.roll" ] = "lua/log/writer/file/roll.lua", 56 | ["log.writer.filter" ] = "lua/log/writer/filter.lua", 57 | ["log.writer.filter.lvl.eq" ] = "lua/log/writer/filter/lvl/eq.lua", 58 | ["log.writer.filter.lvl.le" ] = "lua/log/writer/filter/lvl/le.lua", 59 | ["log.writer.format" ] = "lua/log/writer/format.lua", 60 | ["log.writer.list" ] = "lua/log/writer/list.lua", 61 | ["log.writer.net.server.udp" ] = "lua/log/writer/net/server/udp.lua", 62 | ["log.writer.net.server.zmq" ] = "lua/log/writer/net/server/zmq.lua", 63 | ["log.writer.net.smtp" ] = "lua/log/writer/net/smtp.lua", 64 | ["log.writer.net.udp" ] = "lua/log/writer/net/udp.lua", 65 | ["log.writer.net.zmq" ] = "lua/log/writer/net/zmq.lua", 66 | ["log.writer.net.zmq._private.compat" ] = "lua/log/writer/net/zmq/_private/compat.lua", 67 | ["log.writer.net.zmq._private.impl" ] = "lua/log/writer/net/zmq/_private/impl.lua", 68 | ["log.writer.net.zmq.pub" ] = "lua/log/writer/net/zmq/pub.lua", 69 | ["log.writer.net.zmq.push" ] = "lua/log/writer/net/zmq/push.lua", 70 | ["log.writer.net.zmq.srv.pub" ] = "lua/log/writer/net/zmq/srv/pub.lua", 71 | ["log.writer.stderr" ] = "lua/log/writer/stderr.lua", 72 | ["log.writer.stdout" ] = "lua/log/writer/stdout.lua", 73 | } 74 | } 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /rockspecs/lua-log-0.1.3-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-log" 2 | version = "0.1.3-1" 3 | source = { 4 | url = "https://github.com/moteus/lua-log/archive/v0.1.3.zip", 5 | dir = "lua-log-0.1.3", 6 | } 7 | 8 | description = { 9 | summary = "Asynchronous logging library", 10 | detailed = [[ 11 | ]], 12 | homepage = "https://github.com/moteus/lua-log", 13 | license = "MIT/X11", 14 | } 15 | 16 | dependencies = { 17 | "lua >= 5.1", 18 | "date >= 2.0", 19 | -- "llthread >= 1.2", 20 | -- "luasocket >= 2.0.1", 21 | -- "lzmq >= 0.1.0", 22 | -- "sendmail >= 0.1.0", 23 | -- "lanes >= 3.5", 24 | -- "ansicolors >= 1.0", 25 | } 26 | 27 | build = { 28 | type = "builtin", 29 | copy_directories = { 30 | "examples", 31 | "utils", 32 | "doc", 33 | }, 34 | modules = { 35 | ["log" ] = "lua/log.lua", 36 | ["log.formatter.concat" ] = "lua/log/formatter/concat.lua", 37 | ["log.formatter.format" ] = "lua/log/formatter/format.lua", 38 | ["log.formatter.mix" ] = "lua/log/formatter/mix.lua", 39 | ["log.logformat.default" ] = "lua/log/logformat/default.lua", 40 | ["log.logformat.proxy" ] = "lua/log/logformat/proxy.lua", 41 | ["log.logformat.proxy.pack" ] = "lua/log/logformat/proxy/pack.lua", 42 | ["log.logformat.syslog" ] = "lua/log/logformat/syslog.lua", 43 | ["log.writer.async._private.server" ] = "lua/log/writer/async/_private/server.lua", 44 | ["log.writer.async.lane" ] = "lua/log/writer/async/lane.lua", 45 | ["log.writer.async.server.lane"] = "lua/log/writer/async/server/lane.lua", 46 | ["log.writer.async.server.udp" ] = "lua/log/writer/async/server/udp.lua", 47 | ["log.writer.async.server.zmq" ] = "lua/log/writer/async/server/zmq.lua", 48 | ["log.writer.async.udp" ] = "lua/log/writer/async/udp.lua", 49 | ["log.writer.async.zmq" ] = "lua/log/writer/async/zmq.lua", 50 | ["log.writer.console" ] = "lua/log/writer/console.lua", 51 | ["log.writer.console.color" ] = "lua/log/writer/console/color.lua", 52 | ["log.writer.file" ] = "lua/log/writer/file.lua", 53 | ["log.writer.file.by_day" ] = "lua/log/writer/file/by_day.lua", 54 | ["log.writer.file.private.impl"] = "lua/log/writer/file/private/impl.lua", 55 | ["log.writer.file.roll" ] = "lua/log/writer/file/roll.lua", 56 | ["log.writer.filter" ] = "lua/log/writer/filter.lua", 57 | ["log.writer.filter.lvl.eq" ] = "lua/log/writer/filter/lvl/eq.lua", 58 | ["log.writer.filter.lvl.le" ] = "lua/log/writer/filter/lvl/le.lua", 59 | ["log.writer.format" ] = "lua/log/writer/format.lua", 60 | ["log.writer.list" ] = "lua/log/writer/list.lua", 61 | ["log.writer.net.server.udp" ] = "lua/log/writer/net/server/udp.lua", 62 | ["log.writer.net.server.zmq" ] = "lua/log/writer/net/server/zmq.lua", 63 | ["log.writer.net.smtp" ] = "lua/log/writer/net/smtp.lua", 64 | ["log.writer.net.udp" ] = "lua/log/writer/net/udp.lua", 65 | ["log.writer.net.zmq" ] = "lua/log/writer/net/zmq.lua", 66 | ["log.writer.net.zmq._private.compat" ] = "lua/log/writer/net/zmq/_private/compat.lua", 67 | ["log.writer.net.zmq._private.impl" ] = "lua/log/writer/net/zmq/_private/impl.lua", 68 | ["log.writer.net.zmq.pub" ] = "lua/log/writer/net/zmq/pub.lua", 69 | ["log.writer.net.zmq.push" ] = "lua/log/writer/net/zmq/push.lua", 70 | ["log.writer.net.zmq.srv.pub" ] = "lua/log/writer/net/zmq/srv/pub.lua", 71 | ["log.writer.stderr" ] = "lua/log/writer/stderr.lua", 72 | ["log.writer.stdout" ] = "lua/log/writer/stdout.lua", 73 | } 74 | } 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /rockspecs/lua-log-0.1.4-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-log" 2 | version = "0.1.4-1" 3 | source = { 4 | url = "https://github.com/moteus/lua-log/archive/v0.1.4.zip", 5 | dir = "lua-log-0.1.4", 6 | } 7 | 8 | description = { 9 | summary = "Asynchronous logging library", 10 | detailed = [[ 11 | ]], 12 | homepage = "https://github.com/moteus/lua-log", 13 | license = "MIT/X11", 14 | } 15 | 16 | dependencies = { 17 | "lua >= 5.1", 18 | "date >= 2.0", 19 | -- "llthread >= 1.2", 20 | -- "luasocket >= 2.0.1", 21 | -- "lzmq >= 0.1.0", 22 | -- "sendmail >= 0.1.0", 23 | -- "lanes >= 3.5", 24 | -- "ansicolors >= 1.0", 25 | } 26 | 27 | build = { 28 | type = "builtin", 29 | copy_directories = { 30 | "examples", 31 | "utils", 32 | "doc", 33 | }, 34 | modules = { 35 | ["log" ] = "lua/log.lua", 36 | ["log.formatter.concat" ] = "lua/log/formatter/concat.lua", 37 | ["log.formatter.format" ] = "lua/log/formatter/format.lua", 38 | ["log.formatter.mix" ] = "lua/log/formatter/mix.lua", 39 | ["log.logformat.default" ] = "lua/log/logformat/default.lua", 40 | ["log.logformat.proxy" ] = "lua/log/logformat/proxy.lua", 41 | ["log.logformat.proxy.pack" ] = "lua/log/logformat/proxy/pack.lua", 42 | ["log.logformat.syslog" ] = "lua/log/logformat/syslog.lua", 43 | ["log.writer.async._private.server" ] = "lua/log/writer/async/_private/server.lua", 44 | ["log.writer.async.lane" ] = "lua/log/writer/async/lane.lua", 45 | ["log.writer.async.server.lane"] = "lua/log/writer/async/server/lane.lua", 46 | ["log.writer.async.server.udp" ] = "lua/log/writer/async/server/udp.lua", 47 | ["log.writer.async.server.zmq" ] = "lua/log/writer/async/server/zmq.lua", 48 | ["log.writer.async.udp" ] = "lua/log/writer/async/udp.lua", 49 | ["log.writer.async.zmq" ] = "lua/log/writer/async/zmq.lua", 50 | ["log.writer.console" ] = "lua/log/writer/console.lua", 51 | ["log.writer.console.color" ] = "lua/log/writer/console/color.lua", 52 | ["log.writer.file" ] = "lua/log/writer/file.lua", 53 | ["log.writer.file.by_day" ] = "lua/log/writer/file/by_day.lua", 54 | ["log.writer.file.private.impl"] = "lua/log/writer/file/private/impl.lua", 55 | ["log.writer.file.roll" ] = "lua/log/writer/file/roll.lua", 56 | ["log.writer.filter" ] = "lua/log/writer/filter.lua", 57 | ["log.writer.filter.lvl.eq" ] = "lua/log/writer/filter/lvl/eq.lua", 58 | ["log.writer.filter.lvl.le" ] = "lua/log/writer/filter/lvl/le.lua", 59 | ["log.writer.format" ] = "lua/log/writer/format.lua", 60 | ["log.writer.list" ] = "lua/log/writer/list.lua", 61 | ["log.writer.net.server.udp" ] = "lua/log/writer/net/server/udp.lua", 62 | ["log.writer.net.server.zmq" ] = "lua/log/writer/net/server/zmq.lua", 63 | ["log.writer.net.smtp" ] = "lua/log/writer/net/smtp.lua", 64 | ["log.writer.net.udp" ] = "lua/log/writer/net/udp.lua", 65 | ["log.writer.net.zmq" ] = "lua/log/writer/net/zmq.lua", 66 | ["log.writer.net.zmq._private.compat" ] = "lua/log/writer/net/zmq/_private/compat.lua", 67 | ["log.writer.net.zmq._private.impl" ] = "lua/log/writer/net/zmq/_private/impl.lua", 68 | ["log.writer.net.zmq.pub" ] = "lua/log/writer/net/zmq/pub.lua", 69 | ["log.writer.net.zmq.push" ] = "lua/log/writer/net/zmq/push.lua", 70 | ["log.writer.net.zmq.srv.pub" ] = "lua/log/writer/net/zmq/srv/pub.lua", 71 | ["log.writer.stderr" ] = "lua/log/writer/stderr.lua", 72 | ["log.writer.stdout" ] = "lua/log/writer/stdout.lua", 73 | } 74 | } 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /rockspecs/lua-log-0.1.5-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-log" 2 | version = "0.1.5-1" 3 | source = { 4 | url = "https://github.com/moteus/lua-log/archive/v0.1.5.zip", 5 | dir = "lua-log-0.1.5", 6 | } 7 | 8 | description = { 9 | summary = "Asynchronous logging library", 10 | detailed = [[ 11 | ]], 12 | homepage = "https://github.com/moteus/lua-log", 13 | license = "MIT/X11", 14 | } 15 | 16 | dependencies = { 17 | "lua >= 5.1", 18 | "date >= 2.0", 19 | -- "llthread >= 1.2", 20 | -- "luasocket >= 2.0.1", 21 | -- "lzmq >= 0.4.2", 22 | -- "sendmail >= 0.1.0", 23 | -- "lanes >= 3.5", 24 | -- "ansicolors >= 1.0", 25 | } 26 | 27 | build = { 28 | type = "builtin", 29 | copy_directories = { 30 | "examples", 31 | "utils", 32 | "doc", 33 | }, 34 | modules = { 35 | ["log" ] = "lua/log.lua", 36 | ["log.formatter.concat" ] = "lua/log/formatter/concat.lua", 37 | ["log.formatter.format" ] = "lua/log/formatter/format.lua", 38 | ["log.formatter.mix" ] = "lua/log/formatter/mix.lua", 39 | ["log.logformat.default" ] = "lua/log/logformat/default.lua", 40 | ["log.logformat.proxy" ] = "lua/log/logformat/proxy.lua", 41 | ["log.logformat.proxy.pack" ] = "lua/log/logformat/proxy/pack.lua", 42 | ["log.logformat.syslog" ] = "lua/log/logformat/syslog.lua", 43 | ["log.writer.async._private.server" ] = "lua/log/writer/async/_private/server.lua", 44 | ["log.writer.async.lane" ] = "lua/log/writer/async/lane.lua", 45 | ["log.writer.async.server.lane"] = "lua/log/writer/async/server/lane.lua", 46 | ["log.writer.async.server.udp" ] = "lua/log/writer/async/server/udp.lua", 47 | ["log.writer.async.server.zmq" ] = "lua/log/writer/async/server/zmq.lua", 48 | ["log.writer.async.udp" ] = "lua/log/writer/async/udp.lua", 49 | ["log.writer.async.zmq" ] = "lua/log/writer/async/zmq.lua", 50 | ["log.writer.console" ] = "lua/log/writer/console.lua", 51 | ["log.writer.console.color" ] = "lua/log/writer/console/color.lua", 52 | ["log.writer.file" ] = "lua/log/writer/file.lua", 53 | ["log.writer.file.by_day" ] = "lua/log/writer/file/by_day.lua", 54 | ["log.writer.file.private.impl"] = "lua/log/writer/file/private/impl.lua", 55 | ["log.writer.file.roll" ] = "lua/log/writer/file/roll.lua", 56 | ["log.writer.filter" ] = "lua/log/writer/filter.lua", 57 | ["log.writer.filter.lvl.eq" ] = "lua/log/writer/filter/lvl/eq.lua", 58 | ["log.writer.filter.lvl.le" ] = "lua/log/writer/filter/lvl/le.lua", 59 | ["log.writer.format" ] = "lua/log/writer/format.lua", 60 | ["log.writer.list" ] = "lua/log/writer/list.lua", 61 | ["log.writer.net.server.udp" ] = "lua/log/writer/net/server/udp.lua", 62 | ["log.writer.net.server.zmq" ] = "lua/log/writer/net/server/zmq.lua", 63 | ["log.writer.net.smtp" ] = "lua/log/writer/net/smtp.lua", 64 | ["log.writer.net.udp" ] = "lua/log/writer/net/udp.lua", 65 | ["log.writer.net.zmq" ] = "lua/log/writer/net/zmq.lua", 66 | ["log.writer.net.zmq._private.compat" ] = "lua/log/writer/net/zmq/_private/compat.lua", 67 | ["log.writer.net.zmq._private.impl" ] = "lua/log/writer/net/zmq/_private/impl.lua", 68 | ["log.writer.net.zmq.pub" ] = "lua/log/writer/net/zmq/pub.lua", 69 | ["log.writer.net.zmq.push" ] = "lua/log/writer/net/zmq/push.lua", 70 | ["log.writer.net.zmq.srv.pub" ] = "lua/log/writer/net/zmq/srv/pub.lua", 71 | ["log.writer.stderr" ] = "lua/log/writer/stderr.lua", 72 | ["log.writer.stdout" ] = "lua/log/writer/stdout.lua", 73 | } 74 | } 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /rockspecs/lua-log-0.1.6-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-log" 2 | version = "0.1.6-1" 3 | source = { 4 | url = "https://github.com/moteus/lua-log/archive/v0.1.6.zip", 5 | dir = "lua-log-0.1.6", 6 | } 7 | 8 | description = { 9 | summary = "Asynchronous logging library", 10 | detailed = [[ 11 | ]], 12 | homepage = "https://github.com/moteus/lua-log", 13 | license = "MIT/X11", 14 | } 15 | 16 | dependencies = { 17 | "lua >= 5.1, < 5.4", 18 | "date >= 2.0", 19 | -- "lpeg >= 0.10.0", 20 | -- "llthread >= 1.2", 21 | -- "luasocket >= 2.0.1", 22 | -- "lzmq >= 0.4.2", 23 | -- "sendmail >= 0.1.0", 24 | -- "lanes >= 3.5", 25 | -- "ansicolors >= 1.0", 26 | } 27 | 28 | build = { 29 | type = "builtin", 30 | copy_directories = { 31 | "examples", 32 | "utils", 33 | "doc", 34 | }, 35 | modules = { 36 | ["log" ] = "lua/log.lua", 37 | ["log.formatter.concat" ] = "lua/log/formatter/concat.lua", 38 | ["log.formatter.default" ] = "lua/log/formatter/default.lua", 39 | ["log.formatter.format" ] = "lua/log/formatter/format.lua", 40 | ["log.formatter.pformat" ] = "lua/log/formatter/pformat.lua", 41 | ["log.formatter.mix" ] = "lua/log/formatter/mix.lua", 42 | ["log.logformat.default" ] = "lua/log/logformat/default.lua", 43 | ["log.logformat.proxy" ] = "lua/log/logformat/proxy.lua", 44 | ["log.logformat.proxy.pack" ] = "lua/log/logformat/proxy/pack.lua", 45 | ["log.logformat.syslog" ] = "lua/log/logformat/syslog.lua", 46 | ["log.writer.async._private.server" ] = "lua/log/writer/async/_private/server.lua", 47 | ["log.writer.async.lane" ] = "lua/log/writer/async/lane.lua", 48 | ["log.writer.async.server.lane"] = "lua/log/writer/async/server/lane.lua", 49 | ["log.writer.async.server.udp" ] = "lua/log/writer/async/server/udp.lua", 50 | ["log.writer.async.server.zmq" ] = "lua/log/writer/async/server/zmq.lua", 51 | ["log.writer.async.udp" ] = "lua/log/writer/async/udp.lua", 52 | ["log.writer.async.zmq" ] = "lua/log/writer/async/zmq.lua", 53 | ["log.writer.console" ] = "lua/log/writer/console.lua", 54 | ["log.writer.console.color" ] = "lua/log/writer/console/color.lua", 55 | ["log.writer.file" ] = "lua/log/writer/file.lua", 56 | ["log.writer.file.by_day" ] = "lua/log/writer/file/by_day.lua", 57 | ["log.writer.file.private.impl"] = "lua/log/writer/file/private/impl.lua", 58 | ["log.writer.file.roll" ] = "lua/log/writer/file/roll.lua", 59 | ["log.writer.filter" ] = "lua/log/writer/filter.lua", 60 | ["log.writer.filter.lvl.eq" ] = "lua/log/writer/filter/lvl/eq.lua", 61 | ["log.writer.filter.lvl.le" ] = "lua/log/writer/filter/lvl/le.lua", 62 | ["log.writer.format" ] = "lua/log/writer/format.lua", 63 | ["log.writer.list" ] = "lua/log/writer/list.lua", 64 | ["log.writer.net.server.udp" ] = "lua/log/writer/net/server/udp.lua", 65 | ["log.writer.net.server.zmq" ] = "lua/log/writer/net/server/zmq.lua", 66 | ["log.writer.net.smtp" ] = "lua/log/writer/net/smtp.lua", 67 | ["log.writer.net.udp" ] = "lua/log/writer/net/udp.lua", 68 | ["log.writer.net.zmq" ] = "lua/log/writer/net/zmq.lua", 69 | ["log.writer.net.zmq._private.compat" ] = "lua/log/writer/net/zmq/_private/compat.lua", 70 | ["log.writer.net.zmq._private.impl" ] = "lua/log/writer/net/zmq/_private/impl.lua", 71 | ["log.writer.net.zmq.pub" ] = "lua/log/writer/net/zmq/pub.lua", 72 | ["log.writer.net.zmq.push" ] = "lua/log/writer/net/zmq/push.lua", 73 | ["log.writer.net.zmq.srv.pub" ] = "lua/log/writer/net/zmq/srv/pub.lua", 74 | ["log.writer.prefix" ] = "lua/log/writer/prefix.lua", 75 | ["log.writer.stderr" ] = "lua/log/writer/stderr.lua", 76 | ["log.writer.stdout" ] = "lua/log/writer/stdout.lua", 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /rockspecs/lua-log-scm-0.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-log" 2 | version = "scm-0" 3 | source = { 4 | url = "https://github.com/moteus/lua-log/archive/master.zip", 5 | dir = "lua-log-master", 6 | } 7 | 8 | description = { 9 | summary = "Asynchronous logging library", 10 | detailed = [[ 11 | ]], 12 | homepage = "https://github.com/moteus/lua-log", 13 | license = "MIT/X11", 14 | } 15 | 16 | dependencies = { 17 | "lua >= 5.1, < 5.4", 18 | "date >= 2.0", 19 | -- "lpeg >= 0.10.0", 20 | -- "llthread >= 1.2", 21 | -- "luasocket >= 2.0.1", 22 | -- "lzmq >= 0.4.2", 23 | -- "sendmail >= 0.1.0", 24 | -- "lanes >= 3.5", 25 | -- "ansicolors >= 1.0", 26 | } 27 | 28 | build = { 29 | type = "builtin", 30 | copy_directories = { 31 | "examples", 32 | "utils", 33 | "doc", 34 | }, 35 | modules = { 36 | ["log" ] = "lua/log.lua", 37 | ["log.formatter.concat" ] = "lua/log/formatter/concat.lua", 38 | ["log.formatter.default" ] = "lua/log/formatter/default.lua", 39 | ["log.formatter.format" ] = "lua/log/formatter/format.lua", 40 | ["log.formatter.pformat" ] = "lua/log/formatter/pformat.lua", 41 | ["log.formatter.mix" ] = "lua/log/formatter/mix.lua", 42 | ["log.logformat.default" ] = "lua/log/logformat/default.lua", 43 | ["log.logformat.proxy" ] = "lua/log/logformat/proxy.lua", 44 | ["log.logformat.proxy.pack" ] = "lua/log/logformat/proxy/pack.lua", 45 | ["log.logformat.syslog" ] = "lua/log/logformat/syslog.lua", 46 | ["log.writer.async._private.server" ] = "lua/log/writer/async/_private/server.lua", 47 | ["log.writer.async.lane" ] = "lua/log/writer/async/lane.lua", 48 | ["log.writer.async.server.lane"] = "lua/log/writer/async/server/lane.lua", 49 | ["log.writer.async.server.udp" ] = "lua/log/writer/async/server/udp.lua", 50 | ["log.writer.async.server.zmq" ] = "lua/log/writer/async/server/zmq.lua", 51 | ["log.writer.async.udp" ] = "lua/log/writer/async/udp.lua", 52 | ["log.writer.async.zmq" ] = "lua/log/writer/async/zmq.lua", 53 | ["log.writer.console" ] = "lua/log/writer/console.lua", 54 | ["log.writer.console.color" ] = "lua/log/writer/console/color.lua", 55 | ["log.writer.file" ] = "lua/log/writer/file.lua", 56 | ["log.writer.file.by_day" ] = "lua/log/writer/file/by_day.lua", 57 | ["log.writer.file.private.impl"] = "lua/log/writer/file/private/impl.lua", 58 | ["log.writer.file.roll" ] = "lua/log/writer/file/roll.lua", 59 | ["log.writer.filter" ] = "lua/log/writer/filter.lua", 60 | ["log.writer.filter.lvl.eq" ] = "lua/log/writer/filter/lvl/eq.lua", 61 | ["log.writer.filter.lvl.le" ] = "lua/log/writer/filter/lvl/le.lua", 62 | ["log.writer.format" ] = "lua/log/writer/format.lua", 63 | ["log.writer.list" ] = "lua/log/writer/list.lua", 64 | ["log.writer.net.server.udp" ] = "lua/log/writer/net/server/udp.lua", 65 | ["log.writer.net.server.zmq" ] = "lua/log/writer/net/server/zmq.lua", 66 | ["log.writer.net.smtp" ] = "lua/log/writer/net/smtp.lua", 67 | ["log.writer.net.udp" ] = "lua/log/writer/net/udp.lua", 68 | ["log.writer.net.zmq" ] = "lua/log/writer/net/zmq.lua", 69 | ["log.writer.net.zmq._private.compat" ] = "lua/log/writer/net/zmq/_private/compat.lua", 70 | ["log.writer.net.zmq._private.impl" ] = "lua/log/writer/net/zmq/_private/impl.lua", 71 | ["log.writer.net.zmq.pub" ] = "lua/log/writer/net/zmq/pub.lua", 72 | ["log.writer.net.zmq.push" ] = "lua/log/writer/net/zmq/push.lua", 73 | ["log.writer.net.zmq.srv.pub" ] = "lua/log/writer/net/zmq/srv/pub.lua", 74 | ["log.writer.prefix" ] = "lua/log/writer/prefix.lua", 75 | ["log.writer.stderr" ] = "lua/log/writer/stderr.lua", 76 | ["log.writer.stdout" ] = "lua/log/writer/stdout.lua", 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /spec/basic_spec.lua: -------------------------------------------------------------------------------- 1 | package.path = './spec/?.lua;../lua/?.lua;'..package.path 2 | 3 | local path = require "path" 4 | local utils = require "utils" 5 | 6 | local LUA, ARGS = utils.lua_args(arg) 7 | local PATH = path.fullpath(".") 8 | 9 | local DATE_PAT = "%d%d%d%d%-%d%d%-%d%d %d%d:%d%d:%d%d" 10 | local TESTDIR = ".test_log" 11 | 12 | local function exec_file(file) 13 | assert(path.isfile(file)) 14 | return utils.exec(PATH, LUA, '%s', path.quote(file)) 15 | end 16 | 17 | local function exec_code(src) 18 | local tmpfile = assert(path.tmpname()) 19 | local f = assert(utils.write_file(tmpfile, src)) 20 | local a, b, c = exec_file(tmpfile) 21 | path.remove(tmpfile) 22 | return a, b, c 23 | end 24 | 25 | do local s = require("say") 26 | 27 | local function is_match(state, arguments) 28 | local pat, str = arguments[1], arguments[2] 29 | 30 | if type(pat) ~= "string" and type(str) ~= "string" then 31 | return false 32 | end 33 | 34 | return (not not string.match(str, pat)) 35 | end 36 | 37 | s:set("assertion.match.positive", "String `%s` expected match to \n`%s`") 38 | s:set("assertion.match.negative", "String\n`%s` not expected match to \n`%s`") 39 | assert:register("assertion", "match", is_match, "assertion.match.positive", "assertion.match.negative") 40 | 41 | end 42 | 43 | describe("writers", function() 44 | 45 | it('basic format', function() 46 | local ok, status, msg = exec_code[[ 47 | local LOG = require"log".new('trace', 48 | require "log.writer.stdout".new() 49 | ) 50 | 51 | LOG.emerg("can not allocate memory") 52 | LOG.alert("can not allocate memory") 53 | LOG.fatal("can not allocate memory") 54 | LOG.error("file not found") 55 | LOG.warning("cache server is not started") 56 | LOG.notice("message has 2 file") 57 | LOG.info("new message is received") 58 | LOG.debug("message has 2 file") 59 | LOG.trace("message has 2 file") 60 | ]] 61 | assert.True(ok, msg) 62 | 63 | assert.match(DATE_PAT .. " %[EMERG%] can not allocate memory", msg) 64 | assert.match(DATE_PAT .. " %[ALERT%] can not allocate memory", msg) 65 | assert.match(DATE_PAT .. " %[FATAL%] can not allocate memory", msg) 66 | assert.match(DATE_PAT .. " %[ERROR%] file not found", msg) 67 | assert.match(DATE_PAT .. " %[WARNING%] cache server is not started", msg) 68 | assert.match(DATE_PAT .. " %[NOTICE%] message has 2 file", msg) 69 | assert.match(DATE_PAT .. " %[INFO%] new message is received", msg) 70 | assert.match(DATE_PAT .. " %[DEBUG%] message has 2 file", msg) 71 | assert.match(DATE_PAT .. " %[TRACE%] message has 2 file", msg) 72 | end) 73 | 74 | it('level', function() 75 | local ok, status, msg = exec_code[[ 76 | local LOG = require"log".new('notice', 77 | require "log.writer.stdout".new() 78 | ) 79 | 80 | LOG.emerg("can not allocate memory") 81 | LOG.alert("can not allocate memory") 82 | LOG.fatal("can not allocate memory") 83 | LOG.error("file not found") 84 | LOG.warning("cache server is not started") 85 | LOG.notice("message has 2 file") 86 | LOG.info("new message is received") 87 | LOG.debug("message has 2 file") 88 | LOG.trace("message has 2 file") 89 | ]] 90 | assert.True(ok, msg) 91 | 92 | assert.match (DATE_PAT .. " %[EMERG%] can not allocate memory", msg) 93 | assert.match (DATE_PAT .. " %[ALERT%] can not allocate memory", msg) 94 | assert.match (DATE_PAT .. " %[FATAL%] can not allocate memory", msg) 95 | assert.match (DATE_PAT .. " %[ERROR%] file not found", msg) 96 | assert.match (DATE_PAT .. " %[WARNING%] cache server is not started", msg) 97 | assert.match (DATE_PAT .. " %[NOTICE%] message has 2 file", msg) 98 | assert.not_match(DATE_PAT .. " %[INFO%] new message is received", msg) 99 | assert.not_match(DATE_PAT .. " %[DEBUG%] message has 2 file", msg) 100 | assert.not_match(DATE_PAT .. " %[TRACE%] message has 2 file", msg) 101 | end) 102 | 103 | it('formatter', function() 104 | local ok, status, msg = exec_code[[ 105 | local writer = require "log.writer.stdout".new() 106 | local LOG = require"log".new(writer, 107 | require "log.formatter.concat".new(':') 108 | ) 109 | local LOG_FMT = require"log".new(writer, 110 | require "log.formatter.format".new() 111 | ) 112 | LOG.info('new', 'message', 'is', 'received') 113 | LOG_FMT.notice("message has %d %s", 2, 'file') 114 | ]] 115 | assert.True(ok, msg) 116 | 117 | assert.match('new:message:is:received', msg) 118 | assert.match('message has 2 file', msg) 119 | end) 120 | 121 | it('async_zmq', function() 122 | local ok, status, msg = exec_code[[ 123 | local ztimer = require "lzmq.timer" 124 | 125 | local writer = require "log.writer.async.zmq".new('inproc://async.logger', 126 | "return require 'log.writer.stdout'.new()" 127 | ) 128 | ztimer.sleep(500) 129 | 130 | local LOG = require"log".new(writer) 131 | ztimer.sleep(500) 132 | 133 | LOG.fatal("can not allocate memory") 134 | 135 | ztimer.sleep(5000) 136 | 137 | require "lzmq.threads".context():destroy() 138 | ztimer.sleep(5000) 139 | ]] 140 | assert.True(ok, msg) 141 | 142 | assert.match('can not allocate memory', msg) 143 | end) 144 | 145 | it('async_udp', function() 146 | local ok, status, msg = exec_code[[ 147 | local writer = require "log.writer.async.udp".new('127.0.0.1', '5555', 148 | "return require 'log.writer.stdout'.new()" 149 | ) 150 | 151 | local LOG = require"log".new(writer) 152 | 153 | LOG.fatal("can not allocate memory") 154 | 155 | require 'lzmq.timer'.sleep(1000) 156 | ]] 157 | assert.True(ok, msg) 158 | 159 | assert.match('can not allocate memory', msg) 160 | end) 161 | 162 | if _G.jit then pending"FIXME: makes LuaLane work with LuaJIT" 163 | else it('async_lane', function() 164 | local ok, status, msg = exec_code[[ 165 | local writer = require "log.writer.async.lane".new('lane.logger', 166 | "return require 'log.writer.stdout'.new()" 167 | ) 168 | 169 | local LOG = require"log".new(writer) 170 | 171 | LOG.fatal("can not allocate memory") 172 | 173 | require 'lzmq.timer'.sleep(1000) 174 | ]] 175 | assert.True(ok, msg) 176 | 177 | assert.match('can not allocate memory', msg) 178 | end) end 179 | 180 | it('async_proxy', function() 181 | local ok, status, msg = exec_code[[ 182 | local zthreads = require "lzmq.threads" 183 | local ztimer = require 'lzmq.timer' 184 | 185 | -- create log thread 186 | local LOG = require"log".new( 187 | require "log.writer.async.zmq".new('inproc://async.logger', 188 | "return require 'log.writer.stdout'.new()" 189 | ) 190 | ) 191 | ztimer.sleep(500) 192 | 193 | -- log from separate thread via proxy 194 | local Thread = function() 195 | local LOG = require"log".new( 196 | require "log.writer.async.zmq".new('inproc://async.logger') 197 | ) 198 | 199 | LOG.error("(Thread) file not found") 200 | end 201 | 202 | local child_thread = zthreads.xrun(Thread):start() 203 | ztimer.sleep(500) 204 | 205 | LOG.fatal("can not allocate memory") 206 | 207 | child_thread:join() 208 | 209 | ztimer.sleep(5000) 210 | 211 | zthreads.context():destroy() 212 | 213 | ztimer.sleep(1500) 214 | ]] 215 | assert.True(ok, msg) 216 | 217 | assert.match('can not allocate memory', msg) 218 | assert.match('%(Thread%) file not found', msg) 219 | end) 220 | 221 | it('async_filter_le', function() 222 | local ok, status, msg = exec_code[[ 223 | local writer = require 'log.writer.stdout'.new() 224 | local Filter = require "log.writer.filter" 225 | 226 | local LOG = require"log".new( 227 | Filter.new('warning', writer) 228 | ) 229 | 230 | LOG.fatal("can not allocate memory") 231 | LOG.warning("cache server is not started") 232 | LOG.info("new message is received") 233 | 234 | require 'lzmq.timer'.sleep(1000) 235 | ]] 236 | assert.True(ok, msg) 237 | 238 | assert.match('can not allocate memory', msg) 239 | assert.match('cache server is not started', msg) 240 | assert.not_match('new message is received', msg) 241 | end) 242 | 243 | it('async_filter_eq', function() 244 | local ok, status, msg = exec_code[[ 245 | local writer = require 'log.writer.stdout'.new() 246 | local Filter = require "log.writer.filter.lvl.eq" 247 | 248 | local LOG = require"log".new( 249 | Filter.new('warning', writer) 250 | ) 251 | 252 | LOG.fatal("can not allocate memory") 253 | LOG.warning("cache server is not started") 254 | LOG.info("new message is received") 255 | 256 | require 'lzmq.timer'.sleep(1000) 257 | ]] 258 | assert.True(ok, msg) 259 | 260 | assert.not_match('can not allocate memory', msg) 261 | assert.match('cache server is not started', msg) 262 | assert.not_match('new message is received', msg) 263 | end) 264 | 265 | it('formatter_mix', function() 266 | local ok, status, msg = exec_code[[ 267 | local LOG = require"log".new('trace', 268 | require "log.writer.stdout".new(), 269 | require "log.formatter.mix".new() 270 | ) 271 | 272 | LOG.emerg("can not allocate memory") 273 | LOG.alert(function(str) return str end, "can not allocate memory") 274 | LOG.fatal("can not allocate %s", "memory") 275 | ]] 276 | assert.True(ok, msg) 277 | 278 | assert.match(DATE_PAT .. " %[EMERG%] can not allocate memory", msg) 279 | assert.match(DATE_PAT .. " %[ALERT%] can not allocate memory", msg) 280 | assert.match(DATE_PAT .. " %[FATAL%] can not allocate memory", msg) 281 | end) 282 | 283 | end) -------------------------------------------------------------------------------- /spec/utils.lua: -------------------------------------------------------------------------------- 1 | 2 | local function prequire(...) 3 | local ok, mod = pcall(require, ...) 4 | if not ok then return nil, mod end 5 | return mod, ... 6 | end 7 | 8 | local function vrequire(...) 9 | local errors = {} 10 | for i, n in ipairs{...} do 11 | local mod, err = prequire(n) 12 | if mod then return mod, err end 13 | errors[#errors + 1] = err 14 | end 15 | error(table.concat(errors, "\n\n")) 16 | end 17 | 18 | local path = require "path" 19 | 20 | local function read_file(n) 21 | local f, e = io.open(n, "r") 22 | if not f then return nil, e end 23 | local d, e = f:read("*all") 24 | f:close() 25 | return d, e 26 | end 27 | 28 | local function write_file(n, src) 29 | local f, e = io.open(n, "w+") 30 | if not f then return nil, e end 31 | local d, e = f:write(src) 32 | f:close() 33 | return d, e 34 | end 35 | 36 | local function mkfile(P, data) 37 | P = path.fullpath(P) 38 | path.mkdir(path.dirname(P)) 39 | local f, e = io.open(P, "w+b") 40 | if not f then return nil, err end 41 | if data then assert(f:write(data)) end 42 | f:close() 43 | return P 44 | end 45 | 46 | local function remove_dir(f) 47 | if not path.exists(f) then return end 48 | local mask = path.ensure_dir_end(f) 49 | path.each(mask, function(f) 50 | collectgarbage("collect") collectgarbage("collect") 51 | path.remove(f) 52 | end, {recurse = true, delay = true, reverse = true}) 53 | collectgarbage("collect") collectgarbage("collect") 54 | path.remove(f) 55 | end 56 | 57 | local function count_logs(f) 58 | local counter = 0 59 | path.each(path.join(f,"*.log"), function() 60 | counter = counter + 1 61 | end) 62 | return counter 63 | end 64 | 65 | ----------------------------------------------------------- 66 | local exec do 67 | 68 | local lua_version_t 69 | local function lua_version() 70 | if not lua_version_t then 71 | local version = rawget(_G,"_VERSION") 72 | local maj,min = version:match("^Lua (%d+)%.(%d+)$") 73 | if maj then lua_version_t = {tonumber(maj),tonumber(min)} 74 | elseif not math.mod then lua_version_t = {5,2} 75 | elseif table.pack and not pack then lua_version_t = {5,2} 76 | else lua_version_t = {5,2} end 77 | end 78 | return lua_version_t[1], lua_version_t[2] 79 | end 80 | 81 | local LUA_MAJOR, LUA_MINOR = lua_version() 82 | local LUA_VERSION = LUA_MAJOR * 100 + LUA_MINOR 83 | local LUA_52 = 502 84 | local IS_WINDOWS = package.config:sub(1,1) == '\\' 85 | 86 | local function read_file(n) 87 | local f, e = io.open(n, "r") 88 | if not f then return nil, e end 89 | local d, e = f:read("*all") 90 | f:close() 91 | return d, e 92 | end 93 | 94 | exec = function(cwd, cmd, ...) 95 | assert(cmd, 'No command was provided') 96 | local tmpfile = assert(path.tmpname()) 97 | 98 | cmd = path.quote(cmd) 99 | if ... then 100 | cmd = cmd .. ' ' .. string.format(...) .. ' ' 101 | if IS_WINDOWS then cmd = path.quote(cmd) end 102 | end 103 | cmd = cmd .. ' >' .. path.quote(tmpfile) .. ' 2>&1' 104 | 105 | local p 106 | if cwd and (cwd ~= "") and (cwd ~= ".") then 107 | p = path.currentdir() 108 | path.chdir(cwd) 109 | end 110 | 111 | local res1,res2,res2 = os.execute(cmd) 112 | if p then path.chdir(p) end 113 | 114 | local data = read_file(tmpfile) 115 | path.remove(tmpfile) 116 | 117 | if LUA_VERSION < LUA_52 then 118 | return res1==0, res1, data 119 | end 120 | 121 | return res1, res2, data 122 | end 123 | 124 | end 125 | ----------------------------------------------------------- 126 | 127 | local function lua_args(arg) 128 | local argv do 129 | argv = {} 130 | for i = -1000, 1000 do 131 | if arg[i] then argv[#argv + 1] = "[" .. i .. "]" .. arg[i] end 132 | end 133 | argv = table.concat(argv, ' ') 134 | end 135 | 136 | local args = {} 137 | 138 | for i = -1000, -1 do 139 | if arg[i] then args[#args + 1] = arg[i] end 140 | end 141 | 142 | local lua = assert(table.remove(args, 1), "Can not find Lua interpreter in command string:\n" .. argv) 143 | args = table.concat(args, ' ') 144 | return lua, args 145 | end 146 | 147 | return { 148 | exec = exec; 149 | read_file = read_file; 150 | write_file = write_file; 151 | lua_args = lua_args; 152 | remove_dir = remove_dir; 153 | mkfile = mkfile; 154 | count_logs = count_logs; 155 | } 156 | -------------------------------------------------------------------------------- /test/test_basic.lua: -------------------------------------------------------------------------------- 1 | local HAS_RUNNER = not not lunit 2 | local lunit = require "lunit" 3 | local TEST_CASE = assert(lunit.TEST_CASE) 4 | local skip = lunit.skip or function() end 5 | local path = require "path" 6 | local utils = require "utils" 7 | 8 | local LUA, ARGS = utils.lua_args(arg) 9 | local PATH = path.fullpath(".") 10 | 11 | local DATE_PAT = "%d%d%d%d%-%d%d%-%d%d %d%d:%d%d:%d%d" 12 | local TESTDIR = ".test_log" 13 | 14 | local function exec_file(file) 15 | assert(path.isfile(file)) 16 | return utils.exec(PATH, LUA, '%s %s', ARGS, path.quote(file)) 17 | end 18 | 19 | local function exec_code(src) 20 | local tmpfile = path.tmpname() 21 | local f = assert(utils.write_file(tmpfile, src)) 22 | local a, b, c = exec_file(tmpfile) 23 | path.remove(tmpfile) 24 | return a, b, c 25 | end 26 | 27 | local _ENV = TEST_CASE'basic' do 28 | 29 | function test() 30 | local ok, status, msg = exec_code[[ 31 | local LOG = require"log".new('trace', 32 | require "log.writer.stdout".new() 33 | ) 34 | 35 | LOG.emerg("can not allocate memory") 36 | LOG.alert("can not allocate memory") 37 | LOG.fatal("can not allocate memory") 38 | LOG.error("file not found") 39 | LOG.warning("cache server is not started") 40 | LOG.notice("message has 2 file") 41 | LOG.info("new message is received") 42 | LOG.debug("message has 2 file") 43 | LOG.trace("message has 2 file") 44 | ]] 45 | assert_true(ok, msg) 46 | 47 | assert_match(DATE_PAT .. " %[EMERG%] can not allocate memory", msg) 48 | assert_match(DATE_PAT .. " %[ALERT%] can not allocate memory", msg) 49 | assert_match(DATE_PAT .. " %[FATAL%] can not allocate memory", msg) 50 | assert_match(DATE_PAT .. " %[ERROR%] file not found", msg) 51 | assert_match(DATE_PAT .. " %[WARNING%] cache server is not started", msg) 52 | assert_match(DATE_PAT .. " %[NOTICE%] message has 2 file", msg) 53 | assert_match(DATE_PAT .. " %[INFO%] new message is received", msg) 54 | assert_match(DATE_PAT .. " %[DEBUG%] message has 2 file", msg) 55 | assert_match(DATE_PAT .. " %[TRACE%] message has 2 file", msg) 56 | end 57 | 58 | function test_level() 59 | local ok, status, msg = exec_code[[ 60 | local LOG = require"log".new('notice', 61 | require "log.writer.stdout".new() 62 | ) 63 | 64 | LOG.emerg("can not allocate memory") 65 | LOG.alert("can not allocate memory") 66 | LOG.fatal("can not allocate memory") 67 | LOG.error("file not found") 68 | LOG.warning("cache server is not started") 69 | LOG.notice("message has 2 file") 70 | LOG.info("new message is received") 71 | LOG.debug("message has 2 file") 72 | LOG.trace("message has 2 file") 73 | ]] 74 | assert_true(ok, msg) 75 | 76 | assert_match(DATE_PAT .. " %[EMERG%] can not allocate memory", msg) 77 | assert_match(DATE_PAT .. " %[ALERT%] can not allocate memory", msg) 78 | assert_match(DATE_PAT .. " %[FATAL%] can not allocate memory", msg) 79 | assert_match(DATE_PAT .. " %[ERROR%] file not found", msg) 80 | assert_match(DATE_PAT .. " %[WARNING%] cache server is not started", msg) 81 | assert_match(DATE_PAT .. " %[NOTICE%] message has 2 file", msg) 82 | assert_not_match(DATE_PAT .. " %[INFO%] new message is received", msg) 83 | assert_not_match(DATE_PAT .. " %[DEBUG%] message has 2 file", msg) 84 | assert_not_match(DATE_PAT .. " %[TRACE%] message has 2 file", msg) 85 | 86 | end 87 | 88 | function test_formatter() 89 | local ok, status, msg = exec_code[[ 90 | local writer = require "log.writer.stdout".new() 91 | local LOG = require"log".new(writer, 92 | require "log.formatter.concat".new(':') 93 | ) 94 | local LOG_FMT = require"log".new(writer, 95 | require "log.formatter.format".new() 96 | ) 97 | LOG.info('new', 'message', 'is', 'received') 98 | LOG_FMT.notice("message has %d %s", 2, 'file') 99 | ]] 100 | assert_true(ok, msg) 101 | 102 | assert_match('new:message:is:received', msg) 103 | assert_match('message has 2 file', msg) 104 | end 105 | 106 | function test_async_zmq() 107 | local ok, status, msg = exec_code[[ 108 | local ztimer = require "lzmq.timer" 109 | 110 | local writer = require "log.writer.async.zmq".new('inproc://async.logger', 111 | "return require 'log.writer.stdout'.new()" 112 | ) 113 | ztimer.sleep(500) 114 | 115 | local LOG = require"log".new(writer) 116 | ztimer.sleep(500) 117 | 118 | LOG.fatal("can not allocate memory") 119 | 120 | ztimer.sleep(5000) 121 | 122 | require "lzmq.threads".context():destroy() 123 | 124 | ztimer.sleep(5000) 125 | ]] 126 | assert_true(ok, msg) 127 | 128 | assert_match('can not allocate memory', msg) 129 | end 130 | 131 | function test_async_udp() 132 | local ok, status, msg = exec_code[[ 133 | local writer = require "log.writer.async.udp".new('127.0.0.1', '5555', 134 | "return require 'log.writer.stdout'.new()" 135 | ) 136 | 137 | local LOG = require"log".new(writer) 138 | 139 | LOG.fatal("can not allocate memory") 140 | 141 | require 'lzmq.timer'.sleep(1000) 142 | ]] 143 | assert_true(ok, msg) 144 | 145 | assert_match('can not allocate memory', msg) 146 | end 147 | 148 | function test_async_lane() 149 | if _G.jit then return skip"FIXME: makes LuaLane work with LuaJIT" end 150 | local ok, status, msg = exec_code[[ 151 | local writer = require "log.writer.async.lane".new('lane.logger', 152 | "return require 'log.writer.stdout'.new()" 153 | ) 154 | 155 | local LOG = require"log".new(writer) 156 | 157 | LOG.fatal("can not allocate memory") 158 | 159 | require 'lzmq.timer'.sleep(1000) 160 | ]] 161 | assert_true(ok, msg) 162 | 163 | assert_match('can not allocate memory', msg) 164 | end 165 | 166 | function test_async_proxy() 167 | local ok, status, msg = exec_code[[ 168 | local zthreads = require "lzmq.threads" 169 | local ztimer = require 'lzmq.timer' 170 | 171 | -- create log thread 172 | local LOG = require"log".new( 173 | require "log.writer.async.zmq".new('inproc://async.logger', 174 | "return require 'log.writer.stdout'.new()" 175 | ) 176 | ) 177 | ztimer.sleep(500) 178 | 179 | -- log from separate thread via proxy 180 | local Thread = function() 181 | local LOG = require"log".new( 182 | require "log.writer.async.zmq".new('inproc://async.logger') 183 | ) 184 | 185 | LOG.error("(Thread) file not found") 186 | end 187 | 188 | local child_thread = zthreads.xrun(Thread):start() 189 | ztimer.sleep(500) 190 | 191 | LOG.fatal("can not allocate memory") 192 | 193 | child_thread:join() 194 | 195 | ztimer.sleep(5000) 196 | 197 | zthreads.context():destroy() 198 | 199 | ztimer.sleep(1500) 200 | ]] 201 | assert_true(ok, msg) 202 | 203 | assert_match('can not allocate memory', msg) 204 | assert_match('%(Thread%) file not found', msg) 205 | end 206 | 207 | function test_async_filter_le() 208 | local ok, status, msg = exec_code[[ 209 | local writer = require 'log.writer.stdout'.new() 210 | local Filter = require "log.writer.filter" 211 | 212 | local LOG = require"log".new( 213 | Filter.new('warning', writer) 214 | ) 215 | 216 | LOG.fatal("can not allocate memory") 217 | LOG.warning("cache server is not started") 218 | LOG.info("new message is received") 219 | 220 | require 'lzmq.timer'.sleep(1000) 221 | ]] 222 | assert_true(ok, msg) 223 | 224 | assert_match('can not allocate memory', msg) 225 | assert_match('cache server is not started', msg) 226 | assert_not_match('new message is received', msg) 227 | end 228 | 229 | function test_async_filter_eq() 230 | local ok, status, msg = exec_code[[ 231 | local writer = require 'log.writer.stdout'.new() 232 | local Filter = require "log.writer.filter.lvl.eq" 233 | 234 | local LOG = require"log".new( 235 | Filter.new('warning', writer) 236 | ) 237 | 238 | LOG.fatal("can not allocate memory") 239 | LOG.warning("cache server is not started") 240 | LOG.info("new message is received") 241 | 242 | require 'lzmq.timer'.sleep(1000) 243 | ]] 244 | assert_true(ok, msg) 245 | 246 | assert_not_match('can not allocate memory', msg) 247 | assert_match('cache server is not started', msg) 248 | assert_not_match('new message is received', msg) 249 | end 250 | 251 | function test_formatter_mix() 252 | local ok, status, msg = exec_code[[ 253 | local LOG = require"log".new('trace', 254 | require "log.writer.stdout".new(), 255 | require "log.formatter.mix".new() 256 | ) 257 | 258 | LOG.emerg("can not allocate memory") 259 | LOG.alert(function(str) return str end, "can not allocate memory") 260 | LOG.fatal("can not allocate %s", "memory") 261 | ]] 262 | assert_true(ok, msg) 263 | 264 | assert_match(DATE_PAT .. " %[EMERG%] can not allocate memory", msg) 265 | assert_match(DATE_PAT .. " %[ALERT%] can not allocate memory", msg) 266 | assert_match(DATE_PAT .. " %[FATAL%] can not allocate memory", msg) 267 | end 268 | 269 | end 270 | 271 | local _ENV = TEST_CASE'protected formatter' do 272 | 273 | function test_do_not_raise_error_nil_argument() 274 | local formatter = require "log.formatter.pformat".new() 275 | 276 | local msg 277 | assert_pass(function() msg = formatter("%d", nil) end) 278 | assert_string(msg) 279 | 280 | local expected = tostring(nil) 281 | assert_equal(expected, string.sub(msg,1, #expected)) 282 | 283 | assert_match('Error formatting log message', msg) 284 | end 285 | 286 | function test_do_not_raise_error_unknown_format() 287 | local formatter = require "log.formatter.pformat".new() 288 | 289 | local msg 290 | assert_pass(function() msg = formatter("%t", 10) end) 291 | assert_string(msg) 292 | 293 | local expected = "%t" 294 | assert_equal(expected, string.sub(msg,1, #expected)) 295 | 296 | assert_match('Error formatting log message', msg) 297 | end 298 | 299 | function test_do_not_raise_error_and_no_warning_nil_argument() 300 | local formatter = require "log.formatter.pformat".new(nil, true) 301 | 302 | local msg 303 | assert_pass(function() msg = formatter("%t", nil) end) 304 | assert_string(msg) 305 | 306 | local expected = '%t' 307 | assert_equal(expected, msg) 308 | end 309 | 310 | function test_do_not_raise_error_and_no_warning_unknown_format() 311 | local formatter = require "log.formatter.pformat".new(nil, true) 312 | 313 | local msg 314 | assert_pass(function() msg = formatter("%t", 10) end) 315 | assert_string(msg) 316 | 317 | local expected = '%t' 318 | assert_equal(expected, msg) 319 | end 320 | 321 | function test_do_not_raise_error_nil_argument_2() 322 | local formatter = require "log.formatter.pformat".new(true) 323 | 324 | local msg 325 | assert_pass(function() msg = formatter("%d", nil) end) 326 | assert_string(msg) 327 | 328 | local expected = tostring(nil) 329 | assert_equal(expected, string.sub(msg,1, #expected)) 330 | 331 | assert_match('Error formatting log message', msg) 332 | end 333 | 334 | function test_raise_error_unknown_format() 335 | local formatter = require "log.formatter.pformat".new(true) 336 | 337 | local msg = assert_error(function() msg = formatter("%t", 10) end) 338 | end 339 | 340 | end 341 | 342 | if not HAS_RUNNER then lunit.run() end 343 | -------------------------------------------------------------------------------- /test/test_file_writer.lua: -------------------------------------------------------------------------------- 1 | local HAS_RUNNER = not not lunit 2 | local lunit = require "lunit" 3 | local TEST_CASE = assert(lunit.TEST_CASE) 4 | local skip = lunit.skip or function() end 5 | local path = require "path" 6 | local utils = require "utils" 7 | 8 | local TESTDIR = ".test_log" 9 | 10 | local mkfile, read_file, remove_dir, count_logs = 11 | utils.mkfile, utils.read_file, utils.remove_dir, utils.count_logs 12 | 13 | local _ENV = TEST_CASE "test_log.writer.file" do 14 | 15 | local file_logger = require "log.writer.file.private.impl" 16 | local logger 17 | 18 | local function write_logs(N) 19 | for i = 1, N do 20 | logger:write( 21 | string.format("%5d", i) 22 | ) 23 | end 24 | end 25 | 26 | function setup() 27 | remove_dir(TESTDIR) 28 | path.mkdir(TESTDIR) 29 | assert(path.isdir(TESTDIR)) 30 | end 31 | 32 | function teardown() 33 | if logger then logger:close() end 34 | remove_dir(TESTDIR) 35 | end 36 | 37 | function test_create_dir() 38 | local p = path.join(TESTDIR, "some", "inner", "path") 39 | logger = file_logger:new{ log_dir = p; log_name = "events.log"; } 40 | assert(path.isdir(p)) 41 | end 42 | 43 | function test_rows() 44 | logger = file_logger:new{ 45 | log_dir = TESTDIR; log_name = "events.log"; max_rows = 10; 46 | } 47 | 48 | write_logs(100) 49 | 50 | assert_equal(10, count_logs(TESTDIR)) 51 | end 52 | 53 | function test_reuse_rows() 54 | logger = file_logger:new{ 55 | log_dir = TESTDIR; log_name = "events.log"; max_rows = 100; 56 | } 57 | 58 | write_logs(50) 59 | 60 | assert_equal(1, count_logs(TESTDIR)) 61 | 62 | logger:close() 63 | 64 | logger = file_logger:new{ 65 | log_dir = TESTDIR; log_name = "events.log"; max_rows = 100; 66 | reuse = true; 67 | } 68 | 69 | write_logs(50) 70 | 71 | assert_equal(1, count_logs(TESTDIR)) 72 | end 73 | 74 | function test_reset_rows() 75 | logger = file_logger:new{ 76 | log_dir = TESTDIR; log_name = "events.log"; max_rows = 100; 77 | } 78 | 79 | write_logs(50) 80 | 81 | assert_equal(1, count_logs(TESTDIR)) 82 | 83 | logger:close() 84 | 85 | logger = file_logger:new{ 86 | log_dir = TESTDIR; log_name = "events.log"; max_rows = 100; 87 | } 88 | 89 | write_logs(1) 90 | 91 | assert_equal(2, count_logs(TESTDIR)) 92 | end 93 | 94 | function test_roll_count() 95 | logger = file_logger:new{ 96 | log_dir = TESTDIR; log_name = "events.log"; 97 | max_rows = 10; roll_count = 5; 98 | } 99 | 100 | write_logs(100) 101 | 102 | -- active log + archive logs 103 | assert_equal(6, count_logs(TESTDIR)) 104 | end 105 | 106 | end 107 | 108 | if not HAS_RUNNER then lunit.run() end 109 | -------------------------------------------------------------------------------- /test/test_zmq_finalizer.lua: -------------------------------------------------------------------------------- 1 | local ztimer = require "lzmq.timer" 2 | 3 | local writer = require "log.writer.async.zmq".new('inproc://async.logger', 4 | string.dump(function() 5 | local Log = require "log" 6 | Log.add_cleanup(function() 7 | require "lzmq.timer".sleep(5000) 8 | print "Done!" 9 | os.exit(0) 10 | end) 11 | return require"log.writer.stdout".new() 12 | end) 13 | ) 14 | 15 | local LOG = require"log".new(writer) 16 | 17 | LOG.fatal("can not allocate memory") 18 | 19 | do return -1 end -------------------------------------------------------------------------------- /test/utils.lua: -------------------------------------------------------------------------------- 1 | 2 | local function prequire(...) 3 | local ok, mod = pcall(require, ...) 4 | if not ok then return nil, mod end 5 | return mod, ... 6 | end 7 | 8 | local function vrequire(...) 9 | local errors = {} 10 | for i, n in ipairs{...} do 11 | local mod, err = prequire(n) 12 | if mod then return mod, err end 13 | errors[#errors + 1] = err 14 | end 15 | error(table.concat(errors, "\n\n")) 16 | end 17 | 18 | local path = require "path" 19 | 20 | local function read_file(n) 21 | local f, e = io.open(n, "r") 22 | if not f then return nil, e end 23 | local d, e = f:read("*all") 24 | f:close() 25 | return d, e 26 | end 27 | 28 | local function write_file(n, src) 29 | local f, e = io.open(n, "w+") 30 | if not f then return nil, e end 31 | local d, e = f:write(src) 32 | f:close() 33 | return d, e 34 | end 35 | 36 | local function mkfile(P, data) 37 | P = path.fullpath(P) 38 | path.mkdir(path.dirname(P)) 39 | local f, e = io.open(P, "w+b") 40 | if not f then return nil, err end 41 | if data then assert(f:write(data)) end 42 | f:close() 43 | return P 44 | end 45 | 46 | local function remove_dir(f) 47 | if not path.exists(f) then return end 48 | local mask = path.ensure_dir_end(f) 49 | path.each(mask, function(f) 50 | collectgarbage("collect") collectgarbage("collect") 51 | path.remove(f) 52 | end, {recurse = true, delay = true, reverse = true}) 53 | collectgarbage("collect") collectgarbage("collect") 54 | path.remove(f) 55 | end 56 | 57 | local function count_logs(f) 58 | local counter = 0 59 | path.each(path.join(f,"*.log"), function() 60 | counter = counter + 1 61 | end) 62 | return counter 63 | end 64 | 65 | ----------------------------------------------------------- 66 | local exec do 67 | 68 | local lua_version_t 69 | local function lua_version() 70 | if not lua_version_t then 71 | local version = rawget(_G,"_VERSION") 72 | local maj,min = version:match("^Lua (%d+)%.(%d+)$") 73 | if maj then lua_version_t = {tonumber(maj),tonumber(min)} 74 | elseif not math.mod then lua_version_t = {5,2} 75 | elseif table.pack and not pack then lua_version_t = {5,2} 76 | else lua_version_t = {5,2} end 77 | end 78 | return lua_version_t[1], lua_version_t[2] 79 | end 80 | 81 | local LUA_MAJOR, LUA_MINOR = lua_version() 82 | local LUA_VERSION = LUA_MAJOR * 100 + LUA_MINOR 83 | local LUA_52 = 502 84 | local IS_WINDOWS = package.config:sub(1,1) == '\\' 85 | 86 | local function read_file(n) 87 | local f, e = io.open(n, "r") 88 | if not f then return nil, e end 89 | local d, e = f:read("*all") 90 | f:close() 91 | return d, e 92 | end 93 | 94 | exec = function(cwd, cmd, ...) 95 | local tmpfile = path.tmpname() 96 | 97 | cmd = path.quote(cmd) 98 | if ... then 99 | cmd = cmd .. ' ' .. string.format(...) .. ' ' 100 | if IS_WINDOWS then cmd = path.quote(cmd) end 101 | end 102 | cmd = cmd .. ' >' .. path.quote(tmpfile) .. ' 2>&1' 103 | 104 | local p 105 | if cwd and (cwd ~= "") and (cwd ~= ".") then 106 | p = path.currentdir() 107 | path.chdir(cwd) 108 | end 109 | 110 | local res1,res2,res2 = os.execute(cmd) 111 | if p then path.chdir(p) end 112 | 113 | local data = read_file(tmpfile) 114 | path.remove(tmpfile) 115 | 116 | if LUA_VERSION < LUA_52 then 117 | return res1==0, res1, data 118 | end 119 | 120 | return res1, res2, data 121 | end 122 | 123 | end 124 | ----------------------------------------------------------- 125 | 126 | local function lua_args(arg) 127 | local args = {} 128 | 129 | for i = -1000, -1 do 130 | if arg[i] then args[#args + 1] = arg[i] end 131 | end 132 | 133 | local lua = table.remove(args, 1) 134 | args = table.concat(args, ' ') 135 | return lua, args 136 | end 137 | 138 | return { 139 | exec = exec; 140 | read_file = read_file; 141 | write_file = write_file; 142 | lua_args = lua_args; 143 | remove_dir = remove_dir; 144 | mkfile = mkfile; 145 | count_logs = count_logs; 146 | } 147 | -------------------------------------------------------------------------------- /utils/monitor.lua: -------------------------------------------------------------------------------- 1 | local function write(who, msg) print(who .. ' : ' .. msg) end 2 | ----------------------------------------------------------------------- 3 | 4 | local socket = require "socket" 5 | local Z = require "log.writer.net.zmq._private.compat" 6 | local zmq, zpoller = Z.zmq, Z.poller 7 | local zassert, zrecv_all = Z.assert, Z.recv_all 8 | 9 | local host = arg[1] or '127.0.0.1' 10 | local port = arg[2] or 514 11 | 12 | local zmq_bind_host = 'tcp://' .. host .. ':' .. port 13 | local udp_bind_host = host 14 | local udp_bind_port = port 15 | 16 | local uskt = assert(socket.udp()) 17 | assert(uskt:setsockname(udp_bind_host, udp_bind_port)) 18 | 19 | local ctx = zmq.init(1) 20 | local zskt = ctx:socket(zmq.SUB) 21 | zassert((zskt.set_subscribe or zskt.subscribe)(zskt, '')) 22 | zassert(zskt:bind(zmq_bind_host)) 23 | 24 | local loop = zpoller.new(2) 25 | 26 | loop:add(zskt, zmq.POLLIN, function() 27 | local msg = zrecv_all(zskt) 28 | if msg[2] then write(tostring(msg[1]), tostring(msg[2])) 29 | else write('zmq://unknown', tostring(msg[1])) end 30 | end) 31 | 32 | loop:add(uskt:getfd(), zmq.POLLIN, function() 33 | local msg, ip, port = uskt:receivefrom() 34 | local name = 'udp://' .. ip .. ":" .. port 35 | write(name, msg) 36 | end) 37 | 38 | loop:start() 39 | -------------------------------------------------------------------------------- /utils/proxy_monitor.lua: -------------------------------------------------------------------------------- 1 | local log_packer = require "log.logformat.proxy.pack" 2 | local logformat = require "log.logformat.default".new() 3 | local writer = require "log.writer.console.color".new() 4 | local function write(msg) 5 | local msg, lvl, now = log_packer.unpack(msg) 6 | if msg and lvl and now then writer(logformat, msg, lvl, now) end 7 | end 8 | ----------------------------------------------------------------------- 9 | 10 | local socket = require "socket" 11 | local Z = require "log.writer.net.zmq._private.compat" 12 | local zmq, zpoller = Z.zmq, Z.poller 13 | local zassert, zrecv_all = Z.assert, Z.recv_all 14 | 15 | local host = arg[1] or '127.0.0.1' 16 | local port = arg[2] or 514 17 | 18 | local zmq_bind_host = 'tcp://' .. host .. ':' .. port 19 | local udp_bind_host = host 20 | local udp_bind_port = port 21 | 22 | local uskt = assert(socket.udp()) 23 | assert(uskt:setsockname(udp_bind_host, udp_bind_port)) 24 | 25 | local ctx = zmq.init(1) 26 | local zskt = ctx:socket(zmq.PULL) 27 | zassert(zskt:bind(zmq_bind_host)) 28 | 29 | local loop = zpoller.new(2) 30 | 31 | loop:add(zskt, zmq.POLLIN, function() 32 | local msg = zrecv_all(zskt) 33 | write(msg[1]) 34 | end) 35 | 36 | loop:add(uskt:getfd(), zmq.POLLIN, function() 37 | local msg, ip, port = uskt:receivefrom() 38 | write(msg) 39 | end) 40 | 41 | loop:start() 42 | --------------------------------------------------------------------------------