Squashed 'src/deps/src/lualogging/' content from commit 465c99478

git-subtree-dir: src/deps/src/lualogging
git-subtree-split: 465c994788f1bc18fca950934fa5ec9a909f496c
This commit is contained in:
Théophile Diot 2023-06-30 15:39:07 -04:00
commit 2d86912af8
56 changed files with 5199 additions and 0 deletions

20
.editorconfig Normal file
View File

@ -0,0 +1,20 @@
root = true
[*]
end_of_line = lf
insert_final_newline = true
charset = utf-8
[*.lua]
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
[*.html]
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
[Makefile]
indent_style = tab
trim_trailing_whitespace = true

34
.github/workflows/deploy.yml vendored Normal file
View File

@ -0,0 +1,34 @@
name: Deploy
on: [ push, workflow_dispatch ]
jobs:
affected:
uses: lunarmodules/.github/.github/workflows/list_affected_rockspecs.yml@main
build:
needs: affected
if: ${{ needs.affected.outputs.rockspecs }}
uses: lunarmodules/.github/.github/workflows/test_build_rock.yml@main
with:
rockspecs: ${{ needs.affected.outputs.rockspecs }}
upload:
needs: [ affected, build ]
# Only run upload if:
# 1. We are on the canonical repository (no uploads from forks)
# 2. The current commit is either tagged or on the default branch (the workflow will upload dev/scm rockspecs any
# time they are touched, tagged ones whenever the edited rockspec and tag match)
# 3. Some rockspecs were changed — this implies the commit changing the rockspec is the same one that gets tagged
if: >-
${{
github.repository == 'lunarmodules/lualogging' &&
( github.ref_name == 'master' || startsWith(github.ref, 'refs/tags/') ) &&
needs.affected.outputs.rockspecs
}}
uses: lunarmodules/.github/.github/workflows/upload_to_luarocks.yml@main
with:
rockspecs: ${{ needs.affected.outputs.rockspecs }}
secrets:
apikey: ${{ secrets.LUAROCKS_APIKEY }}

13
.github/workflows/luacheck.yml vendored Normal file
View File

@ -0,0 +1,13 @@
name: Luacheck
on: [ push, pull_request ]
jobs:
luacheck:
runs-on: ubuntu-20.04
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Luacheck
uses: lunarmodules/luacheck@v0

34
.github/workflows/test.yml vendored Normal file
View File

@ -0,0 +1,34 @@
name: Test
on: [ push, pull_request ]
jobs:
test:
strategy:
fail-fast: false
matrix:
luaVersion: [ "5.4", "5.3", "5.2", "5.1", "luajit" ] # , "luajit-openresty" ]
debug: ["", "_G.debug = nil"] # also run with debug lib disabled
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup lua
uses: leafo/gh-actions-lua@v9
with:
luaVersion: ${{ matrix.luaVersion }}
- name: Setup luarocks
uses: leafo/gh-actions-luarocks@v4
- name: Setup dependencies
run: |
luarocks install luasocket
- name: Run regression tests
run: make test
env:
LUA_INIT: ${{ matrix.debug }}

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
tests/test.log*
*.rock

34
.luacheckrc Normal file
View File

@ -0,0 +1,34 @@
--std = "ngx_lua+busted"
unused_args = false
redefined = false
max_line_length = false
globals = {
--"_KONG",
--"kong",
--"ngx.IS_CLI",
}
not_globals = {
"string.len",
"table.getn",
}
ignore = {
--"6.", -- ignore whitespace warnings
}
exclude_files = {
-- The Github Actions Lua Environment
".lua",
".luarocks",
".install",
}
files["src/logging/nginx.lua"] = {
std = "+ngx_lua"
}

20
COPYRIGHT Normal file
View File

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

22
Makefile Normal file
View File

@ -0,0 +1,22 @@
# Default prefix
PREFIX = /usr/local
# System's lua directory (where Lua libraries are installed)
LUA_DIR= $(PREFIX)/share/lua/5.1
LUAS= src/logging/console.lua src/logging/email.lua src/logging/file.lua src/logging/rolling_file.lua src/logging/socket.lua src/logging/sql.lua src/logging/nginx.lua src/logging/rsyslog.lua src/logging/envconfig.lua
ROOT_LUAS= src/logging.lua
build clean:
install:
mkdir -p $(LUA_DIR)/logging
cp $(LUAS) $(LUA_DIR)/logging
cp $(ROOT_LUAS) $(LUA_DIR)
test:
cd tests && ./run_tests.sh
cd tests && LUA_INIT="_G.debug = nil" ./run_tests.sh
lint:
luacheck .

12
Makefile.win Normal file
View File

@ -0,0 +1,12 @@
LUA_DIR= c:\lua5.1\lua
LUAS= src\logging\console.lua src\logging\email.lua src\logging\file.lua src\logging\rolling_file.lua src\logging\socket.lua src\logging\sql.lua src/logging/nginx.lua src/logging/rsyslog.lua src/logging/envconfig.lua
ROOT_LUAS= src\logging.lua
build clean:
install:
IF NOT EXIST $(LUA_DIR)\logging mkdir $(LUA_DIR)\logging
FOR %i IN ($(LUAS)) DO copy %i $(LUA_DIR)\logging
FOR %i IN ($(ROOT_LUAS)) DO copy %i $(LUA_DIR)

63
README.md Normal file
View File

@ -0,0 +1,63 @@
LuaLogging
==========
[![Test](https://img.shields.io/github/actions/workflow/status/lunarmodules/lualogging/test.yml?label=Test&branch=master&logo=linux)](https://github.com/lunarmodules/lualogging/actions?workflow=Test)
[![Luacheck](https://img.shields.io/github/actions/workflow/status/lunarmodules/lualogging/luacheck.yml?label=Luacheck&logo=Lua&branch=master)](https://github.com/lunarmodules/lualogging/actions?workflow=Luacheck)
[![GitHub tag (latest SemVer)](https://img.shields.io/github/v/tag/lunarmodules/lualogging?logo=semver)](https://github.com/lunarmodules/lualogging/releases)
[![Luarocks](https://img.shields.io/luarocks/v/tieske/lualogging?label=Luarocks&logo=Lua)](https://luarocks.org/modules/tieske/lualogging)
https://lunarmodules.github.io/lualogging/
LuaLogging provides a simple API to use logging features in Lua.
Its design was based on log4j. LuaLogging currently supports,
through the use of appenders, console, file, rolling file, email, socket and sql outputs.
LuaLogging is free software and uses the same license as Lua. It is part of the Kepler Project.
Please see docs at https://lunarmodules.github.io/lualogging/ for more details
Installation
============
With LuaRocks:
```sh
luarocks install lualogging
```
Latest Git revision
-------------------
With LuaRocks:
```sh
luarocks install lualogging --dev
```
With make:
```sh
sudo make
```
Copyright
=========
See [LICENSE file](https://github.com/lunarmodules/lualogging/blob/master/COPYRIGHT)
History and changelog
=====================
For the changelog see the [online documentation](https://lunarmodules.github.io/lualogging/index.html#history).
### Releasing a new version
- update changelog in docs (`index.html`, update `history` and `status` sections)
- update version in `logging.lua`
- update copyright years if needed
- update rockspec
- commit as `release X.Y.Z`
- tag as `vX.Y.Z`
- push commit and tag
- upload to luarocks
- test luarocks installation

153
docs/console.html Normal file
View File

@ -0,0 +1,153 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"https://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>LuaLogging: A simple API to use logging features in Lua</title>
<link rel="stylesheet" href="doc.css" type="text/css"/>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
</head>
<body>
<div id="container">
<div id="product">
<div id="product_logo">
<img alt="LuaLogging logo" src="images/lualogging-128.png"/>
</div>
<div id="product_name"><big><strong>LuaLogging</strong></big></div>
<div id="product_description">A simple API to use logging features in Lua.</div>
</div> <!-- id="product" -->
<div id="main">
<div id="navigation">
<h1>LuaLogging</h1>
<ul>
<li><a href="index.html">Home</a></li>
<li><a href="manual.html">Manual</a>
<ul>
<li><a href="manual.html#installation">Installation</a></li>
<li><a href="manual.html#logger">Logger objects</a></li>
<li><a href="manual.html#examples">Examples</a></li>
</ul>
</li>
<li><a href="manual.html#appenders">Appenders</a>
<ul>
<li><strong>Console</strong></li>
<li><a href="file.html">File</a></li>
<li><a href="rolling_file.html">Rolling File</a></li>
<li><a href="sql.html">SQL</a></li>
<li><a href="socket.html">Socket</a></li>
<li><a href="rsyslog.html">Rsyslog</a></li>
<li><a href="email.html">Email</a></li>
<li><a href="nginx.html">Nginx</a></li>
</ul>
</li>
<li><a href="https://github.com/lunarmodules/lualogging">Project</a></li>
<li><a href="license.html">License</a></li>
</ul>
</div> <!-- id="navigation" -->
<div id="content">
<h2>Console appender</h2>
<p>Console is the simplest appender. It just writes the log messages
to <code>io.stdout</code> or <code>io.stderr</code>.</p>
<pre class="example">
function logging.console {
[destination = "stdout"|"stderr",]
[logPattern = <i>string</i>,]
[logPatterns = {
[logging.DEBUG = <i>string</i>,]
[logging.INFO = <i>string</i>,]
[logging.WARN = <i>string</i>,]
[logging.ERROR = <i>string</i>,]
[logging.FATAL = <i>string</i>,]
},]
[timestampPattern = <i>string</i>,]
[logLevel = <i>log-level-constant</i>,]
}
</pre>
<ul>
<li><code>logPatterns</code>:<br />
A table with logPattern strings indexed by the log-levels.
A logPattern specifies how the message is written.<br />
If this parameter is omitted, a patterns table will be created with the
parameter <code>logPattern</code> as the default value for each log-level.
If <code>logPattern</code> also is omitted then each level will fall back to
the current default setting, see <code>logging.defaultLogPatterns</code>.
</li>
<li><code>logPattern</code>:<br />
This value will be used as the default value for each log-level that was
omitted in <code>logPatterns</code>.
</li>
<li><code>timestampPattern</code>:<br />
This is an optional parameter that can be used to specify a date/time formatting
in the log message.
See <code>logging.date</code> for the format.
The default is taken from <code>logging.defaultTimestampPattern()</code>.</li>
<li><code>destination</code>:<br />
The destination stream, optional. The value can be either <code>"stdout"</code>,
or <code>"stderr"</code>. The default is <code>stdout</code>.</li>
<li><code>logLevel</code>:<br />
The initial log-level to set for the created logger.</li>
</ul>
<h2>Examples</h2>
<pre class="example">
local ansicolors = require("ansicolors") -- https://github.com/kikito/ansicolors.lua
local ll = require("logging")
require "logging.console"
-- set up the default logger to stderr + colorization
ll.defaultLogger(ll.console {
logLevel = ll.DEBUG,
destination = "stderr",
timestampPattern = "%y-%m-%d %H:%M:%S",
logPatterns = {
[ll.DEBUG] = ansicolors("%date%{cyan} %level %message %{reset}(%source)\n"),
[ll.INFO] = ansicolors("%date %level %message\n"),
[ll.WARN] = ansicolors("%date%{yellow} %level %message\n"),
[ll.ERROR] = ansicolors("%date%{red bright} %level %message %{reset}(%source)\n"),
[ll.FATAL] = ansicolors("%date%{magenta bright} %level %message %{reset}(%source)\n"),
}
})
local log = ll.defaultLogger()
log:info("logging.console test")
log:debug("debugging...")
log:error("error!")
</pre>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
</div> <!-- id="content" -->
</div> <!-- id="main" -->
<div id="about">
<p><a href="https://validator.w3.org/check?uri=referer">
<img src="https://www.w3.org/Icons/valid-xhtml10" alt="XHTML 1.0 v&aacute;lido!" height="31" width="88" /></a></p>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>
</html>

209
docs/doc.css Normal file
View File

@ -0,0 +1,209 @@
body {
color: #47555c;
font-size: 16px;
font-family: "Open Sans", sans-serif;
margin: 0;
padding: 0;
background: #eff4ff;
}
a:link { color: #008fee; }
a:visited { color: #008fee; }
a:hover { color: #22a7ff; }
h1 { font-size:26px; }
h2 { font-size:24px; }
h3 { font-size:18px; }
h4 { font-size:16px; }
hr {
height: 1px;
background: #c1cce4;
border: 0px;
margin: 20px 0;
}
code {
font-family: "Open Sans Mono", "Andale Mono", monospace;
}
tt {
font-family: "Open Sans Mono", "Andale Mono", monospace;
}
body, td, th {
}
textarea, pre, tt {
font-family: "Open Sans Mono", "Andale Mono", monospace;
}
img {
border-width: 0px;
}
.example {
background-color: #323744;
color: white;
font-size: 16px;
padding: 16px 24px;
border-radius: 2px;
}
div.header, div.footer {
}
#container {
}
#product {
background-color: white;
padding: 10px;
height: 130px;
border-bottom: solid #d3dbec 1px;
}
#product big {
font-size: 42px;
}
#product strong {
font-weight: normal;
}
#product_logo {
float: right;
}
#product_name {
padding-top: 15px;
padding-left: 30px;
font-size: 42px;
font-weight: normal;
}
#product_description {
padding-left: 30px;
color: #757779;
}
#main {
background: #eff4ff;
margin: 0;
}
#navigation {
width: 100%;
background-color: rgb(44,62,103);
padding: 10px;
margin: 0;
}
#navigation h1 {
display: none;
}
#navigation a:hover {
text-decoration: underline;
}
#navigation ul li a {
color: rgb(136, 208, 255);
font-weight: bold;
text-decoration: none;
}
#navigation ul li li a {
color: rgb(136, 208, 255);
font-weight: normal;
text-decoration: none;
}
#navigation ul {
display: inline;
color: white;
padding: 0px;
padding-top: 10px;
padding-bottom: 10px;
}
#navigation li {
display: inline;
list-style-type: none;
padding-left: 5px;
padding-right: 5px;
}
#navigation li {
padding: 10px;
padding: 10px;
}
#navigation li li {
}
#navigation li:hover a {
color: rgb(166, 238, 255);
}
#content {
padding: 20px;
width: 800px;
margin-left: auto;
margin-right: auto;
}
#about {
display: none;
}
dl.reference {
background-color: white;
padding: 20px;
border: solid #d3dbec 1px;
}
dl.reference dt {
padding: 5px;
padding-top: 25px;
color: #637bbc;
}
dl.reference dl dt {
padding-top: 5px;
color: #637383;
}
dl.reference dd {
}
@media print {
body {
font: 10pt "Times New Roman", "TimeNR", Times, serif;
}
a {
font-weight:bold; color: #004080; text-decoration: underline;
}
#main {
background-color: #ffffff; border-left: 0px;
}
#container {
margin-left: 2%; margin-right: 2%; background-color: #ffffff;
}
#content {
margin-left: 0px; padding: 1em; border-left: 0px; border-right: 0px; background-color: #ffffff;
}
#navigation {
display: none;
}
#product_logo {
display: none;
}
#about img {
display: none;
}
.example {
font-family: "Andale Mono", monospace;
font-size: 8pt;
page-break-inside: avoid;
}
}

167
docs/email.html Normal file
View File

@ -0,0 +1,167 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"https://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>LuaLogging: A simple API to use logging features in Lua</title>
<link rel="stylesheet" href="doc.css" type="text/css"/>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
</head>
<body>
<div id="container">
<div id="product">
<div id="product_logo">
<img alt="LuaLogging logo" src="images/lualogging-128.png"/>
</div>
<div id="product_name"><big><strong>LuaLogging</strong></big></div>
<div id="product_description">A simple API to use logging features in Lua.</div>
</div> <!-- id="product" -->
<div id="main">
<div id="navigation">
<h1>LuaLogging</h1>
<ul>
<li><a href="index.html">Home</a></li>
<li><a href="manual.html">Manual</a>
<ul>
<li><a href="manual.html#installation">Installation</a></li>
<li><a href="manual.html#logger">Logger objects</a></li>
<li><a href="manual.html#examples">Examples</a></li>
</ul>
</li>
<li><a href="manual.html#appenders">Appenders</a>
<ul>
<li><a href="console.html">Console</a></li>
<li><a href="file.html">File</a></li>
<li><a href="rolling_file.html">Rolling File</a></li>
<li><a href="sql.html">SQL</a></li>
<li><a href="socket.html">Socket</a></li>
<li><a href="rsyslog.html">Rsyslog</a></li>
<li><strong>Email</strong></li>
<li><a href="nginx.html">Nginx</a></li>
</ul>
</li>
<li><a href="https://github.com/lunarmodules/lualogging">Project</a></li>
<li><a href="license.html">License</a></li>
</ul>
</div> <!-- id="navigation" -->
<div id="content">
<h2>Email appender</h2>
<p>This appender can be used to send log requests through email. One email
message is sent for each log request.</p>
<pre class="example">
function logging.email {
from = <i>string</i>,
rcpt = <i>string</i> or <i>string-table</i>,
[user = <i>string</i>,]
[password = <i>string</i>,]
[server = <i>string</i>,]
[port = <i>number</i>,]
[domain = <i>string</i>,]
[headers = <i>table</i>,]
[logPattern = <i>string</i>,]
[logPatterns = {
[logging.DEBUG = <i>string</i>,]
[logging.INFO = <i>string</i>,]
[logging.WARN = <i>string</i>,]
[logging.ERROR = <i>string</i>,]
[logging.FATAL = <i>string</i>,]
},]
[timestampPattern = <i>string</i>,]
[logLevel = <i>log-level-constant</i>,]
}
</pre>
<ul>
<li><code>from</code>:<br />
The sender of the email message.</li>
<li><code>rcpt</code>:<br />
The recipient of the email message. A string or a numerically indexed Lua table with strings.</li>
<li><code>user</code>:<br />
User for authentication.</li>
<li><code>password</code>:<br />
Password for authentication.</li>
<li><code>server</code>:<br />
Server to connect to. Default is <code>"localhost"</code>.</li>
<li><code>port</code>:<br />
Port to connect to. Default is <code>25</code>.</li>
<li><code>domain</code>:<br />
Domain name used to greet the server. Defaults to the local
machine host name.</li>
<li><code>headers.to</code>:<br />
The recipient of the message, as an extended description.</li>
<li><code>headers.from</code>:<br />
The sender of the message, as an extended description.</li>
<li><code>headers.subject</code>:<br />
The subject of the message sent. This can contain patterns like
the <code>logPattern</code> parameter.</li>
<li><code>logPatterns</code>:<br />
A table with logPattern strings indexed by the log-levels.
A logPattern specifies how the message is written.<br />
If this parameter is omitted, a patterns table will be created with the
parameter <code>logPattern</code> as the default value for each log-level.
If <code>logPattern</code> also is omitted then each level will fall back to
the current default setting, see <code>logging.defaultLogPatterns</code>.
</li>
<li><code>logPattern</code>:<br />
This value will be used as the default value for each log-level that was
omitted in <code>logPatterns</code>.
</li>
<li><code>timestampPattern</code>:<br />
This is an optional parameter that can be used to specify a date/time formatting
in the log message. The default is taken from <code>logging.defaultTimestampPattern()</code>.</li>
<li><code>logLevel</code>:<br />
The initial log-level to set for the created logger.</li>
</ul>
<h2>Example</h2>
<pre class="example">
require"logging.email"
local logger = logging.email {
rcpt = "mail@host.com",
from = "mail@host.com",
headers = {
subject = "[%level] logging.email test",
},
}
logger:info("logging.email test")
logger:debug("debugging...")
logger:error("error!")
</pre>
</div> <!-- id="content" -->
</div> <!-- id="main" -->
<div id="about">
<p><a href="https://validator.w3.org/check?uri=referer">
<img src="https://www.w3.org/Icons/valid-xhtml10" alt="XHTML 1.0 v&aacute;lido!" height="31" width="88" /></a></p>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>
</html>

