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

system logs ui changes #571

Merged
merged 16 commits into from
Oct 19, 2024
Merged

Conversation

vishalmishraa
Copy link
Contributor

Linked Issue(s)

issue #555

Acceptance Criteria fulfillment

  • Task 1
  • Task 2
  • Task 3

Proposed changes (including videos or screenshots)

Screenshot 2024-10-05 at 1 53 58 AM
Screenshot 2024-10-05 at 1 50 18 AM
Screenshot 2024-10-05 at 1 50 03 AM
Screenshot 2024-10-05 at 1 50 11 AM

Further comments

@jayantbh
Copy link
Contributor

jayantbh commented Oct 4, 2024

Thanks for your contribution, @vishalmishraa!
Based on the screenshots it's certainly a decent improvement.

I would love to accept this PR, but it needs a few improvements to avoid performance issues.

Given that there could be thousands of lines being rendered, we do need to keep an eye out for excessive calls to JSON.stringify, Array.includes, and various other places.

Additionally, I think the metadata doesn't really need to be printed. What do you think?

@jayantbh
Copy link
Contributor

jayantbh commented Oct 4, 2024

Linter failed. We need to sort out the linting issues but for now you can check our contributing guide to see how to fix that. Let us know if it doesn't work out for you.

@vishalmishraa
Copy link
Contributor Author

vishalmishraa commented Oct 4, 2024

Thanks for your contribution, @vishalmishraa! Based on the screenshots it's certainly a decent improvement.

I would love to accept this PR, but it needs a few improvements to avoid performance issues.

Given that there could be thousands of lines being rendered, we do need to keep an eye out for excessive calls to JSON.stringify, Array.includes, and various other places.

Additionally, I think the metadata doesn't really need to be printed. What do you think?

hi , @jayantbh ,

I’ll remove the metadata entirely, so there’s no need to use JSON.stringify. for array.includes, I’ll use Set() instead, as it takes O(1) time complexity, which is a better approach. . am I taking the correct approach? and yup, I need to fix the linting issues

@jayantbh
Copy link
Contributor

jayantbh commented Oct 5, 2024

Directionally I think yes.
Your last commit looks better too.

I haven't done a proper review but I'll ask the team to check it out.

@jayantbh
Copy link
Contributor

jayantbh commented Oct 5, 2024

This PR could use some tests. Especially with various kinds of logs that might come through.

@jayantbh
Copy link
Contributor

jayantbh commented Oct 5, 2024

And of course, to test the performance of how many lines you're able to process via your functions as a benchmark.

@jayantbh
Copy link
Contributor

jayantbh commented Oct 5, 2024

I ran a quick test myself based on the number of lines, and I think we'll hit other performance limits before we hit perf issues with the regexes and matches.

@vishalmishraa
Copy link
Contributor Author

I ran a quick test myself based on the number of lines, and I think we'll hit other performance limits before we hit perf issues with the regexes and matches.

okay , i think i need to change my approach ,Could you suggest an alternatives or any way to do that ? I will work on implementing it accordingly.

@jayantbh
Copy link
Contributor

jayantbh commented Oct 6, 2024

No no. I'm saying you might not need to change your approach.

@vishalmishraa
Copy link
Contributor Author

so , do i need to do any improvements or it is good to go ?

@jayantbh
Copy link
Contributor

jayantbh commented Oct 6, 2024

This PR could use some tests. Especially with various kinds of logs that might come through.

This.

@vishalmishraa
Copy link
Contributor Author

vishalmishraa commented Oct 6, 2024

Okay, I’m working on it. I’ll be writing tests in ./web-server/src/utils/test, where I’ll create a logFormatter.test.ts file and write the tests.

@vishalmishraa
Copy link
Contributor Author

vishalmishraa commented Oct 6, 2024

@jayantbh I need help regarding generating different types of system logs. Should I generate new logs for testing? If yes, how can I generate other different logs? Or is it better to use the logs that are currently being generated and write test for them only?

UPDATE : got some different kind of logs from my own dbs , gonna use them in testing :

FOR REDIS :

Screenshot 2024-10-06 at 9 00 56 PM

FOR POSTGRES:

Screenshot 2024-10-06 at 9 01 26 PM

AND i think for gunicorn the default system generated logs are enogh

@jayantbh
Copy link
Contributor

jayantbh commented Oct 6, 2024

I think handle for the variety of logs that are generated today.
But ensure that when and if something unexpected comes in the logs (such as, missing HTTP method in HTTP logs, dates formatted in a different way, etc.) Then it doesn't break the UI.

@vishalmishraa
Copy link
Contributor Author

vishalmishraa commented Oct 7, 2024

i think the function I wrote is not feasible for parsing all types of logs. There are several different types of logs, such as system-generated logs (some print statements in backend code), HTTP server error logs, and others.

i did some research on better logging solutions and found a CNCF-based library called Fluentd, which offers unified logging, better parsing, and improved data collection and observation. It's also lightweight, requiring only 40 to 50 MB of space. I think it could be a good fit for this project.

