aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/log.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/log.c')
-rw-r--r--src/nvim/log.c174
1 files changed, 95 insertions, 79 deletions
diff --git a/src/nvim/log.c b/src/nvim/log.c
index 5539e3d6c5..57c7c4758b 100644
--- a/src/nvim/log.c
+++ b/src/nvim/log.c
@@ -16,16 +16,19 @@
#include <uv.h>
#include "auto/config.h"
+#include "nvim/eval.h"
#include "nvim/log.h"
+#include "nvim/main.h"
+#include "nvim/message.h"
#include "nvim/os/os.h"
#include "nvim/os/time.h"
+#include "nvim/path.h"
#include "nvim/types.h"
-#define LOG_FILE_ENV "NVIM_LOG_FILE"
-
/// Cached location of the expanded log file path decided by log_path_init().
static char log_file_path[MAXPATHL + 1] = { 0 };
+static bool did_log_init = false;
static uv_mutex_t mutex;
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -49,36 +52,29 @@ static bool log_try_create(char *fname)
return true;
}
-/// Initializes path to log file. Sets $NVIM_LOG_FILE if empty.
-///
-/// Tries $NVIM_LOG_FILE, or falls back to $XDG_CACHE_HOME/nvim/log. Path to log
-/// file is cached, so only the first call has effect, unless first call was not
-/// successful. Failed initialization indicates either a bug in expand_env()
-/// or both $NVIM_LOG_FILE and $HOME environment variables are undefined.
+/// Initializes the log file path and sets $NVIM_LOG_FILE if empty.
///
-/// @return true if path was initialized, false otherwise.
-static bool log_path_init(void)
+/// Tries $NVIM_LOG_FILE, or falls back to $XDG_STATE_HOME/nvim/log. Failed
+/// initialization indicates either a bug in expand_env() or both $NVIM_LOG_FILE
+/// and $HOME environment variables are undefined.
+static void log_path_init(void)
{
- if (log_file_path[0]) {
- return true;
- }
size_t size = sizeof(log_file_path);
- expand_env((char_u *)"$" LOG_FILE_ENV, (char_u *)log_file_path,
- (int)size - 1);
- if (strequal("$" LOG_FILE_ENV, log_file_path)
+ expand_env((char_u *)"$" ENV_LOGFILE, (char_u *)log_file_path, (int)size - 1);
+ if (strequal("$" ENV_LOGFILE, log_file_path)
|| log_file_path[0] == '\0'
|| os_isdir((char_u *)log_file_path)
|| !log_try_create(log_file_path)) {
- // Make kXDGCacheHome if it does not exist.
- char *cachehome = get_xdg_home(kXDGCacheHome);
+ // Make $XDG_STATE_HOME if it does not exist.
+ char *loghome = get_xdg_home(kXDGStateHome);
char *failed_dir = NULL;
bool log_dir_failure = false;
- if (!os_isdir((char_u *)cachehome)) {
- log_dir_failure = (os_mkdir_recurse(cachehome, 0700, &failed_dir) != 0);
+ if (!os_isdir((char_u *)loghome)) {
+ log_dir_failure = (os_mkdir_recurse(loghome, 0700, &failed_dir) != 0);
}
- XFREE_CLEAR(cachehome);
+ XFREE_CLEAR(loghome);
// Invalid $NVIM_LOG_FILE or failed to expand; fall back to default.
- char *defaultpath = stdpaths_user_cache_subpath("log");
+ char *defaultpath = stdpaths_user_state_subpath("log", 0, true);
size_t len = xstrlcpy(log_file_path, defaultpath, size);
xfree(defaultpath);
// Fall back to .nvimlog
@@ -88,22 +84,23 @@ static bool log_path_init(void)
// Fall back to stderr
if (len >= size || !log_try_create(log_file_path)) {
log_file_path[0] = '\0';
- return false;
+ return;
}
- os_setenv(LOG_FILE_ENV, log_file_path, true);
+ os_setenv(ENV_LOGFILE, log_file_path, true);
if (log_dir_failure) {
WLOG("Failed to create directory %s for writing logs: %s",
failed_dir, os_strerror(log_dir_failure));
}
XFREE_CLEAR(failed_dir);
}
- return true;
}
void log_init(void)
{
- uv_mutex_init(&mutex);
+ uv_mutex_init_recursive(&mutex);
+ // AFTER init_homedir ("~", XDG) and set_init_1 (env vars). 22b52dd462e5 #11501
log_path_init();
+ did_log_init = true;
}
void log_lock(void)
@@ -124,10 +121,20 @@ void log_unlock(void)
/// @param line_num Source line number, or -1
/// @param eol Append linefeed "\n"
/// @param fmt printf-style format string
+///
+/// @return true if log was emitted normally, false if failed or recursive
bool logmsg(int log_level, const char *context, const char *func_name, int line_num, bool eol,
const char *fmt, ...)
FUNC_ATTR_UNUSED FUNC_ATTR_PRINTF(6, 7)
{
+ static bool recursive = false;
+ static bool did_msg = false; // Showed recursion message?
+ if (!did_log_init) {
+ g_stats.log_skip++;
+ // set_init_1 may try logging before we are ready. 6f27f5ef91b3 #10183
+ return false;
+ }
+
if (log_level < MIN_LOG_LEVEL) {
return false;
}
@@ -139,13 +146,19 @@ bool logmsg(int log_level, const char *context, const char *func_name, int line_
#endif
log_lock();
+ if (recursive) {
+ if (!did_msg) {
+ did_msg = true;
+ msg_schedule_semsg("E5430: %s:%d: recursive log!", func_name ? func_name : context, line_num);
+ }
+ g_stats.log_skip++;
+ log_unlock();
+ return false;
+ }
+ recursive = true;
bool ret = false;
FILE *log_file = open_log_file();
- if (log_file == NULL) {
- goto end;
- }
-
va_list args;
va_start(args, fmt);
ret = v_do_log_to_file(log_file, log_level, context, func_name, line_num,
@@ -155,7 +168,8 @@ bool logmsg(int log_level, const char *context, const char *func_name, int line_
if (log_file != stderr && log_file != stdout) {
fclose(log_file);
}
-end:
+
+ recursive = false;
log_unlock();
return ret;
}
@@ -166,51 +180,36 @@ void log_uv_handles(void *loop)
log_lock();
FILE *log_file = open_log_file();
- if (log_file == NULL) {
- goto end;
- }
-
uv_print_all_handles(l, log_file);
if (log_file != stderr && log_file != stdout) {
fclose(log_file);
}
-end:
+
log_unlock();
}
/// Open the log file for appending.
///
-/// @return FILE* decided by log_path_init() or stderr in case of error
+/// @return Log file, or stderr on failure
FILE *open_log_file(void)
{
- static bool opening_log_file = false;
- // Disallow recursion. (This only matters for log_path_init; for logmsg and
- // friends we use a mutex: log_lock).
- if (opening_log_file) {
- do_log_to_file(stderr, ERROR_LOG_LEVEL, NULL, __func__, __LINE__, true,
- "Cannot LOG() recursively.");
- return stderr;
- }
-
- FILE *log_file = NULL;
- opening_log_file = true;
- if (log_path_init()) {
- log_file = fopen(log_file_path, "a");
- }
- opening_log_file = false;
-
- if (log_file != NULL) {
- return log_file;
+ errno = 0;
+ if (log_file_path[0]) {
+ FILE *f = fopen(log_file_path, "a");
+ if (f != NULL) {
+ return f;
+ }
}
// May happen if:
- // - LOG() is called before early_init()
+ // - fopen() failed
+ // - LOG() is called before log_init()
// - Directory does not exist
// - File is not writable
- do_log_to_file(stderr, ERROR_LOG_LEVEL, NULL, __func__, __LINE__, true,
- "Logging to stderr, failed to open $" LOG_FILE_ENV ": %s",
- log_file_path);
+ do_log_to_file(stderr, LOGLVL_ERR, NULL, __func__, __LINE__, true,
+ "failed to open $" ENV_LOGFILE " (%s): %s",
+ strerror(errno), log_file_path);
return stderr;
}
@@ -237,8 +236,7 @@ void log_callstack_to_file(FILE *log_file, const char *const func_name, const in
// Now we have a command string like:
// addr2line -e /path/to/exe -f -p 0x123 0x456 ...
- do_log_to_file(log_file, DEBUG_LOG_LEVEL, NULL, func_name, line_num, true,
- "trace:");
+ do_log_to_file(log_file, LOGLVL_DBG, NULL, func_name, line_num, true, "trace:");
FILE *fp = popen(cmdbuf, "r");
char linebuf[IOSIZE];
while (fgets(linebuf, sizeof(linebuf) - 1, fp) != NULL) {
@@ -255,13 +253,7 @@ void log_callstack(const char *const func_name, const int line_num)
{
log_lock();
FILE *log_file = open_log_file();
- if (log_file == NULL) {
- goto end;
- }
-
log_callstack_to_file(log_file, func_name, line_num);
-
-end:
log_unlock();
}
#endif
@@ -282,14 +274,18 @@ static bool do_log_to_file(FILE *log_file, int log_level, const char *context,
static bool v_do_log_to_file(FILE *log_file, int log_level, const char *context,
const char *func_name, int line_num, bool eol, const char *fmt,
va_list args)
+ FUNC_ATTR_PRINTF(7, 0)
{
+ // Name of the Nvim instance that produced the log.
+ static char name[16] = { 0 };
+
static const char *log_levels[] = {
- [DEBUG_LOG_LEVEL] = "DEBUG",
- [INFO_LOG_LEVEL] = "INFO ",
- [WARN_LOG_LEVEL] = "WARN ",
- [ERROR_LOG_LEVEL] = "ERROR",
+ [LOGLVL_DBG] = "DBG",
+ [LOGLVL_INF] = "INF",
+ [LOGLVL_WRN] = "WRN",
+ [LOGLVL_ERR] = "ERR",
};
- assert(log_level >= DEBUG_LOG_LEVEL && log_level <= ERROR_LOG_LEVEL);
+ assert(log_level >= LOGLVL_DBG && log_level <= LOGLVL_ERR);
// Format the timestamp.
struct tm local_time;
@@ -297,8 +293,7 @@ static bool v_do_log_to_file(FILE *log_file, int log_level, const char *context,
return false;
}
char date_time[20];
- if (strftime(date_time, sizeof(date_time), "%Y-%m-%dT%H:%M:%S",
- &local_time) == 0) {
+ if (strftime(date_time, sizeof(date_time), "%Y-%m-%dT%H:%M:%S", &local_time) == 0) {
return false;
}
@@ -308,16 +303,37 @@ static bool v_do_log_to_file(FILE *log_file, int log_level, const char *context,
millis = (int)curtime.tv_usec / 1000;
}
+ // Get a name for this Nvim instance.
+ // TODO(justinmk): expose this as v:name ?
+ if (name[0] == '\0') {
+ // Parent servername.
+ const char *parent = path_tail(os_getenv(ENV_NVIM));
+ // Servername. Empty until starting=false.
+ const char *serv = path_tail(get_vim_var_str(VV_SEND_SERVER));
+ if (parent && parent[0] != NUL) {
+ snprintf(name, sizeof(name), "%s/c", parent); // "/c" indicates child.
+ } else if (serv[0] != NUL) {
+ snprintf(name, sizeof(name), "%s", serv);
+ } else {
+ int64_t pid = os_get_pid();
+ snprintf(name, sizeof(name), "?.%-5" PRId64, pid);
+ }
+ }
+
// Print the log message.
- int64_t pid = os_get_pid();
int rv = (line_num == -1 || func_name == NULL)
- ? fprintf(log_file, "%s %s.%03d %-5" PRId64 " %s",
- log_levels[log_level], date_time, millis, pid,
+ ? fprintf(log_file, "%s %s.%03d %-10s %s",
+ log_levels[log_level], date_time, millis, name,
(context == NULL ? "?:" : context))
- : fprintf(log_file, "%s %s.%03d %-5" PRId64 " %s%s:%d: ",
- log_levels[log_level], date_time, millis, pid,
+ : fprintf(log_file, "%s %s.%03d %-10s %s%s:%d: ",
+ log_levels[log_level], date_time, millis, name,
(context == NULL ? "" : context),
func_name, line_num);
+ if (name[0] == '?') {
+ // No v:servername yet. Clear `name` so that the next log can try again.
+ name[0] = '\0';
+ }
+
if (rv < 0) {
return false;
}