148
docs/file.html Normal file
View File

@ -0,0 +1,148 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"https://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>LuaLogging: A simple API to use logging features in Lua</title>
<link rel="stylesheet" href="doc.css" type="text/css"/>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
</head>
<body>
<div id="container">
<div id="product">
<div id="product_logo">
<img alt="LuaLogging logo" src="images/lualogging-128.png"/>
</div>
<div id="product_name"><big><strong>LuaLogging</strong></big></div>
<div id="product_description">A simple API to use logging features in Lua.</div>
</div> <!-- id="product" -->
<div id="main">
<div id="navigation">
<h1>LuaLogging</h1>
<ul>
<li><a href="index.html">Home</a></li>
<li><a href="manual.html">Manual</a>
<ul>
<li><a href="manual.html#installation">Installation</a></li>
<li><a href="manual.html#logger">Logger objects</a></li>
<li><a href="manual.html#examples">Examples</a></li>
</ul>
</li>
<li><a href="manual.html#appenders">Appenders</a>
<ul>
<li><a href="console.html">Console</a></li>
<li><strong>File</strong></li>
<li><a href="rolling_file.html">Rolling File</a></li>
<li><a href="sql.html">SQL</a></li>
<li><a href="socket.html">Socket</a></li>
<li><a href="rsyslog.html">Rsyslog</a></li>
<li><a href="email.html">Email</a></li>
<li><a href="nginx.html">Nginx</a></li>
</ul>
</li>
<li><a href="https://github.com/lunarmodules/lualogging">Project</a></li>
<li><a href="license.html">License</a></li>
</ul>
</div> <!-- id="navigation" -->
<div id="content">
<h2>File appender</h2>
<p>The file appender can be used to write log messages to a file. It
uses Lua I/O routines to do its job.</p>
<pre class="example">
function logging.file {
[filename = <i>string</i>,]
[datePattern = <i>string</i>,]
[logPattern = <i>string</i>,]
[logPatterns = {
[logging.DEBUG = <i>string</i>,]
[logging.INFO = <i>string</i>,]
[logging.WARN = <i>string</i>,]
[logging.ERROR = <i>string</i>,]
[logging.FATAL = <i>string</i>,]
},]
[timestampPattern = <i>string</i>,]
[logLevel = <i>log-level-constant</i>,]
}
</pre>
<ul>
<li><code>filename</code>:<br />
The name of the file to be written to. On each call to log a
message the file is opened for appending and closed immediately.<br />
If the file cannot be opened for appending the logging request
returns nil and an error message.<br />
The default value is <code>"lualogging.log"</code>.</li>
<li><code>datePattern</code>:<br />
This is an optional parameter that can be used to specify a date
pattern that will be passed to the
<a href="https://www.lua.org/manual/5.1/manual.html#pdf-os.date"><code>os.date</code></a>
function to compose the filename.<br />
This is useful to create daily or monthly log files. If the user
wants to create one log file per day he specifies a
<code>"%Y-%m-%d"</code> pattern and a filename like
<code>"temp%s.log"</code>.</li>
<li><code>logPatterns</code>:<br />
A table with logPattern strings indexed by the log-levels.
A logPattern specifies how the message is written.<br />
If this parameter is omitted, a patterns table will be created with the
parameter <code>logPattern</code> as the default value for each log-level.
If <code>logPattern</code> also is omitted then each level will fall back to
the current default setting, see <code>logging.defaultLogPatterns</code>.
</li>
<li><code>logPattern</code>:<br />
This value will be used as the default value for each log-level that was
omitted in <code>logPatterns</code>.
</li>
<li><code>timestampPattern</code>:<br />
This is an optional parameter that can be used to specify a date/time formatting
in the log message.
See <code>logging.date</code> for the format.
The default is taken from <code>logging.defaultTimestampPattern()</code>.</li>
<li><code>logLevel</code>:<br />
The initial log-level to set for the created logger.</li>
</ul>
<h2>Example</h2>
<pre class="example">
require"logging.file"
local logger = logging.file {
filename = "test%s.log",
datePattern = "%Y-%m-%d",
}
logger:info("logging.file test")
logger:debug("debugging...")
logger:error("error!")
</pre>
<p>&nbsp;</p>
<p>&nbsp;</p>
</div> <!-- id="content" -->
</div> <!-- id="main" -->
<div id="about">
<p><a href="https://validator.w3.org/check?uri=referer">
<img src="https://www.w3.org/Icons/valid-xhtml10" alt="XHTML 1.0 v&aacute;lido!" height="31" width="88" /></a></p>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

232
docs/index.html Normal file
View File

@ -0,0 +1,232 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"https://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>LuaLogging: A simple API to use logging features in Lua</title>
<link rel="stylesheet" href="doc.css" type="text/css"/>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
</head>
<body>
<div id="container">
<div id="product">
<div id="product_logo">
<img alt="LuaLogging logo" src="images/lualogging-128.png"/>
</div>
<div id="product_name"><big><strong>LuaLogging</strong></big></div>
<div id="product_description">A simple API to use logging features in Lua.</div>
</div> <!-- id="product" -->
<div id="main">
<div id="navigation">
<h1>LuaLogging</h1>
<ul>
<li><strong>Home</strong></li>
<li><a href="manual.html">Manual</a>
<ul>
<li><a href="manual.html#installation">Installation</a></li>
<li><a href="manual.html#logger">Logger objects</a></li>
<li><a href="manual.html#examples">Examples</a></li>
</ul>
</li>
<li><a href="manual.html#appenders">Appenders</a>
<ul>
<li><a href="console.html">Console</a></li>
<li><a href="file.html">File</a></li>
<li><a href="rolling_file.html">Rolling File</a></li>
<li><a href="sql.html">SQL</a></li>
<li><a href="socket.html">Socket</a></li>
<li><a href="rsyslog.html">Rsyslog</a></li>
<li><a href="email.html">Email</a></li>
<li><a href="nginx.html">Nginx</a></li>
</ul>
</li>
<li><a href="https://github.com/lunarmodules/lualogging">Project</a></li>
<li><a href="license.html">License</a></li>
</ul>
</div> <!-- id="navigation" -->
<div id="content">
<h2><a name="overview"></a>Overview</h2>
<p>LuaLogging provides a simple API to use logging features in Lua.
Its design was based on
<a href="https://logging.apache.org/log4j/docs/index.html">log4j</a>.
LuaLogging currently supports, through the use of <em><a href="manual.html#appenders">appenders</a></em>,
console, file, email, socket and sql outputs.</p>
<p>
LuaLogging is free software and uses the same <a href="license.html">license</a>
as Lua 5.1.
</p>
<h2><a name="status"></a>Status</h2>
<p>
Current version is 1.8.2. It was developed for Lua 5.1+.
</p>
<h2><a name="download"></a>Download</h2>
<p>
LuaLogging can be downloaded from its
<a href="https://github.com/lunarmodules/lualogging/archives/master">GitHub</a>
downloads page or installed via LuaRocks.
</p>
<h2><a name="dependencies"></a>Dependencies</h2>
<p>LuaLogging dependencies can be separated by the used appenders:</p>
<dl>
<dt><strong>LuaLogging Core, Console and File appenders</strong></dt>
<dd>
<ul>
<li><a href="https://www.lua.org">Lua 5.1+</a></li>
</ul>
</dd>
<dt><strong>Socket, Rsyslog, and Email appenders</strong></dt>
<dd>
<ul>
<li><a href="http://w3.impa.br/~diego/software/luasocket/">LuaSocket</a></li>
</ul>
</dd>
<dt><strong>SQL appender</strong></dt>
<dd>
<ul>
<li>LuaSQL 2.1.x</li>
</ul>
</dd>
</dl>
<h2><a name="history"></a>History</h2>
<dl class="history">
<dt><strong>1.8.2</strong> [27/Jan/2023]</dt>
<dd><strong>Fix</strong>: allow to work without the debug library.</dd>
<dt><strong>1.8.1</strong> [23/Jan/2023]</dt>
<dd><strong>Fix</strong>: use explicit tostring, don't rely on automatic coercion.</dd>
<dd><strong>Fix</strong>: rsyslog appender wouldn't properly clear the socket upon an error.</dd>
<dt><strong>1.8.0</strong> [22/Oct/2022]</dt>
<dd><strong>Added</strong>: new module <code>logging.envconfig</code> to allow log configuration via
environment variables.</dd>
<dd><strong>Refactor</strong>: refactored the appenders to be more object like. They now return a module table.</dd>
<dd><strong>Fix</strong>: add rsyslog appender to makefile install target.</dd>
<dd><strong>Fix</strong>: errorhandler in rsyslog was omitting 'self', and also log the failed message itself to stderr.</dd>
<dd><strong>Fix</strong>: when using Copas and rsyslog on Lua 5.1, coxpcall is required to yield.</dd>
<dd><strong>Fix</strong>: rsyslog; upon failure remove the closed socket from the cache to prevent the retry from using
the closed socket.</dd>
<dt><strong>1.7.0</strong> [21/Sep/2022]</dt>
<dd><strong>Added</strong>: new function <code>logging.date</code> is compatible with <code>os.date</code>
but supports second-fractions, to be represented by <code>%q</code> placeholder in the
<code>timestampPattern</code>.</dd>
<dd><strong>Added</strong>: new bundled appender; "rsyslog". This logger will log in syslog format to a remote
syslog server. It supports both BSD as well as IETF formats, and supports sending over UDP and TCP, with an option
to use Copas for async-sending over TCP.</dd>
<dt><strong>1.6.0</strong> [09/Nov/2021]</dt>
<dd><strong>Added</strong>: the <code>logPattern</code> can now be specified for each log-level individually.
This allows use cases like only adding <code>"%source"</code> on debug level, or injecting ansi-color coding
in for example the error log-level.</dd>
<dd><strong>Added</strong>: new bundled appender; "nginx". This logger has no configuration options, but will
simply pass any log messages on to the nginx log. When using LuaLogging in OpenResty, configure this logger.</dd>
<dd><strong>Added</strong>: application level defaults. New functions <code>logging.defaultLogPatterns()</code>
, <code>logging.defaultTimestampPattern()</code>, <code>logging.defaultLevel()</code>, and <code>logging.defaultLogger()</code>
can be used to get/set the global application level defaults.<br/>
Use carefully, this is global state, so only applications should set those, libraries should not.</dd>
<dd><strong>Added</strong>: the included appenders now have an extra parameter to set
log-level upon creation; <code>logLevel</code>.</dd>
<dd><strong>Added</strong>: the <code>logPattern</code> can now have new placeholders:<br/>
<code>"%file"</code> (source file), <br/>
<code>"%line"</code> (source line), <br/>
<code>"%function"</code> (function name), and <br/>
<code>"%source"</code> (evaluates to <code>"%file:%line in function '%function'"</code>).</dd>
<dd><strong>Added</strong>: the console logger has a new optional parameter "destination",
which must be either "stderr" or "stdout" (defaults to "stdout")</dd>
<dt><strong>1.5.2</strong> [12/Aug/2021]</dt>
<dd><strong>Fix</strong>: rolling-file, 'seek' can occasionally fail, handle it properly.</dd>
<dt><strong>1.5.1</strong> [22/Mar/2021]</dt>
<dd><strong>Reverted</strong>: Functionality to specify custom log levels.</dd>
<dt><strong>1.5.0</strong> [22/Mar/2021]</dt>
<dd><strong>Added</strong>: Functionality to specify custom log levels.</dd>
<dd><strong>Added</strong>: Provide verbose error message when formatting fails.</dd>
<dd><strong>Fix</strong>: disable buffering on windows files</dd>
<dt><strong>1.4.0</strong> [12/Aug/2020]</dt>
<dd><strong>Fix</strong>: No more global on Lua 5.3.</dd>
<dd><strong>Fix</strong>: Reduced log noise when changing log levels.</dd>
<dd><strong>Added</strong>: A new log-level "off" to suspend logging.</dd>
<dd><strong>Fix</strong>: Restored the log-level constants on the logger object.</dd>
<dd><strong>Fix</strong>: catch exceptions when formatting log messages.</dd>
<dd><strong>Change</strong>: creating loggers now always takes a parameters table (consistent
across all appenders). The old parameters are now deprecated, but still
work for backward compatibility purposes.</dd>
<dd><strong>Added</strong>: All appenders have a new property <code>timestampPattern</code>
to specify the format of the timestamp in the log message</dd>
<dd><strong>Added</strong>: <code>logger:getPrint()</code> function that returns a print-like
function, but directs all output to the logger.</dd>
<dt><strong>1.3.0</strong> [5/Mar/2013]</dt>
<dt><strong>1.2.0</strong> [20/Apr/2011]</dt>
<dd>Improved performance of logging.</dd>
<dd>Added Rolling File Appender.</dd>
<dt><strong>1.1.4</strong> [30/Oct/2007]</dt>
<dd>Fixed bug #1719 -
inefficient handling of file loggers (Patch by J&uuml;rgen H&ouml;tzel).</dd>
<dt><strong>1.1.3</strong> [08/Aug/2007]</dt>
<dd>New makefile for Windows (using nmake) and configure script for Unix.</dd>
<dt><strong>1.1.2</strong> [14/Aug/2006]</dt>
<dd>Fixed a bug found by Carlos Augusto where tostring() was being
incorrectly used with numbers.</dd>
<dt><strong>1.1.1</strong> [31/Mar/2006]</dt>
<dd></dd>
<dt><strong>1.1.0</strong> [12/Nov/2004]</dt>
<dd></dd>
<dt><strong>1.0.0</strong> [02/Jul/2004]</dt>
<dd></dd>
</dl>
<h2><a name="credits"></a>Credits</h2>
<p>
LuaLogging 1.2 and 1.3 were maintained by Robert G. Jakabosky.
</p>
<p>
LuaLogging 1.1.x was designed by Danilo Tuler and Thiago Ponte
and implemented by Thiago Ponte.
</p>
<p>LuaLogging 1.0.0 was designed by Danilo Tuler (and log4j)
and implemented by Danilo Tuler and Andr&eacute; Carregal.</p>
</div> <!-- id="content" -->
</div> <!-- id="main" -->
<div id="about">
<p><a href="https://validator.w3.org/check?uri=referer">
<img src="https://www.w3.org/Icons/valid-xhtml10" alt="XHTML 1.0 v&aacute;lido!" height="31" width="88" /></a></p>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>
</html>

118
docs/license.html Normal file
View File

@ -0,0 +1,118 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"https://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>LuaLogging: A simple API to use logging features in Lua</title>
<link rel="stylesheet" href="doc.css" type="text/css"/>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
</head>
<body>
<div id="container">
<div id="product">
<div id="product_logo">
<img alt="LuaLogging logo" src="images/lualogging-128.png"/>
</div>
<div id="product_name"><big><strong>LuaLogging</strong></big></div>
<div id="product_description">A simple API to use logging features in Lua.</div>
</div> <!-- id="product" -->
<div id="main">
<div id="navigation">
<h1>LuaLogging</h1>
<ul>
<li><a href="index.html">Home</a></li>
<li><a href="manual.html">Manual</a>
<ul>
<li><a href="manual.html#installation">Installation</a></li>
<li><a href="manual.html#logger">Logger objects</a></li>
<li><a href="manual.html#examples">Examples</a></li>
</ul>
</li>
<li><a href="manual.html#appenders">Appenders</a>
<ul>
<li><a href="console.html">Console</a></li>
<li><a href="file.html">File</a></li>
<li><a href="rolling_file.html">Rolling File</a></li>
<li><a href="sql.html">SQL</a></li>
<li><a href="socket.html">Socket</a></li>
<li><a href="rsyslog.html">Rsyslog</a></li>
<li><a href="email.html">Email</a></li>
<li><a href="nginx.html">Nginx</a></li>
</ul>
</li>
<li><a href="https://github.com/lunarmodules/lualogging">Project</a></li>
<li><a href="license.html">License</a></li>
</ul>
</div> <!-- id="navigation" -->
<div id="content">
<h2>License</h2>
<p>
LuaLogging is free software: it can be used for both academic and
commercial purposes at absolutely no cost. There are no royalties
or GNU-like "copyleft" restrictions. LuaLogging qualifies as <a
href="https://www.opensource.org/docs/definition.html">Open
Source</a> software. Its licenses are compatible with <a href=
"https://www.gnu.org/licenses/gpl.html">GPL</a>. LuaLogging is not
in the public domain and the Kepler Project keep its copyright. The
legal details are below.
</p>
<p>The spirit of the license is that you are free to use LuaLogging
for any purpose at no cost without having to ask us. The only
requirement is that if you do use LuaLogging, then you should give
us credit by including the appropriate copyright notice somewhere
in your product or its documentation.</p>
<p>The LuaLogging library is designed by Danilo Tuler and
implemented by Danilo Tuler, Thiago Ponte and Andr&eacute;
Carregal. The implementation is not derived from licensed
software.</p>
<hr/>
<p>
Copyright &copy; 2004-2010 Kepler Project, 2011-2013 Neopallium, 2020-2023 Thijs Schreijer
</p>
<p>Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:</p>
<p>The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.</p>
<p>THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
</div> <!-- id="content" -->
</div> <!-- id="main" -->
<div id="about">
<p><a href="https://validator.w3.org/check?uri=referer">
<img src="https://www.w3.org/Icons/valid-xhtml10" alt="XHTML 1.0 v&aacute;lido!" height="31" width="88" /></a></p>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>
</html>

472
docs/manual.html Normal file
View File

