Skip to content
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

Merged
merged 3 commits into from
Oct 16, 2018
Merged

log: Add option to re-open a log file #326

merged 3 commits into from
Oct 16, 2018

Conversation

chrissie-c
Copy link
Contributor

With an optional new filename

@chrissie-c
Copy link
Contributor Author

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+");
Copy link
Contributor

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.

Copy link
Contributor Author

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

@wferi
Copy link
Contributor

wferi commented Oct 1, 2018

This changes the API of the custom loggers (to take a struct timespec rather than a time_t)

How does time representation influence log reopening? Thanks.

@chrissie-c
Copy link
Contributor Author

This changes the API of the custom loggers (to take a struct timespec rather than a time_t)

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;
Copy link
Contributor

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.

Copy link
Contributor Author

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!

Copy link
Contributor

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);
Copy link
Contributor

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...

Copy link
Contributor Author

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.

Copy link
Contributor

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.

@chrissie-c
Copy link
Contributor Author

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.

Copy link
Contributor

@wferi wferi left a 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);
Copy link
Contributor

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);
Copy link
Contributor

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);
Copy link
Contributor

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.

@chrissie-c
Copy link
Contributor Author

Chatting with honza it seems we have opened a large can of worms here, the whole reconfig locking is going to need an overhaul.

@wferi
Copy link
Contributor

wferi commented Oct 10, 2018

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.

@jfriesse
Copy link
Member

@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).

@chrissie-c
Copy link
Contributor Author

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).

@jfriesse
Copy link
Member

Last update seems to solve all problems with corosync-cfgtool -R and logging , so ACK by me.

Looks like some of @wferi's comments are still unresolved (Why not return -errno; ...) so probably not yet ready to go.

@wferi
Copy link
Contributor

wferi commented Oct 15, 2018

Yes, thanks, this last change makes fairly explicit where I was going to with that idea: _do_file_reload() is just qb_log_file_reopen() with the arguments swapped, so in my opinion it would be better to not introduce it at all, but call qb_log_file_reopen() from _file_reload().

@chrissie-c
Copy link
Contributor Author

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.

@wferi
Copy link
Contributor

wferi commented Oct 15, 2018

Your call, certainly. I probably wouldn't have brought it up if _do_file_reload() wasn't introduced in this very pull request. If you prefer to have this symmetry between _file_reload() and qb_log_file_reopen(), then be it.

@chrissie-c
Copy link
Contributor Author

Thanks.

@chrissie-c chrissie-c merged commit 6032d21 into ClusterLabs:master Oct 16, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants