aboutsummaryrefslogtreecommitdiff
path: root/src/lua/log.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lua/log.c')
-rw-r--r--src/lua/log.c255
1 files changed, 205 insertions, 50 deletions
diff --git a/src/lua/log.c b/src/lua/log.c
index 4bf4e16..b49c003 100644
--- a/src/lua/log.c
+++ b/src/lua/log.c
@@ -1,37 +1,26 @@
#include <assert.h>
#include <string.h>
-#include "api.h"
+#include "log.h"
#include "../any_log.h"
-static lua_CFunction lua_log_stringformat = NULL;
-
-static int lua_log_format(lua_State *state, any_log_level_t level) {
-
- // Use string.format to print safely
- //
- int n_args = lua_gettop(state);
- lua_pushcfunction(state, lua_log_stringformat);
+static lua_CFunction lua_log_getinfo = NULL;
- for (int i = 1; i <= n_args; ++i)
- lua_pushvalue(state, i);
+static lua_CFunction lua_log_stringformat = NULL;
- lua_call(state, n_args, 1);
- const char *message = lua_tostring(state, -1);
+static lua_CFunction lua_log_tostring = NULL;
- lua_getglobal(state, "debug");
- lua_getfield(state, -1, "getinfo");
+static inline void lua_log_debuginfo(lua_State *state, const char **source, const char **func,
+ char buffer[], size_t size)
+{
+ lua_pushcfunction(state, lua_log_getinfo);
- // Ignore the first frame (lua_log_format itself)
+ // Ignore the first frame (the c function itself)
lua_pushnumber(state, 2);
// S: source, l: currentline, n: name
lua_pushstring(state, "Sln");
- const char *source = "?", *func = "?";
- char buffer[4096];
-
if (lua_pcall(state, 2, 1, 0) == LUA_OK) {
-
lua_getfield(state, -1, "short_src");
const char *shortsrc = lua_tostring(state, -1);
@@ -40,73 +29,239 @@ static int lua_log_format(lua_State *state, any_log_level_t level) {
if (!strncmp(shortsrc, "[string ", 8)) {
int length = strlen(shortsrc);
- snprintf(buffer, sizeof(buffer), "lua %.*s", length - 9, shortsrc + 8);
+ snprintf(buffer, size, "lua %.*s", length - 9, shortsrc + 8);
} else {
- snprintf(buffer, sizeof(buffer), "lua %s:%d", shortsrc, line);
+ snprintf(buffer, size, "lua %s:%d", shortsrc, line);
}
lua_getfield(state, -3, "name");
const char *name = lua_tostring(state, -1);
- source = buffer;
- func = name ? name : "...";
+ *source = buffer;
+ *func = name ? name : "...";
+ } else {
+ log_trace("Failed to retrieve Lua debug information");
}
lua_pop(state, 3);
+}
+
+// Normal logging with any_log_format
+//
+static int lua_log_wrapper(lua_State *state, int skip_args, any_log_level_t level)
+{
+ // Use string.format to print safely
+ //
+ int n_args = lua_gettop(state);
+ lua_pushcfunction(state, lua_log_stringformat);
+
+ for (int i = 1; i <= n_args; ++i)
+ lua_pushvalue(state, skip_args + i);
+
+ lua_call(state, n_args, 1);
+ const char *message = lua_tostring(state, -1);
+
+ const char *source = "?", *func = "?";
+ char buffer[4096];
+ lua_log_debuginfo(state, &source, &func, buffer, sizeof(buffer));
+
any_log_format(level, source, func, "%s", message);
return 0;
}
-static int lua_log_log(lua_State *state) {
- const char *level = luaL_checkstring(state, 1);
- lua_pop(state, 1);
- any_log_level_t l = any_log_level_from_string(level);
- assert(l > ANY_LOG_ALL);
- return lua_log_format(state, l);
+static int lua_log_format(lua_State *state)
+{
+ const char *param = luaL_checkstring(state, 1);
+ any_log_level_t level = any_log_level_from_string(param);
+
+ if (level == ANY_LOG_ALL)
+ luaL_error(state, "Invalid log level, got \"%s\"", param);
+
+ return lua_log_wrapper(state, 1, level);
}
-static int lua_log_panic(lua_State *state) {
- return lua_log_format(state, ANY_LOG_PANIC);
+static int lua_log_error(lua_State *state)
+{
+ return lua_log_wrapper(state, 0, ANY_LOG_ERROR);
}
-static int lua_log_error(lua_State *state) {
- return lua_log_format(state, ANY_LOG_ERROR);
+static int lua_log_warn(lua_State *state)
+{
+ return lua_log_wrapper(state, 0, ANY_LOG_WARN);
}
-static int lua_log_warn(lua_State *state) {
- return lua_log_format(state, ANY_LOG_WARN);
+static int lua_log_info(lua_State *state)
+{
+ return lua_log_wrapper(state, 0, ANY_LOG_INFO);
}
-static int lua_log_info(lua_State *state) {
- return lua_log_format(state, ANY_LOG_INFO);
+static int lua_log_debug(lua_State *state)
+{
+ return lua_log_wrapper(state, 0, ANY_LOG_DEBUG);
}
-static int lua_log_debug(lua_State *state) {
- return lua_log_format(state, ANY_LOG_DEBUG);
+static int lua_log_trace(lua_State *state)
+{
+ return lua_log_wrapper(state, 0, ANY_LOG_TRACE);
}
-static int lua_log_trace(lua_State *state) {
- return lua_log_format(state, ANY_LOG_TRACE);
+// Structured logging with any_log_value
+//
+static int lua_log_wrapperv(lua_State *state, any_log_level_t level)
+{
+ const char *message = lua_tostring(state, 1);
+ lua_remove(state, 1);
+ luaL_checktype(state, 1, LUA_TTABLE);
+
+ const char *source = "?", *func = "?";
+ char buffer[4096];
+ lua_log_debuginfo(state, &source, &func, buffer, sizeof(buffer));
+
+ any_log_value(level, source, func, message,
+ "g:value", ANY_LOG_FORMATTER(lua_print_value), state,
+ NULL);
+ return 0;
+}
+
+static int lua_log_formatv(lua_State *state)
+{
+ const char *param = luaL_checkstring(state, 1);
+ any_log_level_t level = any_log_level_from_string(param);
+
+ if (level == ANY_LOG_ALL)
+ luaL_error(state, "Invalid log level, got \"%s\"", param);
+
+ lua_remove(state, 1);
+ return lua_log_wrapperv(state, level);
+}
+
+static int lua_log_errorv(lua_State *state)
+{
+ return lua_log_wrapperv(state, ANY_LOG_ERROR);
+}
+
+static int lua_log_warnv(lua_State *state)
+{
+ return lua_log_wrapperv(state, ANY_LOG_WARN);
+}
+
+static int lua_log_infov(lua_State *state)
+{
+ return lua_log_wrapperv(state, ANY_LOG_INFO);
+}
+
+static int lua_log_debugv(lua_State *state)
+{
+ return lua_log_wrapperv(state, ANY_LOG_DEBUG);
+}
+
+static int lua_log_tracev(lua_State *state)
+{
+ return lua_log_wrapperv(state, ANY_LOG_TRACE);
+}
+
+void lua_print_value(FILE *stream, lua_State *state)
+{
+ lua_print_value_at(stream, state, 1);
+}
+
+void lua_print_value_at(FILE *stream, lua_State *state, int index)
+{
+ switch (lua_type(state, index)) {
+ case LUA_TNIL:
+ fprintf(stream, "nil");
+ break;
+
+ case LUA_TNUMBER:
+ fprintf(stream, "%lf", lua_tonumber(state, index));
+ break;
+
+ case LUA_TBOOLEAN:
+ fprintf(stream, lua_toboolean(state, index) ? "true" : "false");
+ break;
+
+ case LUA_TSTRING:
+ fprintf(stream, "\"%s\"", lua_tostring(state, index));
+ break;
+
+ case LUA_TTABLE:
+ fputc('{', stream);
+
+ bool first = true;
+ lua_pushnil(state);
+ while (lua_next(state, index) != 0) {
+ if (!first) {
+ fputc(',', stream);
+ fputc(' ', stream);
+ }
+ first = false;
+
+ if (lua_isnumber(state, -2)) {
+ fprintf(stream, "[%lld] = ", lua_tointeger(state, -2));
+ } else {
+ fprintf(stream, "%s = ", lua_tostring(state, -2));
+ }
+
+ lua_print_value_at(stream, state, lua_gettop(state));
+ lua_pop(state, 1);
+ }
+
+ fputc('}', stream);
+ break;
+
+
+ case LUA_TFUNCTION:
+ fprintf(stream, "function (%p)", lua_topointer(state, index));
+ break;
+
+ case LUA_TTHREAD:
+ fprintf(stream, "thread (%p)", lua_tothread(state, index));
+ break;
+
+ default:
+ lua_pushcfunction(state, lua_log_tostring);
+ lua_pushvalue(state, index);
+ lua_call(state, 1, 1);
+ fprintf(stream, "%s", lua_tostring(state, -1));
+ lua_pop(state, 1);
+ break;
+ }
}
int lua_log_library(lua_State *state)
{
static const luaL_Reg library[] = {
- { "log", lua_log_log },
- { "panic", lua_log_panic },
- { "error", lua_log_error },
- { "warn", lua_log_warn },
- { "info", lua_log_info },
- { "debug", lua_log_debug },
- { "trace", lua_log_trace },
+ // TODO: Is it wise to allow panic from Lua?
+ //{ "panic", lua_log_panic },
+
+ { "format", lua_log_format },
+ { "error", lua_log_error },
+ { "warn", lua_log_warn },
+ { "info", lua_log_info },
+ { "debug", lua_log_debug },
+ { "trace", lua_log_trace },
+
+ { "value_format", lua_log_formatv },
+ { "value_error", lua_log_errorv },
+ { "value_warn", lua_log_warnv },
+ { "value_info", lua_log_infov },
+ { "value_debug", lua_log_debugv },
+ { "value_trace", lua_log_tracev },
{ NULL, NULL },
};
+ lua_getglobal(state, "debug");
+ lua_getfield(state, -1, "getinfo");
+ lua_log_getinfo = lua_tocfunction(state, -1);
+
lua_getglobal(state, "string");
lua_getfield(state, -1, "format");
lua_log_stringformat = lua_tocfunction(state, -1);
- lua_pop(state, 2);
+ lua_getglobal(state, "tostring");
+ lua_log_tostring = lua_tocfunction(state, -1);
+
+ lua_pop(state, 5);
luaL_newlib(state, library);
return 1;
}