@ -0,0 +1,472 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"https://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>LuaLogging: A simple API to use logging features in Lua</title>
<link rel="stylesheet" href="doc.css" type="text/css"/>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
</head>
<body>
<div id="container">
<div id="product">
<div id="product_logo">
<img alt="LuaLogging logo" src="images/lualogging-128.png"/>
</div>
<div id="product_name"><big><strong>LuaLogging</strong></big></div>
<div id="product_description">A simple API to use logging features in Lua.</div>
</div> <!-- id="product" -->
<div id="main">
<div id="navigation">
<h1>LuaLogging</h1>
<ul>
<li><a href="index.html">Home</a></li>
<li><strong>Manual</strong>
<ul>
<li><a href="manual.html#installation">Installation</a></li>
<li><a href="manual.html#logger">Logger objects</a></li>
<li><a href="manual.html#examples">Examples</a></li>
</ul>
</li>
<li><a href="manual.html#appenders">Appenders</a>
<ul>
<li><a href="console.html">Console</a></li>
<li><a href="file.html">File</a></li>
<li><a href="rolling_file.html">Rolling File</a></li>
<li><a href="sql.html">SQL</a></li>
<li><a href="socket.html">Socket</a></li>
<li><a href="rsyslog.html">Rsyslog</a></li>
<li><a href="email.html">Email</a></li>
<li><a href="nginx.html">Nginx</a></li>
</ul>
</li>
<li><a href="https://github.com/lunarmodules/lualogging">Project</a></li>
<li><a href="license.html">License</a></li>
</ul>
</div> <!-- id="navigation" -->
<div id="content">
<h2><a name="introduction"></a>Introduction</h2>
<p>LuaLogging provides a simple API to use logging features in Lua.
Its design was based on
<a href="https://logging.apache.org/log4j/docs/index.html">log4j</a>.
LuaLogging currently supports console, file, email, socket and sql
outputs through the use of <em><a href="#appenders">appenders</a></em>.</p>
<p>The <code>logging</code> module holds a <code>new</code> function to create
new <a href="#logger"><code>logger</code></a> objects.</p>
<p>This logger constructor receives a function
(known as the <em>appender</em> function)
that will be called on each call to log a message.</p>
<p>An <em>appender</em> function receives three arguments:</p>
<ul>
<li><strong>self</strong>: the logger object</li>
<li><strong>level</strong>: the logging level</li>
<li><strong>message</strong>: the message to be logged</li>
</ul>
<p>The logger constructor also receives a optional second argument which should
be a table with parameters</p>
<h2><a name="installation"></a>Installation</h2>
<p>LuaLogging is installed as a regular Lua module called <code>logging</code>.</p>
<p>installation is easiest using LuaRocks; "<code>luarocks install lualogging</code>",
or alternatively using the <code>Makefile</code>.</p>
<h2><a name="logging"></a>Logging module</h2>
<p>The logging module has a number of global functions:</p>
<dl class="reference">
<dt><strong>logging.new( function[, logLevel] )</strong></dt>
<dd>Creates a new logger object from a custom 'appender' function. See examples below.
The appender function signature is <code>function(self, level, message)</code>. The
optional logLevel argument specifies the initial log-level to set (the value must be
a valid log-level constant). If omitted defaults to <code>logging.defaultLevel</code>.
</dd>
<dt><strong>patts = logging.buildLogPatterns([table], [string])</strong></dt>
<dd>Creates a log-patterns table. The returned table will for each level have
the logPattern set to 1. the value in the table, or alternatively 2. the
string value, or 3. the pattern from the global defaults.<br/>
<br/>
Returns a logPatterns table.<br/>
<br/>
Example logging source info only on debug-level, and coloring error and fatal
messages:
<pre class="example">
local patterns = logging.buildLogPatterns(
{
[logging.DEBUG] = "%date %level %message (%source)\n"
[logging.ERROR] = "%date "..ansi_red.."%level %message"..ansi_reset.."\n"
[logging.FATAL] = "%date "..ansi_red.."%level %message"..ansi_reset.."\n"
}, "%date %level %message\n"
)
</pre>
</dd>
<dt><strong>date_val = logging.date([format,[time]])</strong></dt>
<dd>Compatible with standard Lua <code>os.date()</code> function, but supports second fractions.
The placeholder in the format string is <code>"%q"</code>, or <code>"%1q"</code> to <code>"%6q"</code>,
where the number 1-6 specifies the number of decimals. The default is 3, so <code>"%q"</code> is the same
as <code>"%3q"</code>. The format will always have the specified length, padded with leading and trailing 0's
where necessary.<br/>
If the pattern is <code>"*t"</code>, then the returned table will have an extra field <code>"secf"</code> that holds
the fractional part.<br/>
<br/>
Example: <code>"%y-%m-%d %H:%M:%S.%6q"</code><br/>
<br/>
Note: if the "time" parameter is not provided, it will try and use the LuaSocket function <code>gettime()</code>
to get the time. If unavailable, the fractional part will be set to 0.
</dd>
<dt><strong>patts = logging.defaultLogPatterns([string | table])</strong></dt>
<dd>Sets the default logPatterns (global!) if a parameter is given.
If the parameter is a string then that string will be used as the pattern for
all log-levels. If a table is given, then it must have all log-levels defined
with a pattern string. See also <code>logging.buildLogPatterns</code>.<br/>
<br/>
The default is <code>"%date %level %message\n"</code> for all log-levels.<br/>
<br/>
Available placeholders in the pattern string are; <code>"%date"</code>, <code>"%level"</code>, <code>"%message"</code>,
<code>"%file"</code>, <code>"%line"</code>, <code>"%function"</code> and <code>"%source"</code>. The <code>"%source"</code> placeholder evaluates
to <code>"%file:%line in function'%function'"</code>.<br/>
<br/>
Returns the current defaultLogPatterns value.<br/>
<br/>
<em>NOTE:</em> since this is a global setting, libraries should never set it,
only applications should.
</dd>
<dt><strong>patt = logging.defaultTimestampPattern([string])</strong></dt>
<dd>Sets the default timestampPattern (global!) if given.
The default is <code>nil</code>, which results in a system specific date/time format.
The pattern should be accepted by the function <code>logging.date</code> for formatting.<br/>
<br/>
Returns the current defaultTimestampPattern value.<br/>
<br/>
<em>NOTE:</em> since this is a global setting, libraries should never set it,
only applications should.
</dd>
<dt><strong>level = logging.defaultLevel([level constant])</strong></dt>
<dd>Sets the default log-level (global!) if given. Each new logger object created
will start with the log-level as specified by this value.
The level parameter must be one of the log-level constants. The default is <code>logging.DEBUG</code>.<br/>
<br/>
Returns the current defaultLevel value.<br/>
<br/>
<em>NOTE:</em> since this is a global setting, libraries should never set it,
only applications should.
</dd>
<dt><strong>logger = logging.defaultLogger([logger object])</strong></dt>
<dd>Sets the default logger object (global!) if given.
The logger parameter must be a LuaLogging logger object. The default is to
generate a new <code>console</code> logger (with "destination" set to "stderr")
on the first call to get the default logger.<br/>
<br/>
Returns the current defaultLogger value.<br/>
<br/>
<em>NOTE:</em> since this is a global setting, libraries should never set it,
only applications should. Libraries should get this logger and use it, assuming
the application has set it.
<pre class="example">
-- Example: application setting the default logger
local color = require("ansicolors") -- https://github.com/kikito/ansicolors.lua
local ll = require("logging")
require "logging.console"
ll.defaultLogger(ll.console {
destination = "stderr",
timestampPattern = "!%y-%m-%dT%H:%M:%S.%qZ", -- ISO 8601 in UTC
logPatterns = {
[ll.DEBUG] = color("%{white}%date%{cyan} %level %message (%source)\n"),
[ll.INFO] = color("%{white}%date%{white} %level %message\n"),
[ll.WARN] = color("%{white}%date%{yellow} %level %message\n"),
[ll.ERROR] = color("%{white}%date%{red bright} %level %message %{cyan}(%source)\n"),
[ll.FATAL] = color("%{white}%date%{magenta bright} %level %message %{cyan}(%source)\n"),
}
})
-- Example: library using default if available (fallback to nop)
local log do
local ll = package.loaded.logging
if ll and type(ll) == "table" and ll.defaultLogger and
tostring(ll._VERSION):find("LuaLogging") then
-- default LuaLogging logger is available
log = ll.defaultLogger()
else
-- just use a stub logger with only no-op functions
local nop = function() end
log = setmetatable({}, {
__index = function(self, key) self[key] = nop return nop end
})
end
end
log:debug("starting my library")
</pre>
</dd>
</dl>
<h2><a name="environment"></a>Environment variables</h2>
<p>The submodule <code>logging.envconfig</code> allows to configure the default
logger via environment variables. This is done based on a "prefix" for the environment
variables (with default value "LL").
The variables are the logger options names with the prefix in all-caps separated by "_".
So if a logger takes an option "<code>opts.logLevel</code>", then it can be configured
using environment variable <code>LL_LOGLEVEL=warn</code>.</p>
<p>The type of logger can be set using environment variable <code>LL_LOGGER=console</code>
(assuming the default prefix "LL").</p>
<p>The defaults are "LL" for the prefix, and a console logger to "stderr".</p>
<p>The exposed functions:</p>
<dl class="reference">
<dt><strong>envconfig.set_default_settings(prefix)</strong></dt>
<dd>Sets the default prefix and loads the configuration. Returns <code>true</code> on
success, <code>nil, "already set a default"</code> if it was called before. Will
throw an error if something during the configuration fails (bad user input in environment
variables for example).<br/>
This method should be called by applications at startup, to set the default prefix.
</dd>
<dt><strong>name, opts = envconfig.get_default_settings()</strong></dt>
<dd>returns the appender name (eg. "file", "console", etc), and the options table for
configuring the appender. The table has a metatable that will dynamically lookup
fields in environment variables, this ensures that any option an appender checks
will be read and returned.
Boolean and number values will be converted to their respective types (case insensitive).<br/>
<br/>
The common <code>logPatterns</code> field is a special case where each level can be
configured by appending the level with an "_". See example below:
<pre class="example">
# set these environment variables
export MYAPP_LOGGER="console"
export MYAPP_LOGLEVEL="info"
export MYAPP_LOGPATTERN = "%message"
export MYAPP_LOGPATTERNS_DEBUG = "%message %source"
export MYAPP_LOGPATTERNS_FATAL = "Oh my!! %message"
-- Lua code (see set_default_logger for a shorter version)
local logging = require "logging"
local logenv = require "logging.envconfig"
assert(logenv.set_default_settings("MYAPP"))
local logger_name, logger_opts = logenv.get_default_settings()
local logger = assert(require("logging."..logger_name)(logger_opts))
logging.setdefaultLogger(logger)
logger:info("configured via environment!")
</pre>
</dd>
<dt><strong>logger = envconfig.set_default_logger(prefix)</strong></dt>
<dd>Sets (and returns) the default logger from the environment.
<pre class="example">
# set these environment variables
export MYAPP_LOGGER="console"
export MYAPP_LOGLEVEL="info"
export MYAPP_LOGPATTERN = "%message"
export MYAPP_LOGPATTERNS_DEBUG = "%message %source"
export MYAPP_LOGPATTERNS_FATAL = "Oh my!! %message"
-- Lua code
local logenv = require "logging.envconfig"
local logger = assert(logenv.set_default_logger("MYAPP"))
logger:info("configured via environment!")
</pre>
</dd>
</dl>
<h2><a name="logger"></a>Logger objects</h2>
<p>Logger objects are created by loading the 'appender' module, and calling on it. For example:</p>
<pre class="example">
local logger = require("logging.console") {
-- options go here (see appenders for options)
}
</pre>
<p>A logger object offers the following methods that write log messages.</p>
<p>For each of the methods below, the parameter <code>message</code> may be any lua value,
not only strings. When necessary <code>message</code> is converted to a string.</p>
<p>The parameter <code>level</code> can be one of the variables enumerated below.
The values are presented in descending criticality, so if the minimum level is
defined as <code>logger.WARN</code> then <code>logger.INFO</code> and
<code>logger.DEBUG</code> level messages are not logged.
The default set level at startup is <code>logger.DEBUG</code>.</p>
<h3>Constants</h3>
<dl class="reference">
<dt><strong>logger.DEBUG</strong></dt>
<dd>The <em>DEBUG</em> level designates fine-grained informational events that
are most useful to debug an application.</dd>
<dt><strong>logger.INFO</strong></dt>
<dd>The <em>INFO</em> level designates informational messages that highlight the
progress of the application at coarse-grained level.</dd>
<dt><strong>logger.WARN</strong></dt>
<dd>The <em>WARN</em> level designates potentially harmful situations.</dd>
<dt><strong>logger.ERROR</strong></dt>
<dd>The <em>ERROR</em> level designates error events that might still allow the
application to continue running.</dd>
<dt><strong>logger.FATAL</strong></dt>
<dd>The <em>FATAL</em> level designates very severe error events that would
presumably lead the application to abort.</dd>
<dt><strong>logger.OFF</strong></dt>
<dd>The <em>OFF</em> level will stop all log messages.</dd>
</dl>
<h3>Methods</h3>
<dl class="reference">
<dt><strong>logger:log (level, [message]|[table]|[format, ...]|[function, ...])</strong></dt>
<dd>Logs a message with the specified level.</dd>
<dt><strong>logger:setLevel (level)</strong></dt>
<dd>This method sets a minimum level for messages to be logged.</dd>
<dt><strong>logger:getPrint (level)</strong></dt>
<dd>This method returns a print-like function that redirects all output to
the logger instead of the console. The <code>level</code> parameter specifies
the log-level of the output.</dd>
</dl>
<p>The following set of methods is dynamically generated from the log-levels.</p>
<dl class="reference">
<dt><strong>logger:debug ([message]|[table]|[format, ...]|[function, ...])</strong></dt>
<dd>Logs a message with DEBUG level.</dd>
<dt><strong>logger:info ([message]|[table]|[format, ...]|[function, ...])</strong></dt>
<dd>Logs a message with INFO level.</dd>
<dt><strong>logger:warn ([message]|[table]|[format, ...]|[function, ...])</strong></dt>
<dd>Logs a message with WARN level.</dd>
<dt><strong>logger:error ([message]|[table]|[format, ...]|[function, ...])</strong></dt>
<dd>Logs a message with ERROR level.</dd>
<dt><strong>logger:fatal ([message]|[table]|[format, ...]|[function, ...])</strong></dt>
<dd>Logs a message with FATAL level.</dd>
</dl>
<h2><a name="examples"></a>Examples</h2>
<p>The example below creates a logger that prints the level and message
to the standard output (or whatever the print function does).</p>
<pre class="example">
local Logging = require "logging"
local appender = function(self, level, message)
print(level, message)
return true
end
local logger = Logging.new(appender)
logger:setLevel(logger.WARN)
logger:log(logger.INFO, "sending email")
logger:info("trying to contact server")
logger:warn("server did not respond yet")
logger:error("server unreachable")
-- dump a table in a log message
local tab = { a = 1, b = 2 }
logger:debug(tab)
-- use string.format() style formatting
logger:info("val1='%s', val2=%d", "string value", 1234)
-- complex log formatting.
local function log_callback(val1, val2)
-- Do some complex pre-processing of parameters, maybe dump a table to a string.
return string.format("val1='%s', val2=%d", val1, val2)
end
-- function 'log_callback' will only be called if the current log level is "DEBUG"
logger:debug(log_callback, "string value", 1234)
-- create a print that redirects to the logger at level "INFO"
logger:setLevel (logger.INFO)
local print = logger:getPrint(logger.INFO)
print "hello\nthere!"
</pre>
<p>Upon execution of the above example the following lines will
show in the standard output. Notice that some of the <em>INFO</em> log requests
are not handled because the minimum level is set to <em>WARN</em>.</p>
<pre class="example">
WARN server did not responded yet
ERROR server unreachable
INFO hello
INFO there!
</pre>
<a name="appenders"></a>
<h2>Appenders</h2>
The following appenders are included in the standard distribution.
<ul>
<li><a href="console.html">Console</a></li>
<li><a href="file.html">File</a></li>
<li><a href="rolling_file.html">Rolling File</a></li>
<li><a href="sql.html">SQL</a></li>
<li><a href="socket.html">Socket</a></li>
<li><a href="rsyslog.html">Rsyslog</a></li>
<li><a href="email.html">Email</a></li>
<li><a href="nginx.html">Nginx/OpenResty</a></li>
</ul>
<h2>Upgrading from 1.0.0</h2>
<p>Upgrading from LuaLogging 1.0.0 is very easy. The
<code>logger</code> object is fully compatible. You just need to
change the code that creates the object.</p>
<p>The <code>logger</code> constructor from 1.0.0 received a single
argument which was a filename. To upgrade to 1.1.0 you should
create a <code>logging.file</code> object instead, passing the
filename as argument. As simple as this.</p>
</div> <!-- id="content" -->
</div> <!-- id="main" -->
<div id="about">
<p><a href="https://validator.w3.org/check?uri=referer">
<img src="https://www.w3.org/Icons/valid-xhtml10" alt="XHTML 1.0 v&aacute;lido!" height="31" width="88" /></a></p>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>
</html>

98
docs/nginx.html Normal file
View File

@ -0,0 +1,98 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"https://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>LuaLogging: A simple API to use logging features in Lua</title>
<link rel="stylesheet" href="doc.css" type="text/css"/>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
</head>
<body>
<div id="container">
<div id="product">
<div id="product_logo">
<img alt="LuaLogging logo" src="images/lualogging-128.png"/>
</div>
<div id="product_name"><big><strong>LuaLogging</strong></big></div>
<div id="product_description">A simple API to use logging features in Lua.</div>
</div> <!-- id="product" -->
<div id="main">
<div id="navigation">
<h1>LuaLogging</h1>
<ul>
<li><a href="index.html">Home</a></li>
<li><a href="manual.html">Manual</a>
<ul>
<li><a href="manual.html#installation">Installation</a></li>
<li><a href="manual.html#logger">Logger objects</a></li>
<li><a href="manual.html#examples">Examples</a></li>
</ul>
</li>
<li><a href="manual.html#appenders">Appenders</a>
<ul>
<li><a href="console.html">Console</a></li>
<li><a href="file.html">File</a></li>
<li><a href="rolling_file.html">Rolling File</a></li>
<li><a href="sql.html">SQL</a></li>
<li><a href="socket.html">Socket</a></li>
<li><a href="rsyslog.html">Rsyslog</a></li>
<li><a href="email.html">Email</a></li>
<li><strong>Nginx</strong></li>
</ul>
</li>
<li><a href="https://github.com/lunarmodules/lualogging">Project</a></li>
<li><a href="license.html">License</a></li>
</ul>
</div> <!-- id="navigation" -->
<div id="content">
<h2>Nginx/Openresty appender</h2>
<p>This appender has no configuration options, and simply forwards whatever is
being logged to the configured Nginx log. LuaLogging log-levels will be converted
to the equivalent Nginx ones, and initial log-level will be set to match the
Nginx system level.</p>
<p>It makes sense to configure this logger and set it as the default logger on
Nginx/OpenResty startup. Any libraries will then be able to just grab the
defaultLogger from <code>logging.defaultLogger()</code> and won't need any
modifications to work with Nginx/OpenResty.</p>
<h2>Examples</h2>
<pre class="example">
require("logging.nginx")
local logging = require("logging")
local logger = logging.nginx()
logging.defaultLogger(logger)
logger:info("logging.nginx test")
logger:debug("debugging...")
logger:error("error!")
</pre>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
</div> <!-- id="content" -->
</div> <!-- id="main" -->
<div id="about">
<p><a href="https://validator.w3.org/check?uri=referer">
<img src="https://www.w3.org/Icons/valid-xhtml10" alt="XHTML 1.0 v&aacute;lido!" height="31" width="88" /></a></p>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>
</html>

153
docs/rolling_file.html Normal file
View File