more details:

since , i think current approach may not be the right fit right now, I’m focusing on other way to solve it.

@vishalmishraa vishalmishraa marked this pull request as draft October 7, 2024 09:17
@jayantbh
Copy link
Contributor

jayantbh commented Oct 7, 2024

I suspected it could go in this way.
You can go with a simpler approach altogether as well.

@vishalmishraa
Copy link
Contributor Author

vishalmishraa commented Oct 7, 2024

I suspected it could go in this way. You can go with a simpler approach altogether as well.

Then , there will be some logs, which can't be parsed, and they will be displayed as it is without colors. I'll try to add regex for logs that are frequently generated, should i proceed with this ?

@jayantbh
Copy link
Contributor

jayantbh commented Oct 8, 2024

I suspected it could go in this way. You can go with a simpler approach altogether as well.

Then , there will be some logs, which can't be parsed, and they will be displayed as it is without colors. I'll try to add regex for logs that are frequently generated, should i proceed with this ?

Yea, I think you could try that.

Something is better than nothing after all. But make sure you have tests.

@vishalmishraa vishalmishraa marked this pull request as ready for review October 8, 2024 11:39
@vishalmishraa
Copy link
Contributor Author

vishalmishraa commented Oct 8, 2024

I suspected it could go in this way. You can go with a simpler approach altogether as well.

Then , there will be some logs, which can't be parsed, and they will be displayed as it is without colors. I'll try to add regex for logs that are frequently generated, should i proceed with this ?

Yea, I think you could try that.

Something is better than nothing after all. But make sure you have tests.

Thanks, @jayantbh. I've updated the functions and added the tests. Please review them.

Reference for Regexes

Redis Log Regex

  • Log Level: Represents log levels (., -, *, #) which correspond to LOG_DEBUG, LOG_INFO, LOG_NOTICE, and LOG_WARNING respectively. For more information, refer to the Redis source code.

  • Role: The role is represented by (X, C, S, or M). For details, see in the Redis source code.

HTTP Gunicorn Log Regex

For details on the HTTP Gunicorn log regex, refer to the official Gunicorn documentation.

@jayantbh
Copy link
Contributor

jayantbh commented Oct 10, 2024

Looks better. :)

Could you use Error Boundaries to show the logs in completely plain-text (the view before your changes that is) in case something breaks in the off chance?

Basically the idea is, that if something does break, it should at least show the plain version of the logs. Of course they should be shown in a tiny text at the top or bottom, or somewhere that they ran into an error and they should create an issue, which should link to: https://github.com/middlewarehq/middleware/issues/new/choose

@vishalmishraa
Copy link
Contributor Author

Hmm, not sure I feel great about that approach. How did you try to add and test the error boundary that it caused a higher level boundary to catch it instead?

i did this :


const SystemLogs = () => {
    return (
        <ErrorBoundary FallbackComponent={SystemLogErrorFallback}>
            // code
        </ErrorBoundary>
    );
};

But it didn’t work, and I think it should have worked.

@vishalmishraa
Copy link
Contributor Author

Also i think the approach I'm using now would be work good because the component which should be passed into the ErrorBoundary fallback should also receive serviceName and other props as it should render logs in plain text. So, in the future, if we make any improvements to the UI for SystemLogs like (implementing search feature ,etc..), we would need to apply the same modifications to the custom fallback component as well, since it's rendering system logs in plain text. Therefore, it would be better to handle this in the same position .

@vishalmishraa
Copy link
Contributor Author

Hi @jayantbh ,

I’ve implemented the error boundary and apologize for the confusion earlier. You were right , I was testing it incorrectly, which is why it didn’t work. However, I’ve now implemented it properly. I’ve also created a useSystemLog hook for it and separated the component.Please check it now.

@jayantbh
Copy link
Contributor

@vishalmishraa could you share screenshots of the error boundary in effect?

@vishalmishraa
Copy link
Contributor Author

@vishalmishraa could you share screenshots of the error boundary in effect?

Sure :

Error.Test.mov

@jayantbh
Copy link
Contributor

Looks great!
The linter appears to have failed. Do check what that's about.

Meanwhile, we'll give this a round of final reviews. But I think we're looking good visually/functionally.

@vishalmishraa
Copy link
Contributor Author

vishalmishraa commented Oct 15, 2024

i think linter is failed because of workflow

Screenshot 2024-10-15 at 2 15 56 PM

@jayantbh
Copy link
Contributor

Okay, I guess ignore that for now. We'll do the reviews as soon as we're able. :)

