-
Notifications
You must be signed in to change notification settings - Fork 98
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
log: Add option to re-open a log file #326
Conversation
This changes the API of the custom loggers (to take a struct timespec rather than a time_t) but we're already changing the soname of the library anyway for v2.0.0 |
lib/log_file.c
Outdated
} | ||
t->instance = fopen(t->filename, "a+"); | ||
newfile = fopen(t->filename, "a+"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe insert a comment here that the caller can do the error handling because errno
is preserved? And hope qb_log_target_user_data_set()
always respects this constraint... Maybe add a comment there as well? Ugh... Rather, what about changing _file_reload()
(a little used function) to return the error code explicitly for better transparency (letting the code document itself)?
On a higher level, is it a good idea to close()
before attempting to open()
? What do you prefer to stay with (or without) on failure? For example, strange things can happen if logrotate compresses a file still being appended to, but that may still be better than a total loss of logs. On the other hand, if the IPC used by the application for triggering log rotation facilitates reporting errors, logrotate can be informed about the failure. (Synchronous IPC also avoids the race between close()
and compression without resorting to delaycompress
, which is not a proper solution in technical sense anyway.) I think it might be better to leave this choice to the application: if the reopen fails, it should be able to continue logging to the old location knowing it reported the error to the requester of the reopen, or just close the logs and optionally prepare for some kind of recovery.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good points all. I've pushed a new commit to fix them
How does time representation influence log reopening? Thanks. |
It doesn't - this is a different PR I'm still working on, sorry. Blame it on Mondays! |
lib/log_file.c
Outdated
|
||
if (!target->instance) { | ||
if (rc) { | ||
return -errno; | ||
} else { | ||
return 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not move this part into _do_file_reload()
so that you can simply return _do_file_reload(t)
here? Gets rid of a conditional and moves reading of errno
much closer to the function setting it, which adds to clarity IMO.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can't do that because _do_file_reload() is called from the logger object and that doesn't return an error (bad design, I know, not guilty). That's why I had to split it into two functions in the first place!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mean _file_reload()
, don't you? You could still define it to errno = -_do_file_reload(t, NULL)
I guess (using the second argument for passing in the new filename as discussed in my other comment). Then get rid of it altogether with the next API change.
lib/log_file.c
Outdated
@@ -132,13 +148,14 @@ int32_t | |||
qb_log_file_reopen(int32_t t, const char *filename) | |||
{ | |||
struct qb_log_target *target = qb_log_target_get(t); | |||
int rc; | |||
|
|||
if (filename) { | |||
(void)strlcpy(target->filename, filename, PATH_MAX); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't changing the filename field premature here? If open()
fails, we keep the old FILE
structure after all. I overlooked this added complication; looks like one must pass the new file name to _do_file_reload()
in a separate argument...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Possibly. But t->filename is only used to open the file anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe you. It might be useful to close this avenue of future confusion anyway.
I've changed the reopen to only close the old file if the new one succeeds, and moved the fclose to be sure that errno is retained on return. Return code from _file_reload() I'm going to leave as-is for the moment. Changing the logger api is a bigger job and should be kept al together IMO. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's a fine addition to the API. Thanks!
} | ||
} | ||
|
||
static int | ||
_do_file_reload(int32_t target) | ||
_do_file_reload(const char *filename, int32_t target) | ||
{ | ||
struct qb_log_target *t = qb_log_target_get(target); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just wondering: why qb_log_target_get()
does not check the validity of target
?
lib/log_file.c
Outdated
(void)strlcpy(target->filename, filename, PATH_MAX); | ||
} | ||
rc = _do_file_reload(t); | ||
rc = _do_file_reload(filename, t); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not return -errno; /* from fopen() */
instead of return -1;
in _do_file_reload()
so that you can then rename it to int32_t qb_log_file_reopen()
and happily be one function down?
lib/log_file.c
Outdated
fclose(t->instance); | ||
/* Only close oldfile if newfile succeeds */ | ||
if (oldfile && newfile) { | ||
fclose(oldfile); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd write this as if (oldfile) { fclose(oldfile); }
inside the if (newfile) { }
block below for clarity. YMMV.
Chatting with honza it seems we have opened a large can of worms here, the whole reconfig locking is going to need an overhaul. |
Wouldn't dropping support for changing the log file name during reopen help a little? That's somewhat unrelated anyway and remains possible by stopping and starting logging the old way. |
@wferi Not too much. Current logging thread is simply not protected from changing configuration at all (so qb_log_ctl2) and errors are visible even when something as simple as reload corosync.conf is performed (it basically works just by accident). |
and add API call to reopen a log file.
To clarify what's happening here. I force pushed a new patch yesterday that adds locking around all of the logging reconfiguration. Today's small amendment is to make 'in_logger' atomic as it's accessed across threads and helgrind was complaining (it still complains but only because it only understands pthreads). |
Last update seems to solve all problems with Looks like some of @wferi's comments are still unresolved (Why not return -errno; ...) so probably not yet ready to go. |
It's tidier
Yes, thanks, this last change makes fairly explicit where I was going to with that idea: |
Thanks. I'd rather leave it as it is TBH, then the two 'API' functions are equal and the fn that does the work is called by both. |
Your call, certainly. I probably wouldn't have brought it up if |
Thanks. |
With an optional new filename