@ -0,0 +1,153 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"https://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>LuaLogging: A simple API to use logging features in Lua</title>
<link rel="stylesheet" href="doc.css" type="text/css"/>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
</head>
<body>
<div id="container">
<div id="product">
<div id="product_logo">
<img alt="LuaLogging logo" src="images/lualogging-128.png"/>
</div>
<div id="product_name"><big><strong>LuaLogging</strong></big></div>
<div id="product_description">A simple API to use logging features in Lua.</div>
</div> <!-- id="product" -->
<div id="main">
<div id="navigation">
<h1>LuaLogging</h1>
<ul>
<li><a href="index.html">Home</a></li>
<li><a href="manual.html">Manual</a>
<ul>
<li><a href="manual.html#installation">Installation</a></li>
<li><a href="manual.html#logger">Logger objects</a></li>
<li><a href="manual.html#examples">Examples</a></li>
</ul>
</li>
<li><a href="manual.html#appenders">Appenders</a>
<ul>
<li><a href="console.html">Console</a></li>
<li><a href="file.html">File</a></li>
<li><strong>Rolling File</strong></li>
<li><a href="sql.html">SQL</a></li>
<li><a href="socket.html">Socket</a></li>
<li><a href="rsyslog.html">Rsyslog</a></li>
<li><a href="email.html">Email</a></li>
<li><a href="nginx.html">Nginx</a></li>
</ul>
</li>
<li><a href="https://github.com/lunarmodules/lualogging">Project</a></li>
<li><a href="license.html">License</a></li>
</ul>
</div> <!-- id="navigation" -->
<div id="content">
<h2>Rolling File appender</h2>
<p>The rolling file appender can be used to write log messages to a file. It
uses Lua I/O routines to do its job. The rolling file appender rolls over the logfile
once it has reached a certain size limit. It also mantains a maximum number of log files.</p>
<pre class="example">
function logging.rolling_file {
[filename = <i>string</i>,]
maxFileSize = <i>number</i>,
[maxBackupIndex = <i>number</i>,]
[logPattern = <i>string</i>,]
[logPatterns = {
[logging.DEBUG = <i>string</i>,]
[logging.INFO = <i>string</i>,]
[logging.WARN = <i>string</i>,]
[logging.ERROR = <i>string</i>,]
[logging.FATAL = <i>string</i>,]
},]
[timestampPattern = <i>string</i>,]
[logLevel = <i>log-level-constant</i>,]
}
</pre>
<ul>
<li><code>filename</code>:<br />
The name of the file to be written to.<br />
If the file cannot be opened for appending the logging request
returns nil and an error message.<br />
The default value is <code>"lualogging.log"</code>.</li>
<li><code>maxFileSize</code>:<br />
The max size of the file in bytes. Every time the file reaches this size
it will rollover, generating a new clean log file and storing the old log
on a filename.n, where n goes from 1 to the configured maxBackupIndex.<br />
The more recent backup is the one with the lowest n on its filename.<br />
Eg. test.log.1 (most recent backup) <br />
test.log.2 (least recent backup)
</li>
<li><code>maxBackupIndex</code>:<br />
The number of backup files that will be generated.
The default value is <code>1</code>.</li>
<li><code>logPatterns</code>:<br />
A table with logPattern strings indexed by the log-levels.
A logPattern specifies how the message is written.<br />
If this parameter is omitted, a patterns table will be created with the
parameter <code>logPattern</code> as the default value for each log-level.
If <code>logPattern</code> also is omitted then each level will fall back to
the current default setting, see <code>logging.defaultLogPatterns</code>.
</li>
<li><code>logPattern</code>:<br />
This value will be used as the default value for each log-level that was
omitted in <code>logPatterns</code>.
</li>
<li><code>timestampPattern</code>:<br />
This is an optional parameter that can be used to specify a date/time formatting
in the log message.
See <code>logging.date</code> for the format.
The default is taken from <code>logging.defaultTimestampPattern()</code>.</li>
<li><code>logLevel</code>:<br />
The initial log-level to set for the created logger.</li>
</ul>
<h2>Example</h2>
<pre class="example">
require"logging.rolling_file"
local logger = logging.rolling_file {
filename = "test.log",
maxFileSize = 1024,
maxBackupIndex = 5,
}
logger:info("logging.rolling_file test")
logger:debug("debugging...")
logger:error("error!")
</pre>
<p>&nbsp;</p>
<p>&nbsp;</p>
</div> <!-- id="content" -->
</div> <!-- id="main" -->
<div id="about">
<p><a href="https://validator.w3.org/check?uri=referer">
<img src="https://www.w3.org/Icons/valid-xhtml10" alt="XHTML 1.0 v&aacute;lido!" height="31" width="88" /></a></p>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>
</html>

239
docs/rsyslog.html Normal file
View File

@ -0,0 +1,239 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"https://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>LuaLogging: A simple API to use logging features in Lua</title>
<link rel="stylesheet" href="doc.css" type="text/css"/>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
</head>
<body>
<div id="container">
<div id="product">
<div id="product_logo">
<img alt="LuaLogging logo" src="images/lualogging-128.png"/>
</div>
<div id="product_name"><big><strong>LuaLogging</strong></big></div>
<div id="product_description">A simple API to use logging features in Lua.</div>
</div> <!-- id="product" -->
<div id="main">
<div id="navigation">
<h1>LuaLogging</h1>
<ul>
<li><a href="index.html">Home</a></li>
<li><a href="manual.html">Manual</a>
<ul>
<li><a href="manual.html#installation">Installation</a></li>
<li><a href="manual.html#logger">Logger objects</a></li>
<li><a href="manual.html#examples">Examples</a></li>
</ul>
</li>
<li><a href="manual.html#appenders">Appenders</a>
<ul>
<li><a href="console.html">Console</a></li>
<li><a href="file.html">File</a></li>
<li><a href="rolling_file.html">Rolling File</a></li>
<li><a href="sql.html">SQL</a></li>
<li><a href="socket.html">Socket</a></li>
<li><strong>Rsyslog</strong></li>
<li><a href="email.html">Email</a></li>
<li><a href="nginx.html">Nginx</a></li>
</ul>
</li>
<li><a href="https://github.com/lunarmodules/lualogging">Project</a></li>
<li><a href="license.html">License</a></li>
</ul>
</div> <!-- id="navigation" -->
<div id="content">
<h2>Remote syslog appender</h2>
<p>This appender can be used to send syslog formatted log requests to a remote syslog server.
This appender relies on <a href="https://github.com/lunarmodules/luasocket">LuaSocket</a>
to do its job. Optionally <a href="https://github.com/lunarmodules/copas">Copas</a> can be
used for async sending of log messages over TCP.<br /></p>
<p>If you want to log to the local syslog daemon, then please use
<a href="https://github.com/lunarmodules/luasyslog">luasyslog</a>.</p>
<p>The LuaLogging log-levels will be mapped to the syslog levels by their name, where
<code>logging.FATAL</code> maps to syslog <code>ALERT</code>. Since LuaLogging only has 5
levels, the syslog levels <code>EMERG</code>, <code>CRIT</code>, and <code>NOTICE</code>
remain unused.</p>
<pre class="example">
function logging.rsyslog {
hostname = <i>string</i>,
[port = <i>number</i>,]
[protocol = <i>"udp"</i> | <i>"tcp"</i>,]
[rfc = <i>"rfc5424"</i> | <i>"rfc3164"</i>,]
[maxsize = <i>number</i>,]
[facility = <i>facility-constant</i>,]
[ident = <i>string</i>,]
[procid = <i>string</i>,]
[msgid = <i>string</i>,]
[logPattern = <i>string</i>,]
[logPatterns = {
[logging.DEBUG = <i>string</i>,]
[logging.INFO = <i>string</i>,]
[logging.WARN = <i>string</i>,]
[logging.ERROR = <i>string</i>,]
[logging.FATAL = <i>string</i>,]
},]
[logLevel = <i>log-level-constant</i>,]
}
</pre>
<ul>
<li><code>hostname</code>:<br />
Hostname can be an IP address or a host name of the server where the log
message will be sent.
</li>
<li><code>port</code>:<br />
The port must be an integer number in the range [1..64K). The default is
<code>514</code>.
</li>
<li><code>protocol</code>:<br />
The network protocol to use, either <code>"udp"</code> or <code>"tcp"</code>.
The default is <code>"tcp"</code>.
<br/><br/>
Note: TCP sending is a blocking operation. For non-blocking use
<a href="https://github.com/lunarmodules/copas">Copas</a>
is supported, see the example below.
</li>
<li><code>rfc</code>:<br />
The message format. 2 options:
<ul>
<li>for the old BSD style use <code>"rfc3164"</code> (the default)</li>
<li>for the IETF standard use <code>"rfc5424"</code>.</li>
</ul>
</li>
<li><code>maxsize</code>:<br />
The maximum message size. Longer messages will be truncated. The minimum value is 480.
The defaults are 1024 for <code>"rfc3164"</code>, and 2048 for <code>"rfc5424"</code>.
</li>
<li><code>facility</code>:<br />
The syslog facility to use, use one of the constants. The default value
is the "user" facility.
<pre class="example">
rsyslog.FACILITY_KERN
rsyslog.FACILITY_USER
rsyslog.FACILITY_MAIL
rsyslog.FACILITY_DAEMON
rsyslog.FACILITY_AUTH
rsyslog.FACILITY_SYSLOG
rsyslog.FACILITY_LPR
rsyslog.FACILITY_NEWS
rsyslog.FACILITY_UUCP
rsyslog.FACILITY_CRON
rsyslog.FACILITY_AUTHPRIV
rsyslog.FACILITY_FTP
rsyslog.FACILITY_NTP
rsyslog.FACILITY_SECURITY
rsyslog.FACILITY_CONSOLE
rsyslog.FACILITY_NETINFO
rsyslog.FACILITY_REMOTEAUTH
rsyslog.FACILITY_INSTALL
rsyslog.FACILITY_RAS
rsyslog.FACILITY_LOCAL0
rsyslog.FACILITY_LOCAL1
rsyslog.FACILITY_LOCAL2
rsyslog.FACILITY_LOCAL3
rsyslog.FACILITY_LOCAL4
rsyslog.FACILITY_LOCAL5
rsyslog.FACILITY_LOCAL6
rsyslog.FACILITY_LOCAL7
</pre>
</li>
<li><code>ident</code>:<br />
The value for the APP-NAME field (rfc5424, called TAG field in rfc3164).
Default value is <code>"lua"</code>.
</li>
<li><code>procid</code>:<br />
The process id value (only applicable for rfc5424 format).
Default value is <code>"-"</code> (absent).
</li>
<li><code>msgid</code>:<br />
The message id value (only applicable for rfc5424 format).
Default value is <code>"-"</code> (absent).
</li>
<li><code>logPattern</code>:<br />
This value will be used as the default value for each log-level that was
omitted in <code>logPatterns</code>, this defaults to <code>"%message"</code>.
The default pattern deviates from standard LuaLogging defaults since those
do not make sense for syslog.
</li>
<li><code>logPatterns</code>:<br />
A table with logPattern strings indexed by the log-levels.
A logPattern specifies how the message is written.<br />
If this parameter is omitted, a patterns table will be created with the
parameter <code>logPattern</code> as the default value for each log-level.
</li>
<li><code>logLevel</code>:<br />
The initial log-level to set for the created logger.
</li>
</ul>
<h2>Example</h2>
<pre class="example">
local rsyslog = require "logging.rsyslog"
rsyslog.copas() -- switch to using non-blocking Copas sockets
local logger = rsyslog {
hostname = "syslog.mycompany.com",
port = 514,
protocol = "tcp",
rfc = "rfc5424",
maxsize = 8000,
facility = rsyslog.FACILITY_LOCAL2,
ident = "my_lua_app",
procid = "socket_mod",
logPattern = "%message %source",
}
copas.loop(function()
logger:info("logging.rsyslog test")
logger:debug("debugging...")
logger:error("error!")
-- destroy to ensure threads are shutdown (only for Copas)
logger:destroy()
end)
</pre>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
</div> <!-- id="content" -->
</div> <!-- id="main" -->
<div id="about">
<p><a href="https://validator.w3.org/check?uri=referer">
<img src="https://www.w3.org/Icons/valid-xhtml10" alt="XHTML 1.0 v&aacute;lido!" height="31" width="88" /></a></p>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>
</html>

144
docs/socket.html Normal file
View File

@ -0,0 +1,144 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"https://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>LuaLogging: A simple API to use logging features in Lua</title>
<link rel="stylesheet" href="doc.css" type="text/css"/>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
</head>
<body>
<div id="container">
<div id="product">
<div id="product_logo">
<img alt="LuaLogging logo" src="images/lualogging-128.png"/>
</div>
<div id="product_name"><big><strong>LuaLogging</strong></big></div>
<div id="product_description">A simple API to use logging features in Lua.</div>
</div> <!-- id="product" -->
<div id="main">
<div id="navigation">
<h1>LuaLogging</h1>
<ul>
<li><a href="index.html">Home</a></li>
<li><a href="manual.html">Manual</a>
<ul>
<li><a href="manual.html#installation">Installation</a></li>
<li><a href="manual.html#logger">Logger objects</a></li>
<li><a href="manual.html#examples">Examples</a></li>
</ul>
</li>
<li><a href="manual.html#appenders">Appenders</a>
<ul>
<li><a href="console.html">Console</a></li>
<li><a href="file.html">File</a></li>
<li><a href="rolling_file.html">Rolling File</a></li>
<li><a href="sql.html">SQL</a></li>
<li><strong>Socket</strong></li>
<li><a href="rsyslog.html">Rsyslog</a></li>
<li><a href="email.html">Email</a></li>
<li><a href="nginx.html">Nginx</a></li>
</ul>
</li>
<li><a href="https://github.com/lunarmodules/lualogging">Project</a></li>
<li><a href="license.html">License</a></li>
</ul>
</div> <!-- id="navigation" -->
<div id="content">
<h2>Socket appender</h2>
This appender can be used to send log requests through a socket.
Socket appender relies on <a href=
"https://www.tecgraf.puc-rio.br/luasocket">LuaSocket</a> to do its
job.<br />
Upon each log request a connection is opened, the message is sent
and the connection is closed.
<pre class="example">
function logging.socket {
hostname = <i>string</i>,
port = <i>number</i>,
[logPattern = <i>string</i>,]
[logPatterns = {
[logging.DEBUG = <i>string</i>,]
[logging.INFO = <i>string</i>,]
[logging.WARN = <i>string</i>,]
[logging.ERROR = <i>string</i>,]
[logging.FATAL = <i>string</i>,]
},]
[timestampPattern = <i>string</i>,]
[logLevel = <i>log-level-constant</i>,]
}
</pre>
<ul>
<li><code>hostname</code>:<br />
Hostname can be an IP address or a host name of the server where the log
message will be sent.</li>
<li><code>port</code>:<br />
The port must be an integer number in the range [1..64K).</li>
<li><code>logPatterns</code>:<br />
A table with logPattern strings indexed by the log-levels.
A logPattern specifies how the message is written.<br />
If this parameter is omitted, a patterns table will be created with the
parameter <code>logPattern</code> as the default value for each log-level.
If <code>logPattern</code> also is omitted then each level will fall back to
the current default setting, see <code>logging.defaultLogPatterns</code>.
</li>
<li><code>logPattern</code>:<br />
This value will be used as the default value for each log-level that was
omitted in <code>logPatterns</code>.
</li>
<li><code>timestampPattern</code>:<br />
This is an optional parameter that can be used to specify a date/time formatting
in the log message.
See <code>logging.date</code> for the format.
The default is taken from <code>logging.defaultTimestampPattern()</code>.</li>
<li><code>logLevel</code>:<br />
The initial log-level to set for the created logger.</li>
</ul>
<h2>Example</h2>
<pre class="example">
require"logging.socket"
local logger = logging.socket {
hostname = "localhost",
port = 5000,
}
logger:info("logging.socket test")
logger:debug("debugging...")
logger:error("error!")
</pre>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
</div> <!-- id="content" -->
</div> <!-- id="main" -->
<div id="about">
<p><a href="https://validator.w3.org/check?uri=referer">
<img src="https://www.w3.org/Icons/valid-xhtml10" alt="XHTML 1.0 v&aacute;lido!" height="31" width="88" /></a></p>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>
</html>

139
docs/sql.html Normal file
View File

@ -0,0 +1,139 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"https://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>LuaLogging: A simple API to use logging features in Lua</title>
<link rel="stylesheet" href="doc.css" type="text/css"/>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
</head>
<body>
<div id="container">
<div id="product">
<div id="product_logo">
<img alt="LuaLogging logo" src="images/lualogging-128.png"/>
</div>
<div id="product_name"><big><strong>LuaLogging</strong></big></div>
<div id="product_description">A simple API to use logging features in Lua.</div>
</div> <!-- id="product" -->
<div id="main">
<div id="navigation">
<h1>LuaLogging</h1>
<ul>
<li><a href="index.html">Home</a></li>
<li><a href="manual.html">Manual</a>
<ul>
<li><a href="manual.html#installation">Installation</a></li>
<li><a href="manual.html#logger">Logger objects</a></li>
<li><a href="manual.html#examples">Examples</a></li>
</ul>
</li>
<li><a href="manual.html#appenders">Appenders</a>
<ul>
<li><a href="console.html">Console</a></li>
<li><a href="file.html">File</a></li>
<li><a href="rolling_file.html">Rolling File</a></li>
<li><strong>SQL</strong></li>
<li><a href="socket.html">Socket</a></li>
<li><a href="rsyslog.html">Rsyslog</a></li>
<li><a href="email.html">Email</a></li>
<li><a href="nginx.html">Nginx</a></li>
</ul>
</li>
<li><a href="https://github.com/lunarmodules/lualogging">Project</a></li>
<li><a href="license.html">License</a></li>
</ul>
</div> <!-- id="navigation" -->
<div id="content">
<h2>SQL appender</h2>
<p>The SQL appender can be used to write log messages to a SQL
database table. It uses <a href="https://keplerproject.github.io/luasql/">LuaSQL</a>,
therefore any database supported by LuaSQL can be used.</p>
<pre class="example">
function logging.sql{
connectionfactory = <i>function</i>,
[tablename = <i>string</i>,]
[logdatefield = <i>string</i>,]
[loglevelfield = <i>string</i>,]
[logmessagefield = <i>string</i>,]
[keepalive = <i>boolean</i>,]
[logLevel = <i>log-level-constant</i>,]
}
</pre>
<ul>
<li><code>connectionfactory</code>:<br />
This must be a function that creates a LuaSQL connection object.
This function will be called everytime a connection needs to be
created.</li>
<li><code>tablename</code>:<br />
The name of the table to write the log requests. Default value is
<code>"LogTable"</code>.</li>
<li><code>logdatefield</code>:<br />
The name of the field to write the date of each log request.
Default value is <code>"LogDate"</code>.</li>
<li><code>loglevelfield</code>:<br />
The name of the field to write the level of each log request.
Default value is <code>"LogLevel"</code>.</li>
<li><code>logmessagefield</code>:<br />
The name of the field to write the message of each log request.
Default value is <code>"LogMessage"</code>.</li>
<li><code>keepalive</code>:<br />
In every log request a connection to the database is opened, the
message is written, and the connection is closed.<br />
If the user wants to keep the connection opened he can specify
<code>keepalive = true</code>.</li>
<li><code>logLevel</code>:<br />
The initial log-level to set for the created logger.</li>
</ul>
<h2>Example</h2>
<pre class="example">
require"logging.sql"
require"luasql.jdbc"
local env, err = luasql.jdbc('com.mysql.jdbc.Driver')
local logger = logging.sql {
connectionfactory = function()
local con, err = env:connect('jdbc:mysql://localhost/test',
'tcp', '123')
assert(con, err)
return con
end,
keepalive = true,
}
logger:info("logging.sql test")
logger:debug("debugging...")
logger:error("error!")
</pre>
</div> <!-- id="content" -->
</div> <!-- id="main" -->
<div id="about">
<p><a href="https://validator.w3.org/check?uri=referer">
<img src="https://www.w3.org/Icons/valid-xhtml10" alt="XHTML 1.0 v&aacute;lido!" height="31" width="88" /></a></p>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>
</html>

47
lualogging-dev-1.rockspec Normal file
View File

@ -0,0 +1,47 @@
local package_name = "lualogging"
local package_version = "dev"
local rockspec_revision = "1"
local github_account_name = "lunarmodules"
local github_repo_name = package_name
package = package_name
version = package_version.."-"..rockspec_revision
source = {
url = "git://github.com/"..github_account_name.."/"..github_repo_name..".git",
branch = (package_version == "dev") and "master" or nil,
tag = (package_version ~= "dev") and ("v"..package_version) or nil,
}
description = {
summary = "A simple API to use logging features",
detailed = [[
LuaLogging provides a simple API to use logging features in Lua. Its design was
based on log4j. LuaLogging currently supports, through the use of appenders,
console, file, rolling file, email, socket and SQL outputs.
]],
homepage = "https://github.com/"..github_account_name.."/"..github_repo_name,
license = "MIT/X11",
}
dependencies = {
"luasocket"
}
build = {
type = "none",
install = {
lua = {
['logging'] = "src/logging.lua",
['logging.console'] = "src/logging/console.lua",
['logging.file'] = "src/logging/file.lua",
['logging.rolling_file'] = "src/logging/rolling_file.lua",
['logging.email'] = "src/logging/email.lua",
['logging.sql'] = "src/logging/sql.lua",
['logging.socket'] = "src/logging/socket.lua",
['logging.nginx'] = "src/logging/nginx.lua",
['logging.rsyslog'] = "src/logging/rsyslog.lua",
['logging.envconfig'] = "src/logging/envconfig.lua",
}
},
copy_directories = {
"docs",
},
}

View File

