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

Configuration option to output logs in logfmt #12934

Open
wants to merge 17 commits into
base: unstable
Choose a base branch
from

Conversation

azuredream
Copy link

@azuredream azuredream commented Jan 11, 2024

Add ability to configure Redis to output logs in logfmt (See https://brandur.org/logfmt) as well as configure timestamp format options to more standard ISO 8601 or unix timestamp.

This change is implemented by two configs:
log-format: Either default or logfmt.
log-timestamp-format: default, iso8601, or unix.

iso8601 includes time zone.
Closes #12918
Closes #12932

LOG_FORMAT_DEFAULT + LOG_TIMESTAMP_DEFAULT
42888:M 12 Jan 2024 18:26:34.377 * Server initialized
42888:M 12 Jan 2024 18:26:34.377 * Ready to accept connections tcp

LOG_FORMAT_LOGFMT + LOG_TIMESTAMP_DEFAULT
pid=43765 role=master   timestamp="12 Jan 2024 18:29:14.441" level=notice  message="Server initialized"
pid=43765 role=master   timestamp="12 Jan 2024 18:29:14.441" level=notice  message="Ready to accept connections tcp"

LOG_FORMAT_LOGFMT + LOG_TIMESTAMP_ISO8601
pid=44325 role=master   timestamp="2024-01-12T18:31:24.697+05:00" level=notice  message="Server initialized"
pid=44325 role=master   timestamp="2024-01-12T18:31:24.697+05:00" level=notice  message="Ready to accept connections tcp"

LOG_FORMAT_LOGFMT + LOG_TIMESTAMP_UNIX
pid=44790 role=master   timestamp="1705102366" level=notice  message="Server initialized"
pid=44790 role=master   timestamp="1705102366" level=notice  message="Ready to accept connections tcp"

Signed-off-by: azuredream <zhaozixuan67@gmail.com>
Copy link
Contributor

@zuiderkwast zuiderkwast left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@azuredream Thanks, it's a good start. Let's see if the core team will accept this.

@madolson do you like the idea of a configurable logging format?

src/config.c Outdated Show resolved Hide resolved
src/server.c Outdated
Comment on lines 147 to 149
if(server.logfmt) {
fprintf(fp,"pid=%d role_char=%c timestamp=\"%s\" level=%c message=\"%s\"\n",
(int)getpid(),role_char, buf,c[level],msg);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest role=%c (instead of role_char=%c). Is one char clear enough? If not, we can consider using role=master, role=sentinel, etc. but it makes the log longer. Maybe one char is clear enough?

For the level, maybe it would be more normal to use the words warning, notice, info, debug instead of the symbols?

Do we need to check if msg contains double quotes and linebreaks and escape those?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One more thing: I think the timestamp should be in ISO format, like 2024-01-11T10:34:38.000Z (always in UTC or with another timezone, i'm not sure what's best...)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your review!
I suppose the questions are for @madolson. I'll wait for his reply.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@azuredream The questions are for you. :) What do you think?

Copy link
Contributor

@madolson madolson Jan 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually have a slightly different opinion, which is that I think the strftime format for timestamps should be configurable. It's easiest to test and validate, and I've seen various asks for it to be in a slight different format over time.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@madolson the log in debug.c you linked prints the argv, which can be a key name.

Correct, it's on my list of stuff I would like to remove. It was vaguely on my radar as one of the things to fix with https://github.com/redis/redis/pulls?q=is%3Apr+is%3Aopen+redact.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@madolson Ah, so do we need to get #11747 merged before we can proceed with this and avoid escaping stuff in message? (I don't like that PRs are blocked by other PRs. Maybe we can just change " to ' in that log entry for now?)

For modules logging, can we simply replace " with ' in-place in moduleLogRaw?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@madolson Ah, so do we need to get #11747 merged before we can proceed with this and avoid escaping stuff in message? (I don't like that PRs are blocked by other PRs. Maybe we can just change " to ' in that log entry for now?)

Let's do as you suggested for the moment. Let's also make a debug assertion so we never add the " for any logs.

For modules logging, can we simply replace " with ' in-place in moduleLogRaw?

Like at runtime? I suppose that works, but is also a bit of a breaking change.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes at runtime. Or replace with \". What else can we do?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes at runtime. Or replace with ". What else can we do?

I suppose we probably need to do that now.

@madolson madolson added the state:major-decision Requires core team consensus label Jan 11, 2024
@madolson
Copy link
Contributor

Also just wanted to mention #12932 for reference.

@CharlesChen888
Copy link
Contributor

CharlesChen888 commented Jan 12, 2024

Does this mean we will have two new configs, one for enabling json logfmt, another one for selecting timestamp format?

@madolson
Copy link
Contributor

Does this mean we will have two new configs, one for enabling json logfmt, another one for selecting timestamp format?

I don't have a great alternative suggestion without making a complex configuration argument.

@ecerulm
Copy link

ecerulm commented Jan 12, 2024

Regarding timestamps #12932, I guess most of the time user would like to see millisecond or microseconds resolution, a configurable strftime() format specification won't cut it because there is no way to specify milliseconds / microseconds in linux's strftime(). Linux strftime() does not have it, and I think BSD's strftime does not have it either.

Even today redis source code adds the milliseconds outside the strftime() as show on

redis/src/server.c

Lines 138 to 139 in 9d0158b

off = strftime(buf,sizeof(buf),"%d %b %Y %H:%M:%S.",&tm);
snprintf(buf+off,sizeof(buf)-off,"%03d",(int)tv.tv_usec/1000);

So then you would need to create your own extended "format specification" supporting "%f" for microseconds (like python does) and to hide differences between strftime format specification across operating system.

Summarizing the issues with strftime() are in my opinion

  • strftime format specification differences between operating systems
  • strftime usually does not allow fractional seconds/ milliseconds / microseconds

To me it seems simpler to enforce a ISO 8601 with microsecond resolution and utc offset:

2024-01-11T12:45:30.123456+02:00

Signed-off-by: azuredream <zhaozixuan67@gmail.com>
@azuredream
Copy link
Author

Added timestamp_format enum.
Use reader friendly role and verbose_level string in logfmt ouput.

@azuredream
Copy link
Author

azuredream commented Jan 12, 2024

DEFAULT
42888:M 12 Jan 2024 18:26:34.377 * Server initialized
42888:M 12 Jan 2024 18:26:34.377 * Ready to accept connections tcp

LOG_FORMAT_LOGFMT + LOG_TIMESTAMP_DEFAULT
pid=43765 role=master   timestamp="12 Jan 2024 18:29:14.441" level=notice  message="Server initialized"
pid=43765 role=master   timestamp="12 Jan 2024 18:29:14.441" level=notice  message="Ready to accept connections tcp"

LOG_FORMAT_LOGFMT + LOG_TIMESTAMP_ISO8601
pid=44325 role=master   timestamp="2024-01-12T18:31:24.697+0000" level=notice  message="Server initialized"
pid=44325 role=master   timestamp="2024-01-12T18:31:24.697+0000" level=notice  message="Ready to accept connections tcp"


LOG_FORMAT_LOGFMT + LOG_TIMESTAMP_UNIX
pid=44790 role=master   timestamp="1705102366" level=notice  message="Server initialized"
pid=44790 role=master   timestamp="1705102366" level=notice  message="Ready to accept connections tcp"

Signed-off-by: azuredream <zhaozixuan67@gmail.com>
Signed-off-by: azuredream <zhaozixuan67@gmail.com>
Signed-off-by: azuredream <zhaozixuan67@gmail.com>
@azuredream
Copy link
Author

Hi @zuiderkwast. Any comment on this PR?

Copy link
Contributor

@zuiderkwast zuiderkwast left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm OK with the config, but it is marked with major-decision which means the core team will need to decide about the config in a meeting. (I'm not in the core team.)

Maybe it's better to wait until the config has been decided before changing the details of the code.

src/server.c Outdated Show resolved Hide resolved
src/server.c Outdated Show resolved Hide resolved
src/server.c Outdated Show resolved Hide resolved
Signed-off-by: azuredream <zhaozixuan67@gmail.com>
@azuredream
Copy link
Author

Thanks for your review. Updated.

Signed-off-by: azuredream <zhaozixuan67@gmail.com>
break;
}

switch (server.log_format) {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are 6 combinations of log_format X role. Could be more moving forward. This is another way to code the logic, but I'm not sure if it's the "better" way...
It does keep the minimum memory usage for role print though.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this approach is sensible enough.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could do like we do for verbosity level. For the legacy format we pick a char from "XCSM" and for logfmt a string from an array, same index.

Signed-off-by: azuredream <zhaozixuan67@gmail.com>
src/server.c Outdated Show resolved Hide resolved
src/server.h Outdated Show resolved Hide resolved
@madolson
Copy link
Contributor

@azuredream Thanks for all your involvement, we'll discuss it tomorrow and give directional input if this is the best format for it to be accepted. Thanks for all your help so far.

@azuredream
Copy link
Author

Hi @madolson. Thanks for review. I just updated my PR based on your suggestions.

@madolson
Copy link
Contributor

@redis/core-team Please review the two configurations at the top for a high level approval. There is one pending question, which is what to do about module logging that might inject ", which would break the logfmt structure.

@oranagra
Copy link
Member

can someone edit the top comment to provide some overview of the differences (examples), as well as justification for this feature?

src/server.c Outdated Show resolved Hide resolved
@azuredream
Copy link
Author

can someone edit the top comment to provide some overview of the differences (examples), as well as justification for this feature?

Thanks to @zuiderkwast for updating the top comment.
I added some examples.

@azuredream
Copy link
Author

PR updated.
Fixed an issue in nolocks_localtime(). The timezone field was not set.

Signed-off-by: azuredream <zhaozixuan67@gmail.com>
Signed-off-by: azuredream <zhaozixuan67@gmail.com>
@azuredream
Copy link
Author

Not sure how to make tm_gmtoff work on every the platform. Maybe that's why it was not set before.
Any advice on this?

@oranagra
Copy link
Member

i still don't see the top comment mentions the justification.
specifically, why do we want to change the log format, and specifically why is logmft the right one.

our log file isn't an API, and for the most part we feel free to add / remove rephrase messages, or change their log level, so i wonder why would someone want to parse by software.

src/server.c Outdated
Comment on lines 146 to 151
snprintf(buf+off,sizeof(buf)-off,"%03d",(int)tv.tv_usec/1000);
char tzbuf[6];
strftime(tzbuf, sizeof(tzbuf), "%z", &tm);
char tz_formatted[7];
snprintf(tz_formatted, sizeof(tz_formatted), "%c%c%c:%c%c", tzbuf[0], tzbuf[1], tzbuf[2], tzbuf[3], tzbuf[4]);
strncat(buf, tz_formatted, sizeof(buf) - strlen(buf) - 1);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good, but maybe we can do it with only one snprintf for milliseconds and time zone?

Suggested change
snprintf(buf+off,sizeof(buf)-off,"%03d",(int)tv.tv_usec/1000);
char tzbuf[6];
strftime(tzbuf, sizeof(tzbuf), "%z", &tm);
char tz_formatted[7];
snprintf(tz_formatted, sizeof(tz_formatted), "%c%c%c:%c%c", tzbuf[0], tzbuf[1], tzbuf[2], tzbuf[3], tzbuf[4]);
strncat(buf, tz_formatted, sizeof(buf) - strlen(buf) - 1);
char tzbuf[6];
strftime(tzbuf, sizeof(tzbuf), "%z", &tm);
snprintf(buf + off, sizeof(buf) - off, "%03d%c%c%c:%c%c",
(int)tv.tv_usec/1000,
tzbuf[0], tzbuf[1], tzbuf[2], tzbuf[3], tzbuf[4]);

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Updated as your suggestion.

@zuiderkwast
Copy link
Contributor

@oranagra The motivation is in the linked issue #12918. I thought it was linked before but it wasn't so I linked it now.

@azuredream Hint for another time: Include Fixes #12918 or Closes #12918 in the PR description to automatically link it to the issue.

@azuredream
Copy link
Author

@oranagra The motivation is in the linked issue #12918. I thought it was linked before but it wasn't so I linked it now.

@azuredream Hint for another time: Include Fixes #12918 or Closes #12918 in the PR description to automatically link it to the issue.

Thanks! I linked two issues that may be closed by this PR

@soloestoy
Copy link
Collaborator

I have no opinion to logfmt (neither supporting nor opposing), but I do believe that time zone information is useful. It helps to determine the actual time when deploying globally. Another PR #12939 has been submitted separately to add time zone information.

@zuiderkwast
Copy link
Contributor

zuiderkwast commented Feb 1, 2024

@redis/core-team There are three more or less orthogonal things to take decisions about here:

  1. Add time zone info in timestamps. This is new info that we don't have today.
  2. Timestamp format. Presumably a more standard format is easier to consume by tools.
  3. Log format that can be more easily aggregated by generic tools ("slurped", "grokkable by telemetry collection systems"). JSON or logfmt or some other format would do. (Logfmt seemed simpler than JSON. I assume we don't want to add a JSON lib to Redis.)

You may want to decide about each of these separately.

@azuredream
Copy link
Author

azuredream commented Feb 4, 2024

Implemented a function to populate timezone string since gmtoff + strftime is not always available.

The tm_gmtoff field is derived from BSD and is a GNU library extension; it is not visible in a strict ISO C environment.

Signed-off-by: azuredream <zhaozixuan67@gmail.com>
Signed-off-by: azuredream <zhaozixuan67@gmail.com>
@CLAassistant
Copy link

CLAassistant commented Mar 24, 2024

CLA assistant check
All committers have signed the CLA.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
state:major-decision Requires core team consensus
Projects
Status: Todo
Development

Successfully merging this pull request may close these issues.

[NEW] Add timezone to redis logs [NEW] Configuration option to output logs in JSON format or logfmt
8 participants