export const parseLogLine = (rawLogLine: string): ParsedLog | null => {
const generalLogMatch = rawLogLine.match(generalLogRegex);
if (generalLogMatch) {
const [, timestamp, , logLevel, message] = generalLogMatch;
Copy link
Contributor

Choose a reason for hiding this comment

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

const [_pickAname, timestamp, _pickAnotherName, logLevel, message]

Copy link
Contributor Author

Choose a reason for hiding this comment

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

got it.

const [method, path] = request.split(' ');
return {
timestamp,
logLevel: 'INFO', // Assuming all HTTP logs are INFO level
Copy link
Contributor

Choose a reason for hiding this comment

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

all these string constants should be wrapped in an enum in our src/types/resources.ts

enum YourEnumName 
{ 
  INFO = 'INFO', 
  DEBUG = 'DEBUG'
  ...other_possible_values... 
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done , created a enum LogLevel.


const postgresLogMatch = rawLogLine.match(postgresLogRegex);
if (postgresLogMatch) {
const [, timestamp, , logLevel, message] = postgresLogMatch;
Copy link
Contributor

Choose a reason for hiding this comment

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

same:

const [_pickAname, timestamp, _pickAnotherName, logLevel, message]


const dataSyncLogMatch = rawLogLine.match(dataSyncLogRegex);
if (dataSyncLogMatch) {
const [, logLevel, action, service, message] = dataSyncLogMatch;
Copy link
Contributor

Choose a reason for hiding this comment

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

same

Comment on lines 20 to 31
export const validLogLevels = new Set([
'DEBUG',
'INFO',
'NOTICE',
'WARNING',
'ERROR',
'LOG',
'FATAL',
'PANIC',
'STATEMENT',
'DETAIL'
]);
Copy link
Contributor

Choose a reason for hiding this comment

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

this should be an enum in our resources file

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added these as enums and also removed the validLogLevels logic from the parser. Previously, I was using two approaches to parse PostgreSQL logs, but during testing, I noticed that my first approach, PostgresMultilineLogMatch, can parse both types of PostgreSQL logs. Therefore, there's no need to write separate logic for each type ,refer to the official postgres documentation . and this article also for log levels .

export const parseLogLine = (rawLogLine: string): ParsedLog | null => {
const generalLogMatch = rawLogLine.match(generalLogRegex);
if (generalLogMatch) {
const [_fullLog, timestamp, _unused, logLevel, message] = generalLogMatch;
Copy link
Contributor

Choose a reason for hiding this comment

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

_unused
this looks off, name it correctly to what this variable stands for, and the underscore is enough to know that this variable is unused

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done. ,changing it with _pid

Comment on lines 28 to 29
_unused,
_unused2,
Copy link
Contributor

Choose a reason for hiding this comment

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

same with these :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

changed the second one to _username , and first one with _ (underscore) refer to this

e-for-eshaan
e-for-eshaan previously approved these changes Oct 18, 2024
Copy link
Contributor

@e-for-eshaan e-for-eshaan left a comment

Choose a reason for hiding this comment

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

LGTM, good stuff @vishalmishraa 🎉

Comment on lines 1 to 7
export interface ParsedLog {
timestamp: string;
logLevel: string;
message: string;
role?: string;
ip?: string;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

move this interface declaration to resources.ts. we use that for all our type declaration

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

Comment on lines 5 to 10
<Line
key={index}
marginBottom={'8px'}
fontSize={'14px'}
fontFamily={'monospace'}
>
Copy link
Contributor

@shivam-bit shivam-bit Oct 18, 2024

Choose a reason for hiding this comment

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

for fontsize use props on our line component tiny | small | medium |bigish | big | huge.
for fontFamily directly pass mono as prop

Comment on lines 13 to 15
const logs = useMemo(() => {
return services[serviceName]?.logs || [];
}, [serviceName, services]);
Copy link
Contributor

Choose a reason for hiding this comment

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

const logs = useMemo(() => services[serviceName]?.logs || [], [serviceName, services]);

Comment on lines 43 to 47
<Line
key={index}
marginBottom={'8px'}
fontSize={'14px'}
fontFamily={'monospace'}
Copy link
Contributor

Choose a reason for hiding this comment

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

same as other requested changes

fontSize={'14px'}
fontFamily={'monospace'}
>
<Line component="span" color="info.main">
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 you can do this like <Line color="info">


const { timestamp, ip, logLevel, message } = log;
return (
<Line medium marginBottom={'8px'} fontFamily={'monospace'}>
Copy link
Contributor

Choose a reason for hiding this comment

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

Try this<Line mono marginBottom={1}>


export const PlainLog = ({ log }: { log: string; index: number }) => {
return (
<Line medium marginBottom={'8px'} fontFamily={'monospace'}>
Copy link
Contributor

Choose a reason for hiding this comment

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

Try this <Line mono marginBottom={1}>, it should ideally give you the same effect.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

@shivam-bit
Copy link
Contributor

@vishalmishraa LGTM!🚀🚀🚀

@jayantbh
Copy link
Contributor

I think this doesn't need another round of reviews. We can go ahead and merge this. Any remaining changes will probably be minor and can be taken up in subsequent PRs.

@jayantbh jayantbh merged commit b524c08 into middlewarehq:main Oct 19, 2024
3 checks passed
@jayantbh
Copy link
Contributor

Congrats on getting this merged!
Good work my dude!

@vishalmishraa vishalmishraa deleted the system-logs-ui branch October 19, 2024 13:45
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.

4 participants