@ -0,0 +1,36 @@
package = "lualogging"
version = "1.2.0-2"
source = {
url = "git://github.com/lunarmodules/lualogging.git",
branch = "v1.2.0",
}
description = {
summary = "A simple API to use logging features",
detailed = [[
LuaLogging provides a simple API to use logging features in Lua. Its design was
based on log4j. LuaLogging currently supports, through the use of appenders,
console, file, rolling file, email, socket and SQL outputs.
]],
homepage = "https://github.com/lunarmodules/lualogging",
license = "MIT/X11",
}
dependencies = {
"luasocket"
}
build = {
type = "none",
install = {
lua = {
['logging'] = "src/logging.lua",
['logging.console'] = "src/logging/console.lua",
['logging.file'] = "src/logging/file.lua",
['logging.rolling_file'] = "src/logging/rolling_file.lua",
['logging.email'] = "src/logging/email.lua",
['logging.sql'] = "src/logging/sql.lua",
['logging.socket'] = "src/logging/socket.lua",
}
},
copy_directories = {
"doc/html",
},
}

View File

@ -0,0 +1,36 @@
package = "lualogging"
version = "1.3.0-2"
source = {
url = "git://github.com/lunarmodules/lualogging.git",
branch = "v1.3.0",
}
description = {
summary = "A simple API to use logging features",
detailed = [[
LuaLogging provides a simple API to use logging features in Lua. Its design was
based on log4j. LuaLogging currently supports, through the use of appenders,
console, file, rolling file, email, socket and SQL outputs.
]],
homepage = "https://github.com/lunarmodules/lualogging",
license = "MIT/X11",
}
dependencies = {
"luasocket"
}
build = {
type = "none",
install = {
lua = {
['logging'] = "src/logging.lua",
['logging.console'] = "src/logging/console.lua",
['logging.file'] = "src/logging/file.lua",
['logging.rolling_file'] = "src/logging/rolling_file.lua",
['logging.email'] = "src/logging/email.lua",
['logging.sql'] = "src/logging/sql.lua",
['logging.socket'] = "src/logging/socket.lua",
}
},
copy_directories = {
"doc/html",
},
}

View File

@ -0,0 +1,36 @@
package = "lualogging"
version = "1.4.0-1"
source = {
url = "git://github.com/lunarmodules/lualogging.git",
branch = "v1.4.0",
}
description = {
summary = "A simple API to use logging features",
detailed = [[
LuaLogging provides a simple API to use logging features in Lua. Its design was
based on log4j. LuaLogging currently supports, through the use of appenders,
console, file, rolling file, email, socket and SQL outputs.
]],
homepage = "https://github.com/lunarmodules/lualogging",
license = "MIT/X11",
}
dependencies = {
"luasocket"
}
build = {
type = "none",
install = {
lua = {
['logging'] = "src/logging.lua",
['logging.console'] = "src/logging/console.lua",
['logging.file'] = "src/logging/file.lua",
['logging.rolling_file'] = "src/logging/rolling_file.lua",
['logging.email'] = "src/logging/email.lua",
['logging.sql'] = "src/logging/sql.lua",
['logging.socket'] = "src/logging/socket.lua",
}
},
copy_directories = {
"docs",
},
}

View File

@ -0,0 +1,43 @@
local package_name = "lualogging"
local package_version = "1.5.0"
local rockspec_revision = "1"
local github_account_name = "lunarmodules"
local github_repo_name = package_name
package = package_name
version = package_version.."-"..rockspec_revision
source = {
url = "git://github.com/"..github_account_name.."/"..github_repo_name..".git",
tag = "v"..package_version
}
description = {
summary = "A simple API to use logging features",
detailed = [[
LuaLogging provides a simple API to use logging features in Lua. Its design was
based on log4j. LuaLogging currently supports, through the use of appenders,
console, file, rolling file, email, socket and SQL outputs.
]],
homepage = "https://github.com/"..github_account_name.."/"..github_repo_name,
license = "MIT/X11",
}
dependencies = {
"luasocket"
}
build = {
type = "none",
install = {
lua = {
['logging'] = "src/logging.lua",
['logging.console'] = "src/logging/console.lua",
['logging.file'] = "src/logging/file.lua",
['logging.rolling_file'] = "src/logging/rolling_file.lua",
['logging.email'] = "src/logging/email.lua",
['logging.sql'] = "src/logging/sql.lua",
['logging.socket'] = "src/logging/socket.lua",
}
},
copy_directories = {
"docs",
},
}

View File

@ -0,0 +1,43 @@
local package_name = "lualogging"
local package_version = "1.5.1"
local rockspec_revision = "1"
local github_account_name = "lunarmodules"
local github_repo_name = package_name
package = package_name
version = package_version.."-"..rockspec_revision
source = {
url = "git://github.com/"..github_account_name.."/"..github_repo_name..".git",
tag = "v"..package_version
}
description = {
summary = "A simple API to use logging features",
detailed = [[
LuaLogging provides a simple API to use logging features in Lua. Its design was
based on log4j. LuaLogging currently supports, through the use of appenders,
console, file, rolling file, email, socket and SQL outputs.
]],
homepage = "https://github.com/"..github_account_name.."/"..github_repo_name,
license = "MIT/X11",
}
dependencies = {
"luasocket"
}
build = {
type = "none",
install = {
lua = {
['logging'] = "src/logging.lua",
['logging.console'] = "src/logging/console.lua",
['logging.file'] = "src/logging/file.lua",
['logging.rolling_file'] = "src/logging/rolling_file.lua",
['logging.email'] = "src/logging/email.lua",
['logging.sql'] = "src/logging/sql.lua",
['logging.socket'] = "src/logging/socket.lua",
}
},
copy_directories = {
"docs",
},
}

View File

@ -0,0 +1,43 @@
local package_name = "lualogging"
local package_version = "1.5.2"
local rockspec_revision = "1"
local github_account_name = "lunarmodules"
local github_repo_name = package_name
package = package_name
version = package_version.."-"..rockspec_revision
source = {
url = "git://github.com/"..github_account_name.."/"..github_repo_name..".git",
tag = "v"..package_version
}
description = {
summary = "A simple API to use logging features",
detailed = [[
LuaLogging provides a simple API to use logging features in Lua. Its design was
based on log4j. LuaLogging currently supports, through the use of appenders,
console, file, rolling file, email, socket and SQL outputs.
]],
homepage = "https://github.com/"..github_account_name.."/"..github_repo_name,
license = "MIT/X11",
}
dependencies = {
"luasocket"
}
build = {
type = "none",
install = {
lua = {
['logging'] = "src/logging.lua",
['logging.console'] = "src/logging/console.lua",
['logging.file'] = "src/logging/file.lua",
['logging.rolling_file'] = "src/logging/rolling_file.lua",
['logging.email'] = "src/logging/email.lua",
['logging.sql'] = "src/logging/sql.lua",
['logging.socket'] = "src/logging/socket.lua",
}
},
copy_directories = {
"docs",
},
}

View File

@ -0,0 +1,44 @@
local package_name = "lualogging"
local package_version = "1.6.0"
local rockspec_revision = "1"
local github_account_name = "lunarmodules"
local github_repo_name = package_name
package = package_name
version = package_version.."-"..rockspec_revision
source = {
url = "git://github.com/"..github_account_name.."/"..github_repo_name..".git",
branch = "master"
}
description = {
summary = "A simple API to use logging features",
detailed = [[
LuaLogging provides a simple API to use logging features in Lua. Its design was
based on log4j. LuaLogging currently supports, through the use of appenders,
console, file, rolling file, email, socket and SQL outputs.
]],
homepage = "https://github.com/"..github_account_name.."/"..github_repo_name,
license = "MIT/X11",
}
dependencies = {
"luasocket"
}
build = {
type = "none",
install = {
lua = {
['logging'] = "src/logging.lua",
['logging.console'] = "src/logging/console.lua",
['logging.file'] = "src/logging/file.lua",
['logging.rolling_file'] = "src/logging/rolling_file.lua",
['logging.email'] = "src/logging/email.lua",
['logging.sql'] = "src/logging/sql.lua",
['logging.socket'] = "src/logging/socket.lua",
['logging.nginx'] = "src/logging/nginx.lua",
}
},
copy_directories = {
"docs",
},
}

View File

@ -0,0 +1,45 @@
local package_name = "lualogging"
local package_version = "1.6.0"
local rockspec_revision = "2"
local github_account_name = "lunarmodules"
local github_repo_name = package_name
package = package_name
version = package_version.."-"..rockspec_revision
source = {
url = "git://github.com/"..github_account_name.."/"..github_repo_name..".git",
branch = (package_version == "dev") and "master" or nil,
tag = (package_version ~= "dev") and ("v"..package_version) or nil,
}
description = {
summary = "A simple API to use logging features",
detailed = [[
LuaLogging provides a simple API to use logging features in Lua. Its design was
based on log4j. LuaLogging currently supports, through the use of appenders,
console, file, rolling file, email, socket and SQL outputs.
]],
homepage = "https://github.com/"..github_account_name.."/"..github_repo_name,
license = "MIT/X11",
}
dependencies = {
"luasocket"
}
build = {
type = "none",
install = {
lua = {
['logging'] = "src/logging.lua",
['logging.console'] = "src/logging/console.lua",
['logging.file'] = "src/logging/file.lua",
['logging.rolling_file'] = "src/logging/rolling_file.lua",
['logging.email'] = "src/logging/email.lua",
['logging.sql'] = "src/logging/sql.lua",
['logging.socket'] = "src/logging/socket.lua",
['logging.nginx'] = "src/logging/nginx.lua",
}
},
copy_directories = {
"docs",
},
}

View File

@ -0,0 +1,46 @@
local package_name = "lualogging"
local package_version = "1.7.0"
local rockspec_revision = "1"
local github_account_name = "lunarmodules"
local github_repo_name = package_name
package = package_name
version = package_version.."-"..rockspec_revision
source = {
url = "git://github.com/"..github_account_name.."/"..github_repo_name..".git",
branch = (package_version == "dev") and "master" or nil,
tag = (package_version ~= "dev") and ("v"..package_version) or nil,
}
description = {
summary = "A simple API to use logging features",
detailed = [[
LuaLogging provides a simple API to use logging features in Lua. Its design was
based on log4j. LuaLogging currently supports, through the use of appenders,
console, file, rolling file, email, socket and SQL outputs.
]],
homepage = "https://github.com/"..github_account_name.."/"..github_repo_name,
license = "MIT/X11",
}
dependencies = {
"luasocket"
}
build = {
type = "none",
install = {
lua = {
['logging'] = "src/logging.lua",
['logging.console'] = "src/logging/console.lua",
['logging.file'] = "src/logging/file.lua",
['logging.rolling_file'] = "src/logging/rolling_file.lua",
['logging.email'] = "src/logging/email.lua",
['logging.sql'] = "src/logging/sql.lua",
['logging.socket'] = "src/logging/socket.lua",
['logging.nginx'] = "src/logging/nginx.lua",
['logging.rsyslog'] = "src/logging/rsyslog.lua",
}
},
copy_directories = {
"docs",
},
}

View File

@ -0,0 +1,47 @@
local package_name = "lualogging"
local package_version = "1.8.0"
local rockspec_revision = "1"
local github_account_name = "lunarmodules"
local github_repo_name = package_name
package = package_name
version = package_version.."-"..rockspec_revision
source = {
url = "git://github.com/"..github_account_name.."/"..github_repo_name..".git",
branch = (package_version == "dev") and "master" or nil,
tag = (package_version ~= "dev") and ("v"..package_version) or nil,
}
description = {
summary = "A simple API to use logging features",
detailed = [[
LuaLogging provides a simple API to use logging features in Lua. Its design was
based on log4j. LuaLogging currently supports, through the use of appenders,
console, file, rolling file, email, socket and SQL outputs.
]],
homepage = "https://github.com/"..github_account_name.."/"..github_repo_name,
license = "MIT/X11",
}
dependencies = {
"luasocket"
}
build = {
type = "none",
install = {
lua = {
['logging'] = "src/logging.lua",
['logging.console'] = "src/logging/console.lua",
['logging.file'] = "src/logging/file.lua",
['logging.rolling_file'] = "src/logging/rolling_file.lua",
['logging.email'] = "src/logging/email.lua",
['logging.sql'] = "src/logging/sql.lua",
['logging.socket'] = "src/logging/socket.lua",
['logging.nginx'] = "src/logging/nginx.lua",
['logging.rsyslog'] = "src/logging/rsyslog.lua",
['logging.envconfig'] = "src/logging/envconfig.lua",
}
},
copy_directories = {
"docs",
},
}

View File

@ -0,0 +1,47 @@
local package_name = "lualogging"
local package_version = "1.8.1"
local rockspec_revision = "1"
local github_account_name = "lunarmodules"
local github_repo_name = package_name
package = package_name
version = package_version.."-"..rockspec_revision
source = {
url = "git://github.com/"..github_account_name.."/"..github_repo_name..".git",
branch = (package_version == "dev") and "master" or nil,
tag = (package_version ~= "dev") and ("v"..package_version) or nil,
}
description = {
summary = "A simple API to use logging features",
detailed = [[
LuaLogging provides a simple API to use logging features in Lua. Its design was
based on log4j. LuaLogging currently supports, through the use of appenders,
console, file, rolling file, email, socket and SQL outputs.
]],
homepage = "https://github.com/"..github_account_name.."/"..github_repo_name,
license = "MIT/X11",
}
dependencies = {
"luasocket"
}
build = {
type = "none",
install = {
lua = {
['logging'] = "src/logging.lua",
['logging.console'] = "src/logging/console.lua",
['logging.file'] = "src/logging/file.lua",
['logging.rolling_file'] = "src/logging/rolling_file.lua",
['logging.email'] = "src/logging/email.lua",
['logging.sql'] = "src/logging/sql.lua",
['logging.socket'] = "src/logging/socket.lua",
['logging.nginx'] = "src/logging/nginx.lua",
['logging.rsyslog'] = "src/logging/rsyslog.lua",
['logging.envconfig'] = "src/logging/envconfig.lua",
}
},
copy_directories = {
"docs",
},
}

View File

@ -0,0 +1,47 @@
local package_name = "lualogging"
local package_version = "1.8.2"
local rockspec_revision = "1"
local github_account_name = "lunarmodules"
local github_repo_name = package_name
package = package_name
version = package_version.."-"..rockspec_revision
source = {
url = "git://github.com/"..github_account_name.."/"..github_repo_name..".git",
branch = (package_version == "dev") and "master" or nil,
tag = (package_version ~= "dev") and ("v"..package_version) or nil,
}
description = {
summary = "A simple API to use logging features",
detailed = [[
LuaLogging provides a simple API to use logging features in Lua. Its design was
based on log4j. LuaLogging currently supports, through the use of appenders,
console, file, rolling file, email, socket and SQL outputs.
]],
homepage = "https://github.com/"..github_account_name.."/"..github_repo_name,
license = "MIT/X11",
}
dependencies = {
"luasocket"
}
build = {
type = "none",
install = {
lua = {
['logging'] = "src/logging.lua",
['logging.console'] = "src/logging/console.lua",
['logging.file'] = "src/logging/file.lua",
['logging.rolling_file'] = "src/logging/rolling_file.lua",
['logging.email'] = "src/logging/email.lua",
['logging.sql'] = "src/logging/sql.lua",
['logging.socket'] = "src/logging/socket.lua",
['logging.nginx'] = "src/logging/nginx.lua",
['logging.rsyslog'] = "src/logging/rsyslog.lua",
['logging.envconfig'] = "src/logging/envconfig.lua",
}
},
copy_directories = {
"docs",
},
}

468
src/logging.lua Normal file
View File

@ -0,0 +1,468 @@
-------------------------------------------------------------------------------
-- includes a new tostring function that handles tables recursively
--
-- @author Danilo Tuler (tuler@ideais.com.br)
-- @author Andre Carregal (info@keplerproject.org)
-- @author Thiago Costa Ponte (thiago@ideais.com.br)
--
-- @copyright 2004-2010 Kepler Project, 2011-2013 Neopallium, 2020-2023 Thijs Schreijer
-------------------------------------------------------------------------------
local type, table, string, _tostring, tonumber = type, table, string, tostring, tonumber
local select = select
local error = error
local format = string.format
local floor = math.floor
local pairs = pairs
local ipairs = ipairs
local logging = {
-- Meta information
_COPYRIGHT = "Copyright (C) 2004-2010 Kepler Project, 2011-2013 Neopallium, 2020-2023 Thijs Schreijer",
_DESCRIPTION = "A simple API to use logging features in Lua",
_VERSION = "LuaLogging 1.8.2",
}
local LEVELS = { "DEBUG", "INFO", "WARN", "ERROR", "FATAL", "OFF" }
local MAX_LEVELS = #LEVELS
for i, level in ipairs(LEVELS) do
LEVELS[level] = i
logging[level] = level
end
local defaultLevel = LEVELS[1]
local defaultLogPattern = "%date %level %message\n"
local defaultLogPatterns = nil
local defaultTimestampPattern = nil
local defaultLogger = nil
local function rewrite_stacktrace()
-- prettify stack-trace, remove lualogging entries and reformat to 1 line
local result = ''
local trace = debug and debug.traceback() or ''
for entry in trace:gmatch("%s*(.-)\n") do
if entry:match("%:%d+%:") and not entry:find('logging.lua') then
result = result .. ' | ' .. entry
end
end
return result
end
-- private log function, with support for formating a complex log message.
local function LOG_MSG(self, level, fmt, ...)
local f_type = type(fmt)
if f_type == 'string' then
if select('#', ...) > 0 then
local status, msg = pcall(format, fmt, ...)
if status then
return self:append(level, msg)
else
return self:append(level, "Error formatting log message: " ..
msg .. rewrite_stacktrace())
end
else
-- only a single string, no formating needed.
return self:append(level, fmt)
end
elseif f_type == 'function' then
-- fmt should be a callable function which returns the message to log
return self:append(level, fmt(...))
end
-- fmt is not a string and not a function, just call tostring() on it.
return self:append(level, logging.tostring(fmt))
end
-- do nothing function for disabled levels.
local function disable_level() end
-- a generic print function that prints to the log
local function print_to_log(logger, level, ...)
local args = { n = select("#", ...), ... }
for i = 1, args.n do args[i] = _tostring(args[i]) end
args = table.concat(args, " ") .. "\n"
for line in args:gmatch("(.-)\n") do
logger:log(level, line)
end
end
-- improved assertion function.
local function assert(exp, ...)
-- if exp is true, we are finished so don't do any processing of the parameters
if exp then return exp, ... end
-- assertion failed, raise error
error(format(...), 2)
end
-------------------------------------------------------------------------------
-- Creates a new logger object
-- @param append Function used by the logger to append a message with a
-- log-level to the log stream.
-- @param startLevel log-level to start with
-- @return Table representing the new logger object.
-- @return String if there was any error setting the custom levels if provided
-------------------------------------------------------------------------------
function logging.new(append, startLevel)
assert(type(append) == "function", "Appender must be a function, got: %s.", type(append))
startLevel = startLevel or defaultLevel
assert(LEVELS[startLevel], "startLevel must be a valid log-level constant if given")
local LEVEL_FUNCS = {}
local logger = {}
logger.append = append
logger.setLevel = function (self, level)
local order = LEVELS[level]
assert(order, "undefined level '%s'", _tostring(level))
local old_level = self.level
self.level = level
self.level_order = order
-- enable/disable levels
for i=1, MAX_LEVELS do
local name = LEVELS[i]:lower()
if i >= order and i ~= MAX_LEVELS then
self[name] = LEVEL_FUNCS[i]
else
self[name] = disable_level
end
end
if old_level and old_level ~= level then
self:log(LEVELS[1], "Logger: changing loglevel from %s to %s", old_level, level)
end
end
-- generic log function.
logger.log = function (self, level, ...)
local order = LEVELS[level]
assert(order, "undefined level `%s'", _tostring(level))
if order < self.level_order then
return
end
return LOG_MSG(self, level, ...)
end
-- a print function generator
logger.getPrint = function (self, level)
local order = LEVELS[level]
assert(order, "undefined level `%s'", _tostring(level))
return function(...)
if order >= self.level_order then
print_to_log(self, level, ...)
end
end
end
-- create the proxy functions for each log level.
for i=1, MAX_LEVELS do
local level = LEVELS[i]
if logger[level:lower()] then
return nil, "'" .. level .."' is not a proper level name since there is already a property '" .. level:lower() .. "'"
end
LEVEL_FUNCS[i] = function(self, ...)
-- no level checking needed here, this function will only be called if it's level is active.
return LOG_MSG(self, level, ...)
end
end
-- insert log level constants
for i=1, MAX_LEVELS do
logger[LEVELS[i]] = LEVELS[i]
end
-- initialize log level.
logger:setLevel(startLevel)
return logger
end
-------------------------------------------------------------------------------
-- Prepares the log message
-------------------------------------------------------------------------------
local sourceDebugLevel = 1 -- this will be set dynamically below
local getDebugInfoLine = debug and "local info = debug.getinfo(%d)" or "local info = { short_src = '?', currentline = -1 }"
function logging.compilePattern(pattern)
pattern = string.format("%q", pattern)
-- replace %source by its components first
pattern = pattern:gsub("%%source", "%%file:%%line in function '%%function'")
local placeholders = {
["date"] = false,
["level"] = false,
["message"] = false,
-- truthy: requires debug info to be fetched first
["file"] = "info.short_src",
["line"] = "tostring(info.currentline)",
["function"] = '(info.name or "unknown function")',
}
local inject_info = false
for placeholder, needs_info in pairs(placeholders) do
local count
pattern, count = pattern:gsub("%%"..placeholder, '"..'..(needs_info or placeholder)..'.."')
inject_info = inject_info or (count>0 and needs_info)
end
-- cleanup start & end
if pattern:sub(1, 4) == '""..' then
pattern = pattern:sub(5, -1)
end
if pattern:sub(-4, -1) == '..""' then
pattern = pattern:sub(1, -5)
end
-- build function
local func = [[
return function(date, level, message)
]]..(inject_info and getDebugInfoLine:format(sourceDebugLevel) or "")..[[
return ]]..pattern..[[
end]]
return (loadstring or load)(func, "lualogging_generated_formatter")()
end
local clearCompiledCache
do
local cache = setmetatable({}, {
__index = function(self, pattern)
-- pattern wasn't found in cache, compile now, and cache format-function
self[pattern] = logging.compilePattern(pattern)
return self[pattern]
end
})
function clearCompiledCache()
for k in pairs(cache) do cache[k] = nil end
end
function logging.prepareLogMsg(lpattern, dpattern, level, message)
return cache[lpattern](dpattern, level, message)
end
end
-------------------------------------------------------------------------------
-- os.date replacement with milliseconds if supported
-- ms placeholder = %q or %xq (where x is number of decimals)
-------------------------------------------------------------------------------
do
local gettime = os.time
local ok, socket = pcall(require, "socket") -- load luasocket if available
if ok then
gettime = socket.gettime
end
-- use a pattern cache to know if we even need ms to format
local patternCache = setmetatable({}, {
__index = function(self, patt)
local placeholder = patt:match("(%%%d*q)")
if not placeholder then
self[patt] = false
return false
end
local size = tonumber(placeholder:sub(2,-2)) or 3
assert(size >= 1 and size <= 6, "millisecond format %q quantifier range is 1 to 6")
self[patt] = ("0"):rep(size) -- a string to grab trailing "0"'s from
return self[patt]
end
})
function logging.date(fmt, t)
fmt = fmt or "%c"
t = t or gettime()
local pad = patternCache[fmt]
local ms
if pad then
-- ms required
ms = math.fmod(t,1)
local mss = (tostring(ms) .. pad):sub(3, -1)
fmt = fmt:gsub("(%%%d*q)", function(placeholder)
return mss:sub(1, #pad)
end)
end
local res, err = os.date(fmt, floor(t)) -- 5.3+ requires t to be an integer
if type(res) == "table" then
res.secf = ms or math.fmod(t,1)
end
return res, err
end
end
-------------------------------------------------------------------------------
-- Converts a Lua value to a string
--
-- Converts Table fields in alphabetical order
-------------------------------------------------------------------------------
local function tostring(value)
local str = ''
if (type(value) ~= 'table') then
if (type(value) == 'string') then
str = string.format("%q", value)
else
str = _tostring(value)
end
else
local auxTable = {}
for key in pairs(value) do
if (tonumber(key) ~= key) then
table.insert(auxTable, key)
else
table.insert(auxTable, tostring(key))
end
end
table.sort(auxTable)
str = str..'{'
local separator = ""
local entry
for _, fieldName in ipairs(auxTable) do
if ((tonumber(fieldName)) and (tonumber(fieldName) > 0)) then
entry = tostring(value[tonumber(fieldName)])
else
entry = fieldName.." = "..tostring(value[fieldName])
end
str = str..separator..entry
separator = ", "
end
str = str..'}'
end
return str
end
logging.tostring = tostring
-------------------------------------------------------------------------------
-- Application level defaults
-------------------------------------------------------------------------------
function logging.defaultLogPatterns(patt)
if patt then
if type(patt) == "string" then
patt = logging.buildLogPatterns({}, patt)
end
assert(type(patt) == "table", "logPatterns must be a string or a table, got: %s", type(patt))
for _, level in ipairs(LEVELS) do
if level ~= "OFF" then
assert(type(patt[level]) == "string", "the patterns contains a '%s' value (instead of a string) for level '%s'", type(patt[level]), level)
end
end
defaultLogPatterns = patt
end
return defaultLogPatterns
end
function logging.defaultTimestampPattern(patt)
if patt then
if type(patt) ~= "string" then
error("timestampPattern must be a string", 2)
end
defaultTimestampPattern = patt
end
return defaultTimestampPattern
end
function logging.defaultLevel(level)
if level then
if not LEVELS[level] then
assert(LEVELS[level], "undefined level '%s'", _tostring(level))
end
defaultLevel = level
end
return defaultLevel
end
function logging.defaultLogger(logger)
if logger then
-- check getPrint to protect against accidental call using colon-notation
if type(logger) ~= "table" or type(logger.getPrint) ~= "function" then
error("expected a logger object", 2)
end
defaultLogger = logger
end
if not defaultLogger then
-- no default logger yet, go create it, using the current defaults
defaultLogger = require("logging.console") { destination = "stderr" }
end
return defaultLogger
end
--- Returns a table of patterns, indexed by loglevel.
-- @param patterns (table, optional) table containing logPattern strings per level, defaults to `{}`
-- @param default (string, optional) the logPattern to be used for levels not yet present in 'patterns'.
-- @return table, with a logPattern for every log-level constant
function logging.buildLogPatterns(patterns, default)
patterns = patterns or {}
assert(type(default) == "string" or type(default) == "nil", "expected default logPattern (2nd argument) to be a string or nil, got: %s", tostring(default))
assert(type(patterns) == "table", "expected patterns (1st argument) to be a table or nil, got: %s", tostring(patterns))
local target = {}
for _, level in ipairs(LEVELS) do
if level ~= "OFF" then
target[level] = patterns[level] or default or defaultLogPatterns[level]
end
end
return target
end
defaultLogPatterns = logging.buildLogPatterns({}, defaultLogPattern)
-------------------------------------------------------------------------------
-- Backward compatible parameter handling
-------------------------------------------------------------------------------
function logging.getDeprecatedParams(lst, ...) -- TODO: remove in next major version
local args = { n = select("#", ...), ...}
if type(args[1]) == "table" then
-- this is the new format of a single params-table
return args[1]
end
local params = {}
for i, param_name in ipairs(lst) do
params[param_name] = args[i]
end
return params
end
-------------------------------------------------------------------------------
-- dynamically detect proper source debug level, since this can vary by Lua versions
-------------------------------------------------------------------------------
if debug then
local detection_logger, test_msg
local function detect_func() detection_logger:debug("message") end -- This function MUST be on a single line!!
local detect_func_info = debug.getinfo(detect_func)
local detect_func_match = detect_func_info.short_src..":"..tostring(detect_func_info.linedefined or -999)
detection_logger = logging.new( function(self, level, message)
test_msg = logging.prepareLogMsg("%source", "", level, message)
end)
while true do
if not pcall(detect_func) then
-- cannot detect debug level, so set the function to fetch debug info to
-- return a table that always returns "na" for each lookup
getDebugInfoLine = "local info = setmetatable({}, { __index = function() return 'na' end })"
break
end
if test_msg:find(detect_func_match, 1, true) then
break -- found correct level, done
end
-- move to next level
sourceDebugLevel = sourceDebugLevel + 1
clearCompiledCache()
end
end
if _VERSION < 'Lua 5.2' then
-- still create 'logging' global for Lua versions < 5.2
_G.logging = logging
end
return logging

53
src/logging/console.lua Normal file
View File

@ -0,0 +1,53 @@
-------------------------------------------------------------------------------
-- Prints logging information to console
--
-- @author Thiago Costa Ponte (thiago@ideais.com.br)
--
-- @copyright 2004-2010 Kepler Project, 2011-2013 Neopallium, 2020-2023 Thijs Schreijer
--
-------------------------------------------------------------------------------
local io = require"io"
local logging = require"logging"
local prepareLogMsg = logging.prepareLogMsg
local destinations = setmetatable({
stdout = "stdout",
stderr = "stderr",
},
{
__index = function(self, key)
if not key then
return "stdout" -- default value
end
error("destination parameter must be either 'stderr' or 'stdout', got: "..tostring(key), 3)
end
})
local M = setmetatable({}, {
__call = function(self, ...)
-- calling on the module instantiates a new logger
return self.new(...)
end,
})
function M.new(params, ...)
params = logging.getDeprecatedParams({ "logPattern" }, params, ...)
local startLevel = params.logLevel or logging.defaultLevel()
local timestampPattern = params.timestampPattern or logging.defaultTimestampPattern()
local destination = destinations[params.destination]
local logPatterns = logging.buildLogPatterns(params.logPatterns, params.logPattern)
return logging.new( function(self, level, message)
io[destination]:write(prepareLogMsg(logPatterns[level], logging.date(timestampPattern), level, message))
return true
end, startLevel)
end
logging.console = M
return M

59
src/logging/email.lua Normal file
View File

@ -0,0 +1,59 @@
-------------------------------------------------------------------------------
-- Emails logging information to the given recipient
--
-- @author Thiago Costa Ponte (thiago@ideais.com.br)
--
-- @copyright 2004-2010 Kepler Project, 2011-2013 Neopallium, 2020-2023 Thijs Schreijer
--
-------------------------------------------------------------------------------
local logging = require"logging"
local smtp = require"socket.smtp"
local M = setmetatable({}, {
__call = function(self, ...)
-- calling on the module instantiates a new logger
return self.new(...)
end,
})
function M.new(params)
params = params or {}
params.headers = params.headers or {}
if params.from == nil then
return nil, "'from' parameter is required"
end
if params.rcpt == nil then
return nil, "'rcpt' parameter is required"
end
local timestampPattern = params.timestampPattern or logging.defaultTimestampPattern()
local logPatterns = logging.buildLogPatterns(params.logPatterns, params.logPattern)
local startLevel = params.logLevel or logging.defaultLevel()
return logging.new( function(self, level, message)
local dt = logging.date(timestampPattern)
local s = logging.prepareLogMsg(logPatterns[level], dt, level, message)
if params.headers.subject then
params.headers.subject =
logging.prepareLogMsg(params.headers.subject, dt, level, message)
end
local msg = { headers = params.headers, body = s }
params.source = smtp.message(msg)
params.port = "25"
local r, e = smtp.send(params)
if not r then
return nil, e
end
return true
end, startLevel)
end
logging.email = M
return M

194
src/logging/envconfig.lua Normal file
View File

@ -0,0 +1,194 @@
-------------------------------------------------------------------------------
-- Configures LuaLogging from the environment
--
-- @author Thijs Schreijer
--
-- @copyright 2004-2010 Kepler Project, 2011-2013 Neopallium, 2020-2023 Thijs Schreijer
--
-------------------------------------------------------------------------------
local ll = require "logging"
local levels = { "DEBUG", "INFO", "WARN", "ERROR", "FATAL", "OFF" }
local default_logger_name = "console"
local default_logger_opts = { destination = "stderr" }
local default_log_level = ll.defaultLevel()
local default_timestamp_pattern = ll.defaultTimestampPattern()
local default_prefix = "LL"
-- -- Debug code to display environment variable access
-- local getenv = os.getenv
-- os.getenv = function(key)
-- print(key.."="..tostring(getenv(key)))
-- return getenv(key)
-- end
local M = {}
local function validate_prefix(prefix)
if type(prefix) ~= "string" then
return nil, "expected prefix to be a string"
end
if prefix ~= prefix:gsub("[^0-9a-zA-Z_]", "") then
return nil, "prefix can contain only A-Z, 0-9, and _"
end
if not prefix:match("^[a-zA-Z]") then
return nil, "prefix must start with A-Z"
end
return true
end
--- table that dynamically looks up environment variables.
-- Keys must be strings "A-Z0-9_", and start with "A-Z". The lookup
-- is case-insensitive, the environment variable MUST be in ALL CAPS.
-- Values will be converted to boolean if "true"/"false" (case-insensitive), or
-- to a number if a valid number.
-- @tparam string prefix for the environment variables
-- @return table
local function envloader(prefix)
assert(validate_prefix(prefix))
prefix = prefix .. "_"
-- looking up in this table will fetch from the environment with the proper prefix
-- will convert bools, numbers, "logLevel" and "logPatterns" to proper format
--
-- NOTE: its a meta-method; so cannot return nil+err, must throw errors!
local env = setmetatable({}, {
__index = function(self, key)
if type(key) ~= "string" or
key ~= key:gsub("[^0-9a-zA-Z_]", "") or
#key == 0 then
return nil
end
local envvar = (prefix..key):upper()
-- log patterns table (special case)
if key:lower() == "logpatterns" then
local patt = {}
for _, l in ipairs(levels) do
local level = ll[l] -- the numeric constant for the level
patt[level] = os.getenv(envvar.."_"..l)
end
if not next(patt) then
return nil -- none set, so return nil instead of empty table
end
return patt
end
-- read the value
local value = os.getenv(envvar)
if value == nil then
return value
end
-- logLevel constants
if key:lower() == "loglevel" then
for _, l in ipairs(levels) do
if value:upper() == l then
return ll[value:upper()]
end
end
error(("not a proper loglevel set in env var %s: '%s' (use one of: %s"):
format(envvar, value, '"DEBUG", "INFO", "WARN", "ERROR", "FATAL", "OFF"'))
end
-- check for boolean
if value:lower() == "true" or value:lower() == "false" then
return value:lower() == "true"
end
-- check for number
if value:gsub("[1-9%.%-]", "") == "" and
#value - #value:gsub("[%.]", "") <= 1 and -- zero or 1 "." character
(#value - #value:gsub("[%-]", "") == 0 or -- zero "-" characters
#value - #value:gsub("[%-]", "") == 1 and value:match("^%-")) and -- 1 "-" character, at first pos
#value - #value:gsub("[1-9]", "") >= 1 then -- at least 1 digit
return tonumber(value)
end
-- return as string
return value
end
})
return env
end
do
local base_set = false
local base_name
local base_opts
--- Collects and sets the default logger and its option (does not create the logger).
-- From an application, this is the first call to make, to set the "prefix" for the
-- environment variables to use.
-- @tparam prefix the prefix for env vars to use.
-- @return true, or nil + err on failure
function M.set_default_settings(prefix)
if base_set then
return nil, "already set a default"
end
local ok, err = validate_prefix(prefix)
if not ok then
return ok, err
end
base_opts = envloader(prefix)
base_name = base_opts.logger
if not base_name then
-- no logger specified, so load options for default, and send defaults if absent
base_name = default_logger_name
for k, v in pairs(default_logger_opts) do
local ev = base_opts[k]
if ev == nil then -- false is a valid value, so check against nil
ev = v
end
base_opts[k] = ev
end
end
-- load additional standard options
base_opts.logLevel = base_opts.logLevel or default_log_level
base_opts.timestampPattern = base_opts.timestampPattern or default_timestamp_pattern
base_opts.logPatterns = ll.buildLogPatterns(base_opts.logPatterns or {}, base_opts.logPattern)
base_set = true
return true
end
-- returns the loggername, and the options table for it
function M.get_default_settings()
if not base_set then
assert(M.set_default_settings(default_prefix))
end
return base_name, base_opts
end
end
--- Gets default logger options and then creates and sets the LuaLogging
-- default logger. Should be called once, at application start. Not to be called by
-- libraries, since this is an application global setting.
-- @tparam[opt] string prefix if provided, then the default settings will be set
-- using this prefix. If they were already set, it will return nil+err in that case.
-- return default logger as set
function M.set_default_logger(prefix)
if prefix then
local ok, err = M.set_default_settings(prefix)
if not ok then
return nil, err
end
end
local name, opts = M.get_default_settings()
-- create the logger
local logger = assert(require("logging."..name)(opts))
-- set default and return it
return ll.defaultLogger(logger)
end
return M

80
src/logging/file.lua Normal file
View File

@ -0,0 +1,80 @@
-------------------------------------------------------------------------------
-- Saves logging information in a file
--
-- @author Thiago Costa Ponte (thiago@ideais.com.br)
--
-- @copyright 2004-2010 Kepler Project, 2011-2013 Neopallium, 2020-2023 Thijs Schreijer
--
-------------------------------------------------------------------------------
local logging = require"logging"
local lastFileNameDatePattern
local lastFileHandler
local buffer_mode do
local dir_separator = _G.package.config:sub(1,1)
local is_windows = dir_separator == '\\'
if is_windows then
-- Windows does not support "line" buffered mode, see
-- https://github.com/lunarmodules/lualogging/pull/9
buffer_mode = "no"
else
buffer_mode = "line"
end
end
local openFileLogger = function (filename, datePattern)
local filename = string.format(filename, logging.date(datePattern))
if (lastFileNameDatePattern ~= filename) then
local f = io.open(filename, "a")
if (f) then
f:setvbuf(buffer_mode)
lastFileNameDatePattern = filename
lastFileHandler = f
return f
else
return nil, string.format("file `%s' could not be opened for writing", filename)
end
else
return lastFileHandler
end
end
local M = setmetatable({}, {
__call = function(self, ...)
-- calling on the module instantiates a new logger
return self.new(...)
end,
})
function M.new(params, ...)
params = logging.getDeprecatedParams({ "filename", "datePattern", "logPattern" }, params, ...)
local filename = params.filename
local datePattern = params.datePattern
local logPatterns = logging.buildLogPatterns(params.logPatterns, params.logPattern)
local timestampPattern = params.timestampPattern or logging.defaultTimestampPattern()
local startLevel = params.logLevel or logging.defaultLevel()
if type(filename) ~= "string" then
filename = "lualogging.log"
end
return logging.new( function(self, level, message)
local f, msg = openFileLogger(filename, datePattern)
if not f then
return nil, msg
end
local s = logging.prepareLogMsg(logPatterns[level], logging.date(timestampPattern), level, message)
f:write(s)
return true
end, startLevel)
end
logging.file = M
return M

55
src/logging/nginx.lua Normal file
View File

@ -0,0 +1,55 @@
-------------------------------------------------------------------------------
-- Forwards logging information to the Nginx log
--
-- @author Thijs Schreijer
--
-- @copyright 2004-2010 Kepler Project, 2011-2013 Neopallium, 2020-2023 Thijs Schreijer
--
-------------------------------------------------------------------------------
local logging = require "logging"
local prepareLogMsg = logging.prepareLogMsg
local ngx_log = assert((ngx or {}).log, "this logger can only be used with OpenResty")
local levels = {
[logging.FATAL] = ngx.EMERG,
[logging.ERROR] = ngx.ERR,
[logging.WARN] = ngx.WARN,
[logging.INFO] = ngx.INFO,
[logging.DEBUG] = ngx.DEBUG,
}
local target_level do
-- set the default lualogging level, based on the detected Nginx level
local sys_log_level = require("ngx.errlog").get_sys_filter_level()
for ll_level, ngx_level in pairs(levels) do
if sys_log_level == ngx_level then
target_level = ll_level
ngx_log(ngx.DEBUG, "setting LuaLogging default level '", ll_level, "' to match Nginx level: ", ngx_level)
break
end
end
assert(target_level, "failed to map ngx log-level '"..tostring(sys_log_level).."' to a LuaLogging level")
end
local M = setmetatable({}, {
__call = function(self, ...)
-- calling on the module instantiates a new logger
return self.new(...)
end,
})
function M.new(params)
return logging.new(function(self, level, message)
return ngx_log(levels[level], prepareLogMsg("%message", "", level, message))
end, target_level)
end
logging.nginx = M
return M

View File

@ -0,0 +1,109 @@
---------------------------------------------------------------------------
-- RollingFileAppender is a FileAppender that rolls over the logfile
-- once it has reached a certain size limit. It also mantains a
-- maximum number of log files.
--
-- @author Tiago Cesar Katcipis (tiagokatcipis@gmail.com)
--
-- @copyright 2004-2010 Kepler Project, 2011-2013 Neopallium, 2020-2023 Thijs Schreijer
---------------------------------------------------------------------------
local logging = require"logging"
local buffer_mode do
local dir_separator = _G.package.config:sub(1,1)
local is_windows = dir_separator == '\\'
if is_windows then
-- Windows does not support "line" buffered mode, see
-- https://github.com/lunarmodules/lualogging/pull/9
buffer_mode = "no"
else
buffer_mode = "line"
end
end
local function openFile(self)
self.file = io.open(self.filename, "a")
if not self.file then
return nil, string.format("file `%s' could not be opened for writing", self.filename)
end
self.file:setvbuf(buffer_mode)
return self.file
end
local rollOver = function (self)
for i = self.maxIndex - 1, 1, -1 do
-- files may not exist yet, lets ignore the possible errors.
os.rename(self.filename.."."..tostring(i), self.filename.."."..tostring(i+1))
end
self.file:close()
self.file = nil
local _, msg = os.rename(self.filename, self.filename..".".."1")
if msg then
return nil, string.format("error %s on log rollover", msg)
end
return openFile(self)
end
local openRollingFileLogger = function (self)
if not self.file then
return openFile(self)
end
local filesize = self.file:seek("end", 0)
if not filesize then
self.file:close()
self.file = nil
return openFile(self)
end
if (filesize < self.maxSize) then
return self.file
end
return rollOver(self)
end
local M = setmetatable({}, {
__call = function(self, ...)
-- calling on the module instantiates a new logger
return self.new(...)
end,
})
function M.new(params, ...)
params = logging.getDeprecatedParams({ "filename", "maxFileSize", "maxBackupIndex", "logPattern" }, params, ...)
local logPatterns = logging.buildLogPatterns(params.logPatterns, params.logPattern)
local timestampPattern = params.timestampPattern or logging.defaultTimestampPattern()
local startLevel = params.logLevel or logging.defaultLevel()
local obj = {
filename = type(params.filename) == "string" and params.filename or "lualogging.log",
maxSize = params.maxFileSize,
maxIndex = params.maxBackupIndex or 1
}
return logging.new( function(self, level, message)
local f, msg = openRollingFileLogger(obj)
if not f then
return nil, msg
end
local s = logging.prepareLogMsg(logPatterns[level], logging.date(timestampPattern), level, message)
f:write(s)
return true
end, startLevel)
end
logging.rolling_file = M
return M

376
src/logging/rsyslog.lua Normal file
View File

@ -0,0 +1,376 @@
-------------------------------------------------------------------------------
-- Syslog for LuaLogging to a remote server using UDP/TCP
--
-- @author Thijs Schreijer
--
-- @copyright 2004-2010 Kepler Project, 2011-2013 Neopallium, 2020-2023 Thijs Schreijer
--
-------------------------------------------------------------------------------
-- This module differs from [`LuaSyslog`](https://github.com/lunarmodules/luasyslog)
-- in that it will log to a remote server, where `LuaSyslog` will log to the local
-- syslog daemon.
local socket = require "socket"
local logging = require "logging"
local prepareLogMsg = logging.prepareLogMsg
local pcall = pcall
local M = setmetatable({}, {
__call = function(self, ...)
-- calling on the module instantiates a new logger
return self.new(...)
end,
})
-- set on module table to be able to override it. For example with a Copas
-- socket. No need for UDP, since UDP sending is non-blocking.
M.tcp = socket.tcp
local NILVALUE = "-"
local MONTHS = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }
-- determine the local machine hostname, once at startup
local HOSTNAME do
if _G.package.config:sub(1,1) == "\\" then
-- Windows
HOSTNAME = os.getenv("COMPUTERNAME")
else
-- Unix like
local f = io.popen ("/bin/hostname")
HOSTNAME = f:read("*a")
f:close()
end
HOSTNAME = (HOSTNAME or ""):gsub("[^\33-\126]", "")
if HOSTNAME == "" then
local s = socket.udp()
s:setpeername("1.1.1.1",80) -- ip for cloudflare dns
HOSTNAME = s:getsockname()
end
HOSTNAME = (HOSTNAME or ""):gsub("[^\33-\126]", "")
if HOSTNAME == "" then
HOSTNAME = NILVALUE
end
end
-- the syslog constants
local syslog_facilities = {
FACILITY_KERN = 0 * 8,
FACILITY_USER = 1 * 8,
FACILITY_MAIL = 2 * 8,
FACILITY_DAEMON = 3 * 8,
FACILITY_AUTH = 4 * 8,
FACILITY_SYSLOG = 5 * 8,
FACILITY_LPR = 6 * 8,
FACILITY_NEWS = 7 * 8,
FACILITY_UUCP = 8 * 8,
FACILITY_CRON = 9 * 8,
FACILITY_AUTHPRIV = 10 * 8,
FACILITY_FTP = 11 * 8,
FACILITY_NTP = 12 * 8,
FACILITY_SECURITY = 13 * 8,
FACILITY_CONSOLE = 14 * 8,
FACILITY_NETINFO = 12 * 8,
FACILITY_REMOTEAUTH = 13 * 8,
FACILITY_INSTALL = 14 * 8,
FACILITY_RAS = 15 * 8,
FACILITY_LOCAL0 = 16 * 8,
FACILITY_LOCAL1 = 17 * 8,
FACILITY_LOCAL2 = 18 * 8,
FACILITY_LOCAL3 = 19 * 8,
FACILITY_LOCAL4 = 20 * 8,
FACILITY_LOCAL5 = 21 * 8,
FACILITY_LOCAL6 = 22 * 8,
FACILITY_LOCAL7 = 23 * 8,
}
for k,v in pairs(syslog_facilities) do
M[k] = v
end
local syslog_levels = {
LOG_EMERG = 0,
LOG_ALERT = 1,
LOG_CRIT = 2,
LOG_ERR = 3,
LOG_WARNING = 4,
LOG_NOTICE = 5,
LOG_INFO = 6,
LOG_DEBUG = 7,
}
-- lookup table: convert lualogging level constant to syslog level constant
local convert = {
[logging.DEBUG] = syslog_levels.LOG_DEBUG,
[logging.INFO] = syslog_levels.LOG_INFO,
[logging.WARN] = syslog_levels.LOG_WARNING,
[logging.ERROR] = syslog_levels.LOG_ERR,
[logging.FATAL] = syslog_levels.LOG_ALERT,
}
-- ---------------------------------------------------------------------------
-- Writer functions for both RFC's
-- ---------------------------------------------------------------------------
M.writers = {}
function M.writers.rfc3164(prio, facility, ident, _, _, msg)
local d = os.date("!*t")
msg = msg:gsub("\n", "\\n"):gsub("\t", " "):gsub("\0-\31", "") -- higher bytes allowed, for UTF8... not according to spec
return ("<%d>%s %2d %02d:%02d:%02d %s %s %s\n"):format(facility + prio, MONTHS[d.month], d.day, d.hour, d.min, d.sec, HOSTNAME, ident, msg)
end
function M.writers.rfc5424(prio, facility, ident, procid, msgid, msg)
local timestamp = logging.date("!%Y-%m-%dT%H:%M:%S.%qZ")
return ("<%d>1 %s %s %s %s %s - %s"):format(facility + prio, timestamp, HOSTNAME, ident, procid, msgid, msg)
end
-- ---------------------------------------------------------------------------
-- sender functions for UDP and TCP
-- ---------------------------------------------------------------------------
M.senders = {}
-- sockets indexed by cache-key.
-- NOTE: sockets should be anchored on the logger object to prevent GC
-- of active ones.
local socket_cache = setmetatable({}, { __mode = "v" })
local function socket_error(self, err)
io.stderr:write(err.."\n")
return nil, err
end
function M.senders.udp(self, msg, is_retry)
local sock = self.socket
if not sock then
local cache_key = self.socket_cache_key
sock = socket_cache[cache_key]
if not sock then
-- create a new socket
local host, port = cache_key:match("^udp://(.+):(%d+)$")
sock = assert(socket.udp())
assert(sock:settimeout(5))
local ok, err = sock:setpeername(host, tonumber(port))
if not ok then
return socket_error(self, "failed to connect to "..cache_key..": ".. tostring(err).."\n"..tostring(msg))
end
-- cache it
socket_cache[cache_key] = sock
end
-- anchor it
self.socket = sock
end
local ok, err = sock:send(msg)
if not ok then
sock:close()
self.socket = nil
socket_cache[self.socket_cache_key] = nil
-- recurse once; will recreate the socket and retry
if not is_retry then
return M.senders.udp(self, msg, true)
else
return socket_error(self, "failed to send msg (after retry) to "..self.socket_cache_key..": ".. tostring(err).."\n"..tostring(msg))
end
end
return true
end
function M.senders.tcp(self, msg, is_retry)
local sock = self.socket
if not sock then
local cache_key = self.socket_cache_key
sock = socket_cache[cache_key]
if not sock then
-- create a new socket
local host, port = cache_key:match("^tcp://(.+):(%d+)$")
sock = assert(M.tcp())
assert(sock:settimeout(5))
local ok, err = sock:connect(host, tonumber(port))
if not ok then
return socket_error(self, "failed to connect to "..cache_key..": ".. tostring(err).."\n"..tostring(msg))
end
ok, err = sock:setoption("keepalive", true)
if not ok then
return socket_error(self, "failed to set keepalive for "..cache_key..": ".. tostring(err).."\n"..tostring(msg))
end
-- cache it
socket_cache[cache_key] = sock
end
-- anchor it
self.socket = sock
end
local payload
if self.send_msg_size then
payload = ("%d %s"):format(#msg, msg)
else
payload = msg.."\n" -- old RFC, terminate by LF
end
local last_byte_send = 0
local size = #payload
local err, last_send_error
while last_byte_send < size do
last_byte_send, err, last_send_error = sock:send(payload, last_byte_send + 1, size)
if not last_byte_send then
sock:close()
self.socket = nil
socket_cache[self.socket_cache_key] = nil
-- recurse once; will recreate the socket and retry
if not is_retry then
return M.senders.tcp(self, msg, true)
else
return socket_error(self, "failed to send msg (after retry) to "..self.socket_cache_key.." ("..tostring(last_send_error).." bytes sent): ".. tostring(err).."\n"..tostring(msg))
end
end
end
return true
end
function M.senders.copas(self, msg)
local q = self.queue
q:push(msg)
if q:get_size() >= 1000 then
-- queue is running full, drop an old message
q:pop()
end
end
-- ---------------------------------------------------------------------------
-- instantiation
-- ---------------------------------------------------------------------------
local function validate_facility(f)
for k, v in pairs(syslog_facilities) do
if v == f then
if tostring(k):find("FACILITY_") then
return f
end
end
end
return nil, "bad facility; " .. tostring(f)
end
local function copas_tcp()
local copas = require("copas")
return copas.wrap(socket.tcp())
end
function M.new(params, ...)
params = logging.getDeprecatedParams({ "ident", "facility" }, params, ...)
local logPatterns = logging.buildLogPatterns(params.logPatterns, params.logPattern or "%message")
local startLevel = params.logLevel or logging.defaultLevel()
local ident = (params.ident or "lua"):gsub("[^\33-\126]", "")
assert(#ident > 0 and ident == (params.ident or "lua"), "invalid ident; invalid characters or empty")
local procid = (params.procid or NILVALUE):gsub("[^\33-\126]", "")
assert(#procid > 0 and procid == (params.procid or NILVALUE), "invalid procid; invalid characters or empty")
local msgid = (params.msgid or NILVALUE):gsub("[^\33-\126]", "")
assert(#msgid > 0 and msgid == (params.msgid or NILVALUE), "invalid msgid invalid characters or empty")
local facility = assert(validate_facility(params.facility or syslog_facilities.FACILITY_USER))
local maxsize = tonumber(params.maxsize)
local write = assert(M.writers[params.rfc or "rfc3164"], "unsupported format: "..tostring(params.rfc))
if not maxsize then
maxsize = 2048
if params.rfc == "rfc3164" then
maxsize = 1024
end
end
assert(maxsize > 480, "expected maxsize to be a number > 480")
-- if send_msg_size is truthy then the message will be prefixed by the length. Otherwise
-- an "\n" will be appended as separator. Only applies to TCP.
local send_msg_size = false
if params.rfc == "rfc5424" then
send_msg_size = true
end
local port = params.port or 514
assert(type(port) == "number" and port > 0 and port < 65535, "not a valid port number: " .. tostring(params.port))
local hostname = params.hostname
assert(type(hostname) == "string", "expected 'hostname' to be a string")
local protocol = params.protocol
local send = assert(M.senders[protocol or "tcp"], "unsupported protocol: "..tostring(protocol))
if protocol == "tcp" and M.tcp == copas_tcp then
-- copas was enabled, so use copas instead of tcp
send = M.senders.copas
end
local new_logger = logging.new(function(self, level, message)
message = prepareLogMsg(logPatterns[level], "", level, message)
message = write(convert[level], facility, ident, procid, msgid, message)
send(self, message:sub(1, maxsize))
return true
end, startLevel)
-- set some logger properties
new_logger.socket_cache_key = ("%s://%s:%d"):format(protocol, hostname, port)
new_logger.send_msg_size = send_msg_size
if protocol == "tcp" and M.tcp == copas_tcp then
-- a copas sender, needs an async queue, and a worker to process the messages
local queue = require("copas.queue").new {
name = "lualogging-rsyslog"
}
local tcp_sender = M.senders.tcp
queue:add_worker(function(msg)
local ok, err = pcall(tcp_sender, new_logger, msg)
if not ok then
socket_error(new_logger, "lualogging rsyslog failed logging through Copas socket: "..tostring(err).."\n"..tostring(msg))
end
end)
new_logger.queue = queue
function new_logger:destroy()
if self.queue then
self.queue:stop()
self.queue = nil
end
end
end
return new_logger
end
function M.copas()
if _VERSION=="Lua 5.1" and not jit then -- prevent yield across c-boundary
pcall = require("coxpcall").pcall
end
M.tcp = copas_tcp
end
logging.rsyslog = M
return M

50
src/logging/socket.lua Normal file
View File

@ -0,0 +1,50 @@
-------------------------------------------------------------------------------
-- Sends the logging information through a socket using luasocket
--
-- @author Thiago Costa Ponte (thiago@ideais.com.br)
--
-- @copyright 2004-2010 Kepler Project, 2011-2013 Neopallium, 2020-2023 Thijs Schreijer
--
-------------------------------------------------------------------------------
local logging = require"logging"
local socket = require"socket"
local M = setmetatable({}, {
__call = function(self, ...)
-- calling on the module instantiates a new logger
return self.new(...)
end,
})
function M.new(params, ...)
params = logging.getDeprecatedParams({ "hostname", "port", "logPattern" }, params, ...)
local hostname = params.hostname
local port = params.port
local logPatterns = logging.buildLogPatterns(params.logPatterns, params.logPattern)
local timestampPattern = params.timestampPattern or logging.defaultTimestampPattern()
local startLevel = params.logLevel or logging.defaultLevel()
return logging.new( function(self, level, message)
local s = logging.prepareLogMsg(logPatterns[level], logging.date(timestampPattern), level, message)
local socket, err = socket.connect(hostname, port)
if not socket then
return nil, err
end
local cond, err = socket:send(s)
if not cond then
return nil, err
end
socket:close()
return true
end, startLevel)
end
logging.socket = M
return M

75
src/logging/sql.lua Normal file
View File

@ -0,0 +1,75 @@
-------------------------------------------------------------------------------
-- Saves the logging information in a table using luasql
--
-- @author Thiago Costa Ponte (thiago@ideais.com.br)
--
-- @copyright 2004-2010 Kepler Project, 2011-2013 Neopallium, 2020-2023 Thijs Schreijer
--
-------------------------------------------------------------------------------
local logging = require"logging"
local M = setmetatable({}, {
__call = function(self, ...)
-- calling on the module instantiates a new logger
return self.new(...)
end,
})
function M.new(params)
params = params or {}
params.tablename = params.tablename or "LogTable"
params.logdatefield = params.logdatefield or "LogDate"
params.loglevelfield = params.loglevelfield or "LogLevel"
params.logmessagefield = params.logmessagefield or "LogMessage"
local startLevel = params.logLevel or logging.defaultLevel()
if params.connectionfactory == nil or type(params.connectionfactory) ~= "function" then
return nil, "No specified connection factory function"
end
local con, err
if params.keepalive then
con, err = params.connectionfactory()
end
return logging.new( function(self, level, message)
if (not params.keepalive) or (con == nil) then
con, err = params.connectionfactory()
if not con then
return nil, err
end
end
local logDate = os.date("%Y-%m-%d %H:%M:%S")
local insert = string.format("INSERT INTO %s (%s, %s, %s) VALUES ('%s', '%s', '%s')",
params.tablename, params.logdatefield, params.loglevelfield,
params.logmessagefield, logDate, level, string.gsub(message, "'", "''"))
local ret = pcall(con.execute, con, insert)
if not ret then
con, err = params.connectionfactory()
if not con then
return nil, err
end
ret, err = con:execute(insert)
if not ret then
return nil, err
end
end
if not params.keepalive then
con:close()
end
return true
end, startLevel)
end
logging.sql = M
return M

249
tests/generic.lua Normal file
View File

@ -0,0 +1,249 @@
local logging = require "logging"
local call_count
local last_msg
local msgs
function logging.test(params)
local logPatterns = logging.buildLogPatterns(params.logPatterns, params.logPattern)
local timestampPattern = params.timestampPattern or logging.defaultTimestampPattern()
return logging.new( function(self, level, message)
last_msg = logging.prepareLogMsg(logPatterns[level], os.date(timestampPattern), level, message)
msgs = msgs or {}
table.insert(msgs, last_msg)
--print("----->",last_msg)
call_count = call_count + 1
return true
end)
end
local function reset()
call_count = 0
last_msg = nil
msgs = nil
end
local tests = {}
tests.constants_for_lualogging = function()
local DEFAULT_LEVELS = { "DEBUG", "INFO", "WARN", "ERROR", "FATAL", "OFF" }
for _, level in ipairs(DEFAULT_LEVELS) do
assert(logging[level], "constant logging."..level.." is not defined")
end
end
tests.deprecated_parameter_handling = function()
local list = { "param1", "next_one", "hello_world" }
local params = logging.getDeprecatedParams(list, {
param1 = 1,
next_one = nil,
hello_world = 3,
})
assert(params.param1 == 1)
assert(params.next_one == nil)
assert(params.hello_world == 3)
params = logging.getDeprecatedParams(list, 1, nil, 3)
assert(params.param1 == 1)
assert(params.next_one == nil)
assert(params.hello_world == 3)
end
tests.buildLogPatterns = function()
local check_patterns = function(t)
assert(type(t) == "table")
assert(t.DEBUG and t.INFO and t.WARN and t.ERROR and t.FATAL)
local i = 0
for k,v in pairs(t) do i = i + 1 end
assert(i == 5)
end
local t = logging.buildLogPatterns()
check_patterns(t)
local t = logging.buildLogPatterns(nil, "hello")
check_patterns(t)
assert(t.DEBUG == "hello")
assert(t.FATAL == "hello")
local t = logging.buildLogPatterns({}, "hello")
check_patterns(t)
assert(t.DEBUG == "hello")
assert(t.FATAL == "hello")
local t = logging.buildLogPatterns({
DEBUG = "bye"
}, "hello")
check_patterns(t)
assert(t.DEBUG == "bye")
assert(t.INFO == "hello")
assert(t.FATAL == "hello")
end
tests.log_levels = function()
local logger = logging.test { logPattern = "%message", timestampPattern = nil }
logger:setLevel(logger.DEBUG)
-- debug gets logged
logger:debug("message 1")
assert(last_msg == "message 1", "got: " .. tostring(last_msg))
assert(call_count == 1, "Got: " .. tostring(call_count))
-- fatal also gets logged at 'debug' setting
logger:fatal("message 2")
assert(last_msg == "message 2", "got: " .. tostring(last_msg))
assert(call_count == 2, "Got: " .. tostring(call_count))
logger:setLevel(logger.FATAL)
-- debug gets logged
logger:debug("message 3") -- should not change the last message
assert(last_msg == "message 2", "got: " .. tostring(last_msg))
assert(call_count == 2, "Got: " .. tostring(call_count))
-- fatal also gets logged at 'debug' setting
logger:fatal("message 4") -- should be logged as 3rd message
assert(last_msg == "message 4", "got: " .. tostring(last_msg))
assert(call_count == 3, "Got: " .. tostring(call_count))
logger:setLevel(logger.OFF)
-- debug gets logged
logger:debug("message 5") -- should not change the last message
assert(last_msg == "message 4", "got: " .. tostring(last_msg))
assert(call_count == 3, "Got: " .. tostring(call_count))
-- fatal also gets logged at 'debug' setting
logger:fatal("message 6") -- should not change the last message
assert(last_msg == "message 4", "got: " .. tostring(last_msg))
assert(call_count == 3, "Got: " .. tostring(call_count))
-- should never log "OFF", even if its set
logger:fatal("message 7") -- should not change the last message
assert(last_msg == "message 4", "got: " .. tostring(last_msg))
assert(call_count == 3, "Got: " .. tostring(call_count))
end
tests.logPatterns = function()
local logger = logging.test { logPattern = "%date", timestampPattern = nil }
logger:debug("hello")
assert(last_msg ~= "%date", "expected '%date' placeholder to be replaced, got: " .. tostring(last_msg))
assert(last_msg ~= "", "expected '%date' placeholder to be replaced by a value, got an empty string")
local logger = logging.test { logPattern = "%level", timestampPattern = nil }
logger:fatal("hello")
assert(last_msg ~= "%level", "expected '%level' placeholder to be replaced, got: " .. tostring(last_msg))
assert(last_msg == "FATAL", "expected '%level' placeholder to be replaced by 'FATAL', got: " .. tostring(last_msg))
local logger = logging.test { logPattern = "%message", timestampPattern = nil }
logger:debug("hello")
assert(last_msg ~= "%message", "expected '%message' placeholder to be replaced, got: " .. tostring(last_msg))
assert(last_msg == "hello", "expected '%message' placeholder to be replaced by 'hello', got: " .. tostring(last_msg))
local logger = logging.test { logPattern = "%source", timestampPattern = nil }
local function test_func()
logger:debug("hello")
end
test_func()
assert(last_msg ~= "%source", "expected '%source' placeholder to be replaced, got: " .. tostring(last_msg))
if debug then
assert(last_msg:find("'test_func'", 1, true), "expected function name in output, got: " .. tostring(last_msg))
assert(last_msg:find(":138 ", 1, true), "expected line number in output, got: " .. tostring(last_msg)) -- update hardcoded linenumber when this fails!
assert(last_msg:find("generic.lua:", 1, true), "expected filename in output, got: " .. tostring(last_msg))
else
-- debug library disabled
assert(last_msg:find("'unknown function'", 1, true), "expected 'unknwon function' in output, got: " .. tostring(last_msg))
assert(last_msg:find(":-1 ", 1, true), "expected line number (-1) in output, got: " .. tostring(last_msg)) -- update hardcoded linenumber when this fails!
assert(last_msg:find("?:", 1, true), "expected filename ('?') in output, got: " .. tostring(last_msg))
end
-- mutiple separate patterns
local logger = logging.test {
logPattern = "%message",
logPatterns = {
[logging.DEBUG] = "hello %message"
},
timestampPattern = nil,
}
logger:debug("world")
assert(last_msg == "hello world", "expected 'hello world', got: " .. tostring(last_msg))
logger:error("world")
assert(last_msg == "world", "expected 'world', got: " .. tostring(last_msg))
end
tests.table_serialization = function()
local logger = logging.test { logPattern = "%message", timestampPattern = nil }
logger:debug({1,2,3,4,5,6,7,8,9,10})
assert(last_msg == "{1, 10, 2, 3, 4, 5, 6, 7, 8, 9}", "got: " .. tostring(last_msg))
logger:debug({abc = "cde", "hello", "world", xyz = true, 1, 2, 3})
assert(last_msg == '{"hello", "world", 1, 2, 3, abc = "cde", xyz = true}', "got: " .. tostring(last_msg))
end
tests.print_function = function()
local logger = logging.test { logPattern = "%level %message" }
local print = logger:getPrint(logger.DEBUG)
print("hey", "there", "dude")
assert(msgs[1] == "DEBUG hey there dude")
print()
assert(msgs[2] == "DEBUG ")
print("hello\nthere")
assert(msgs[3] == "DEBUG hello")
assert(msgs[4] == "DEBUG there")
print({}, true, nil, 0)
assert(msgs[5]:find("table"))
assert(msgs[5]:find(" true nil 0$"))
end
tests.format_error_stacktrace = function()
local count = 0
local logger = logging.test { logPattern = "%level %message" }
logger:debug("%s-%s", 'abc', '007')
assert(last_msg == 'DEBUG abc-007')
logger:debug("%s=%s", nil)
assert(last_msg:find("bad argument #%d to '(.-)'"), "msg:'"..last_msg.."'")
if debug then
assert(last_msg:find("in main chunk"), "msg:'"..last_msg.."'")
assert(last_msg:find("in %w+ 'func'"), "msg:'"..last_msg.."'")
local _, levels = last_msg:gsub("(|)", function() count = count + 1 end)
assert(levels == 3, "got : " .. tostring(levels))
end
end
tests.defaultLogger = function()
-- returns a logger
assert(logging.defaultLogger(), "expected a logger object to be returned)")
local logger = logging.test {}
-- setting a default one
assert(logging.defaultLogger(logger), "expected a logger object to be returned)")
assert(logger == logging.defaultLogger(), "expected my previously set logger to be returned)")
end
tests.defaultLevel = function()
-- default level is 'debug'
local old_level = logging.defaultLevel()
assert(old_level == logging.DEBUG, "expected default to be 'debug'")
-- setting level
assert(logging.defaultLevel(logging.FATAL) == logging.FATAL, "expected updated log-level")
-- new logger uses new default level
local logger = logging.test {}
logger:error("less than 'fatal', should not be logged")
assert(call_count == 0)
logger:fatal("should be logged")
assert(call_count == 1)
-- errors on unknown level
assert(not pcall(logging.defaultLevel, "unknown level"), "expected an error to be thrown")
-- restore old default
logging.defaultLevel(old_level)
end
for name, func in pairs(tests) do
reset()
print("generic test: " .. name)
func()
end
print("[v] all generic tests succesful")

8
tests/run_tests.sh Executable file
View File

@ -0,0 +1,8 @@
#!/bin/sh
set -e
LUA_PATH="../src/?.lua;$LUA_PATH" lua test.lua
rm -f test.db test.log*

18
tests/test.lua Normal file
View File

@ -0,0 +1,18 @@
local test = {
"generic.lua",
"testEnv.lua",
"testConsole.lua",
"testFile.lua",
"testMail.lua",
"testSocket.lua",
"testSQL.lua",
"testRollingFile.lua",
}
print ("Start of Logging tests")
for _, filename in ipairs(test) do
dofile(filename)
end
print ("End of Logging tests")

12
tests/testConsole.lua Normal file
View File

@ -0,0 +1,12 @@
local log_console = require"logging.console"
local logger = log_console()
logger:info("logging.console test")
logger:debug("debugging...")
logger:error("error!")
logger:debug("string with %4")
logger:setLevel("INFO") -- test log level change warning.
print("Console Logging OK")

145
tests/testEnv.lua Normal file
View File

@ -0,0 +1,145 @@
local logging
local logenv
local env
local old_getenv = os.getenv
function os.getenv(key) -- luacheck: ignore
assert(type(key) == "string", "expected env variable name to be a string")
return env[key]
end
local function reset()
env = {}
package.loaded["logging"] = nil
package.loaded["logging.envconfig"] = nil
logging = require "logging"
logenv = require "logging.envconfig"
end
local tests = {}
tests.returns_defaults = function()
local name, opts = logenv.get_default_settings()
-- print("title: ", require("pl.pretty").write(opts))
assert(name == "console", "expected 'console' to be the default logger")
assert(type(opts) == "table", "expected 'opts' to be a table")
assert(opts.destination == "stderr", "expected 'destination' to be 'stderr'")
assert(opts.logLevel == "DEBUG", "expected 'logLevel' to be 'DEBUG'")
assert(opts.logPatterns.DEBUG == "%date %level %message\n")
assert(opts.logPatterns.INFO == "%date %level %message\n")
assert(opts.logPatterns.WARN == "%date %level %message\n")
assert(opts.logPatterns.ERROR == "%date %level %message\n")
assert(opts.logPatterns.FATAL == "%date %level %message\n")
end
tests.returns_defaults_if_prefix_not_found = function()
assert(logenv.set_default_settings("not_a_real_prefix"))
local name, opts = logenv.get_default_settings()
-- print("title: ", require("pl.pretty").write(opts))
assert(name == "console", "expected 'console' to be the default logger")
assert(type(opts) == "table", "expected 'opts' to be a table")
assert(opts.destination == "stderr", "expected 'destination' to be 'stderr'")
assert(opts.logLevel == "DEBUG", "expected 'logLevel' to be 'DEBUG'")
assert(opts.logPatterns.DEBUG == "%date %level %message\n")
assert(opts.logPatterns.INFO == "%date %level %message\n")
assert(opts.logPatterns.WARN == "%date %level %message\n")
assert(opts.logPatterns.ERROR == "%date %level %message\n")
assert(opts.logPatterns.FATAL == "%date %level %message\n")
end
tests.fails_if_default_already_set = function()
assert(logenv.set_default_settings("prefix"))
local ok, err = logenv.set_default_settings("prefix")
assert(err == "already set a default")
assert(ok == nil)
end
tests.prefix_defaults_to_LL = function()
env.LL_LOGLEVEL = assert(logging.ERROR)
local _, opts = logenv.get_default_settings()
-- print("title: ", require("pl.pretty").write(opts))
assert(opts.logLevel == "ERROR", "expected 'logLevel' to be 'ERROR'")
end
tests.loads_patterns_object = function()
env.LL_LOGPATTERN = "not used"
env.LL_LOGPATTERNS_DEBUG = "debug"
env.LL_LOGPATTERNS_INFO = "info"
env.LL_LOGPATTERNS_WARN = "warn"
env.LL_LOGPATTERNS_ERROR = "error"
env.LL_LOGPATTERNS_FATAL = "fatal"
local _, opts = logenv.get_default_settings()
-- print("title: ", require("pl.pretty").write(opts))
assert(opts.logPattern == "not used")
assert(opts.logPatterns.DEBUG == "debug")
assert(opts.logPatterns.INFO == "info")
assert(opts.logPatterns.WARN == "warn")
assert(opts.logPatterns.ERROR == "error")
assert(opts.logPatterns.FATAL == "fatal")
end
tests.fills_patterns_object_from_logpattern = function()
env.LL_LOGPATTERN = "this one"
local _, opts = logenv.get_default_settings()
-- print("title: ", require("pl.pretty").write(opts))
assert(opts.logPattern == "this one")
assert(opts.logPatterns.DEBUG == "this one")
assert(opts.logPatterns.INFO == "this one")
assert(opts.logPatterns.WARN == "this one")
assert(opts.logPatterns.ERROR == "this one")
assert(opts.logPatterns.FATAL == "this one")
end
tests.bad_loglevel_not_accepted = function()
env.LL_LOGLEVEL = "something bad"
local ok, loggername, opts = pcall(logenv.get_default_settings)
-- print("title: ", require("pl.pretty").write(opts))
assert(not ok)
assert(opts == nil)
assert(type(loggername) == "string")
end
tests.does_dynamic_lookups_of_vars = function()
local _, opts = logenv.get_default_settings() -- "LL" is now prefix
-- print("title: ", require("pl.pretty").write(opts))
env.LL_SOME_VALUE = "hello"
assert(opts.some_value == "hello")
end
tests.converts_booleans = function()
local _, opts = logenv.get_default_settings() -- "LL" is now prefix
-- print("title: ", require("pl.pretty").write(opts))
env.LL_ONE = "TRUE"
env.LL_TWO = "true"
env.LL_THREE = "false"
env.LL_FOUR = "FALSE"
assert(opts.one == true)
assert(opts.two == true)
assert(opts.three == false)
assert(opts.four == false)
end
tests.converts_numbers = function()
local _, opts = logenv.get_default_settings() -- "LL" is now prefix
env.LL_ONE = "1"
env.LL_TWO = "-2"
env.LL_THREE = ".2"
env.LL_FOUR = "1.2"
assert(opts.one == 1, "got: "..tostring(opts.one).." ("..type(opts.one)..")")
assert(opts.two == -2, "got: "..tostring(opts.two).." ("..type(opts.two)..")")
assert(opts.three == .2, "got: "..tostring(opts.three).." ("..type(opts.three)..")")
assert(opts.four == 1.2, "got: "..tostring(opts.four).." ("..type(opts.four)..")")
end
for name, func in pairs(tests) do
reset()
print("env-config test: " .. name)
func()
end
print("[v] all env-config tests succesful")
os.getenv = old_getenv -- luacheck: ignore

81
tests/testFile.lua Normal file
View File

@ -0,0 +1,81 @@
local GLOBAL_OS_DATE = os.date
local GLOBAL_IO_OPEN = io.open
local buffer_mode do
local dir_separator = _G.package.config:sub(1,1)
local is_windows = dir_separator == '\\'
if is_windows then
-- Windows does not support "line" buffered mode, see
-- https://github.com/lunarmodules/lualogging/pull/9
buffer_mode = "no"
else
buffer_mode = "line"
end
end
local mock = {
date = nil,
handle = {}
}
io.open = function (file, mode) --luacheck: ignore
if (not string.find(file, "^__TEST*")) then
return GLOBAL_IO_OPEN(file, mode)
end
mock.handle[file] = {}
mock.handle[file].lines = {}
mock.handle[file].mode = mode
return {
setvbuf = function (_, s)
mock.handle[file].setvbuf = s
end,
write = function (_, s)
table.insert(mock.handle[file].lines, s)
end,
}
end
os.date = function (...) --luacheck: ignore
return mock.date
end
local log_file = require "logging.file"
mock.date = "2008-01-01"
local logger = log_file("__TEST%s.log", "%Y-%m-%d")
assert(mock.handle["__TEST"..mock.date..".log"] == nil)
logger:info("logging.file test")
assert(mock.handle["__TEST"..mock.date..".log"].mode == "a")
assert(#mock.handle["__TEST"..mock.date..".log"].lines == 1)
assert(mock.handle["__TEST"..mock.date..".log"].setvbuf == buffer_mode)
assert(mock.handle["__TEST"..mock.date..".log"].lines[1] == "2008-01-01 INFO logging.file test\n")
mock.date = "2008-01-02"
logger:debug("debugging...")
logger:error("error!")
assert(mock.handle["__TEST"..mock.date..".log"].mode == "a")
assert(#mock.handle["__TEST"..mock.date..".log"].lines == 2)
assert(mock.handle["__TEST"..mock.date..".log"].setvbuf == buffer_mode)
assert(mock.handle["__TEST"..mock.date..".log"].lines[1] == "2008-01-02 DEBUG debugging...\n")
assert(mock.handle["__TEST"..mock.date..".log"].lines[2] == "2008-01-02 ERROR error!\n")
mock.date = "2008-01-03"
logger:info({id = "1"})
assert(mock.handle["__TEST"..mock.date..".log"].mode == "a")
assert(#mock.handle["__TEST"..mock.date..".log"].lines == 1)
assert(mock.handle["__TEST"..mock.date..".log"].setvbuf == buffer_mode)
assert(mock.handle["__TEST"..mock.date..".log"].lines[1] == '2008-01-03 INFO {id = "1"}\n')
os.date = GLOBAL_OS_DATE --luacheck: ignore
io.open = GLOBAL_IO_OPEN --luacheck: ignore
print("File Logging OK")

16
tests/testMail.lua Normal file
View File

@ -0,0 +1,16 @@
local log_email = require"logging.email"
local logger = log_email {
rcpt = "mail@host.com",
from = "mail@host.com",
{
subject = "[%level] logging.email test",
}, -- headers
}
logger:info("logging.email test")
logger:debug("debugging...")
logger:error("error!")
print("Mail Logging OK")

30
tests/testRollingFile.lua Normal file
View File

@ -0,0 +1,30 @@
local log_file = require "logging.rolling_file"
local max_size = 1024 * 10 --10kb
local max_index = 5
local total_log_size = max_size * max_index --more than needed because of the log pattern
local log_filename = "test.log"
local logger = log_file(log_filename, max_size, max_index)
-- it will generate the log + max_index backup files
local size = 0
while size < total_log_size do
local data = string.format("Test actual size[%d]", size)
logger:debug(data)
size = size + #data
end
-- lets test if all files where created
for i = 1, max_index do
local file = assert(io.open(log_filename.."."..tostring(i), "r"))
-- since there is an exact precision on the rolling
-- (it can be a little less or a little more than the max_size)
-- lets just test if the file is empty.
assert(file:seek("end", 0) > 0)
file:close()
end
print("RollingFile Logging OK")

25
tests/testSQL.lua Normal file
View File

@ -0,0 +1,25 @@
local log_sql = require "logging.sql"
local _, luasql = pcall(require, "luasql")
local has_module = pcall(require, "luasql.sqlite3")
if not has_module then
print("SQLite 3 Logging SKIP (missing luasql.sqlite3)")
else
if not luasql or not luasql.sqlite3 then
print("Missing LuaSQL SQLite 3 driver!")
else
local env = luasql.sqlite3()
local logger = log_sql{
connectionfactory = function()
return assert(env:connect("test.db"))
end,
keepalive = true,
}
logger:info("logging.sql test")
logger:debug("debugging...")
logger:error("error!")
print("SQLite 3 Logging OK")
end
end

10
tests/testSocket.lua Normal file
View File

@ -0,0 +1,10 @@
local log_sock = require"logging.socket"
local logger = log_sock("localhost", "5000")
logger:info("logging.socket test")
logger:debug("debugging...")
logger:error("error!")
print("Socket Logging OK")