You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: chapters/ch04.2-writing-logs.md
+50-50Lines changed: 50 additions & 50 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,7 +2,7 @@
2
2
3
3
## Writing logs
4
4
5
-
> The code for the entire chapter can be found [at the code/chapter_04.2 directory](/code/chapter_04.2/)
5
+
> The code for the entire chapter can be found [at the src/chapter_04.2 directory](/src/chapter_04.2/)
6
6
7
7
We've covered how to build the core utility helpers that will help us construct our logging library in a more modular way. However, we haven't gotten to the fun part of actually writing logs yet.
8
8
@@ -30,23 +30,23 @@ When logging is performed synchronously, each log entry is written immediately t
30
30
31
31
By decoupling the logging process from the main application logic, we can achieve several advantages:
32
32
33
-
-**Improved Performance:** Asynchronous logging allows the main application thread to continue executing without waiting for log writes to complete. This can be crucial for applications that require high responsiveness and throughput.
33
+
-**Improved Performance:** Asynchronous logging allows the main application thread to continue executing without waiting for log writes to complete. This can be crucial for applications that require high responsiveness and throughput.
34
34
35
-
-**Reduced I/O Overhead:** Writing log messages to disk can be an I/O-intensive operation. By batching multiple log messages together and writing them in one go, you reduce the frequency of disk I/O operations, which can improve efficiency.
35
+
-**Reduced I/O Overhead:** Writing log messages to disk can be an I/O-intensive operation. By batching multiple log messages together and writing them in one go, you reduce the frequency of disk I/O operations, which can improve efficiency.
36
36
37
-
-**Better Resource Utilization:** Asynchronous logging allows you to optimize resource utilization, such as managing concurrent log writes, handling errors without disrupting the main flow, and efficiently managing file handles.
37
+
-**Better Resource Utilization:** Asynchronous logging allows you to optimize resource utilization, such as managing concurrent log writes, handling errors without disrupting the main flow, and efficiently managing file handles.
38
38
39
-
-**Enhanced Scalability:** Applications with multiple threads or processes benefit from asynchronous logging because it minimizes contention for resources like the log file. This is particularly valuable in scenarios where multiple components are concurrently generating log messages
39
+
-**Enhanced Scalability:** Applications with multiple threads or processes benefit from asynchronous logging because it minimizes contention for resources like the log file. This is particularly valuable in scenarios where multiple components are concurrently generating log messages
40
40
41
41
### 4. Getting Caller Information (Module and Line Number)
42
42
43
43
Including caller information, such as the file name and line number from which a log message originated, can significantly enhance the effectiveness of our logging library. This feature provides contextual insight into where specific events occurred in the codebase, making it easier to identify the source of issues and troubleshoot them.
44
44
45
45
When an application encounters an error or unexpected behavior, having access to the module and line number associated with the log message allows developers to:
46
46
47
-
-Quickly locate the exact location in the code where the event occurred.
48
-
-Understand the sequence of events leading up to the issue.
49
-
-Make precise code adjustments to fix problems.
47
+
- Quickly locate the exact location in the code where the event occurred.
48
+
- Understand the sequence of events leading up to the issue.
49
+
- Make precise code adjustments to fix problems.
50
50
51
51
Implementing this feature might involve using techniques from the programming language's stack trace or introspection mechanisms. Here's a high-level overview of how you could achieve this:
52
52
@@ -229,9 +229,9 @@ The "Don't Repeat Yourself" (DRY) principle is a basic concept in software devel
229
229
230
230
DRY encourages developers to write clean, efficient, and modular code by:
231
231
232
-
-Reducing the chances of errors: Duplicated code increases the chances of mistakes or bugs when changes are made in one place but not in others.
233
-
-Simplifying maintenance: When a change is required, you only need to update the code in one place, making it easier to keep your codebase up-to-date and consistent.
234
-
-Enhancing readability: Code that is free from unnecessary duplication is easier to understand and follow, making it more accessible to other developers.
232
+
- Reducing the chances of errors: Duplicated code increases the chances of mistakes or bugs when changes are made in one place but not in others.
233
+
- Simplifying maintenance: When a change is required, you only need to update the code in one place, making it easier to keep your codebase up-to-date and consistent.
234
+
- Enhancing readability: Code that is free from unnecessary duplication is easier to understand and follow, making it more accessible to other developers.
235
235
236
236
Although following the DRY principle is generally beneficial, there can be situations where duplication might not necessarily be a bad thing. Not every instance of code repetition needs to be eliminated, and striving for absolute **DRY**ness in all cases might lead to overcomplicated solutions or premature abstraction.
237
237
@@ -428,37 +428,37 @@ There's a lot going on in this method. Let's break it down.
428
428
429
429
4.`.replace(/[\.:]+/g, "-")` is a regular expression operation. Let's break down the regex:
430
430
431
-
- the square brackets [] are used to define a character class. A character class is a way to specify a set of characters from which a single character can match. For example:
431
+
- the square brackets [] are used to define a character class. A character class is a way to specify a set of characters from which a single character can match. For example:
432
432
433
-
-[abc] matches any single character that is either 'a', 'b', or 'c'.
434
-
-[0-9] matches any single digit from 0 to 9.
435
-
-[a-zA-Z] matches any single alphabetical character, regardless of case.
433
+
-[abc] matches any single character that is either 'a', 'b', or 'c'.
434
+
-[0-9] matches any single digit from 0 to 9.
435
+
-[a-zA-Z] matches any single alphabetical character, regardless of case.
436
436
437
-
You can also use special characters within character classes to match certain character types, like `\d` for digits, `\w` for word characters, `\s` for whitespace, etc.
437
+
You can also use special characters within character classes to match certain character types, like `\d` for digits, `\w` for word characters, `\s` for whitespace, etc.
438
438
439
-
In this case we are looking for all the dot (`.`) characters and colon (`:`) characters in the string.
439
+
In this case we are looking for all the dot (`.`) characters and colon (`:`) characters in the string.
440
440
441
-
-`\.` matches a literal dot character. Since the dot (`.`) is a special character in regular expression, known as a wildcard. It matches any single character except for a newline character (`\n`). This is useful when you want to match any character, which is often used in pattern matching.
441
+
-`\.` matches a literal dot character. Since the dot (`.`) is a special character in regular expression, known as a wildcard. It matches any single character except for a newline character (`\n`). This is useful when you want to match any character, which is often used in pattern matching.
442
442
443
-
However, in this case, we want to match a literal dot character in the string (the dot in the date-time format "00.000Z"). To achieve this, we need to escape the dot character by preceding it with a backslash (`\`). When you place a backslash before a special character, it indicates that you want to match the literal character itself, rather than its special meaning.
443
+
However, in this case, we want to match a literal dot character in the string (the dot in the date-time format "00.000Z"). To achieve this, we need to escape the dot character by preceding it with a backslash (`\`). When you place a backslash before a special character, it indicates that you want to match the literal character itself, rather than its special meaning.
444
444
445
-
-`+` matches one or more of any character except newline. We match for all the characters following the dot (.) and the (:) character.
445
+
-`+` matches one or more of any character except newline. We match for all the characters following the dot (.) and the (:) character.
446
446
447
-
-`/g` is the global flag, indicating that the replacement should be applied to all occurrences of the pattern.
447
+
-`/g` is the global flag, indicating that the replacement should be applied to all occurrences of the pattern.
448
448
449
-
- So, the regex `[\.:]+` matches the dot or colon and all the repeated occurences of these 2 characters.
449
+
- So, the regex `[\.:]+` matches the dot or colon and all the repeated occurences of these 2 characters.
450
450
451
-
- The replacement `"-"` removes the dot and all characters after it and replaces it with a hyphen (`-`).
451
+
- The replacement `"-"` removes the dot and all characters after it and replaces it with a hyphen (`-`).
452
452
453
453
5. The result of the `replace` operation is a modified version of the ISO string, which now includes only the date and time portion, without the fractional seconds.
454
454
455
455
6.`.log` is appended to the modified date and time string to form the final log file name.
456
456
457
457
7.`await fs.open(file_name, "a+")` opens or creates the log file using the `fs.open` function with "a+" flag. We talked about the modes in a [previous chapter](https://github.com/ishtms/learn-nodejs-hard-way/blob/master/chapters/ch03-working-with-files.md#flag-argument)
458
458
459
-
- If the file doesn't exist, it will be created.
460
-
- If the file exists, data can be appended to it.
461
-
- The `"a+"` mode allows both reading and appending. Appending means, we begin writing to the end of the file instead of from the 1st line. However, if the file is empty, it starts from the beginning.
459
+
- If the file doesn't exist, it will be created.
460
+
- If the file exists, data can be appended to it.
461
+
- The `"a+"` mode allows both reading and appending. Appending means, we begin writing to the end of the file instead of from the 1st line. However, if the file is empty, it starts from the beginning.
462
462
463
463
This code initializes the logger by creating or opening a log file with a name based on the current date. The regular expression is used to remove the fractional seconds from the ISO date string, and the resulting string is used as part of the log file name. This allows for creating a new log file every time the `init` method is called, typically representing logs for a specific time period.
464
464
@@ -516,9 +516,9 @@ How do we fix it? Simply by `await`ing the `init()` method.
@@ -641,15 +641,15 @@ But, require is not just a function!
641
641
642
642
For example:
643
643
644
-
-`require.resolve('module-name')`: Returns the path of the module without actually loading it.
645
-
-`require.cache`: Provides access to the cache of loaded modules.
646
-
-`require.main`: Provides access to the `Module` object representing the entry script loaded when the Node.js process launched. This is exactly what we need.
644
+
-`require.resolve('module-name')`: Returns the path of the module without actually loading it.
645
+
-`require.cache`: Provides access to the cache of loaded modules.
646
+
-`require.main`: Provides access to the `Module` object representing the entry script loaded when the Node.js process launched. This is exactly what we need.
647
647
648
648
The reason `require` might feel like both an object and a function is because JavaScript allows functions to have properties. You can test this yourself
Let's go through the `check_and_create_dir` function's code line by line.
688
688
689
689
1. The `path.resolve()` function creates an absolute path by combining a sequence of paths or path segments.
690
690
691
-
It processes the sequence from right to left, with each subsequent path added to the beginning until an absolute path is created. For example, if the path segments are `/foo`, `/bar`, and `baz`, calling `path.resolve('/foo', '/bar', 'baz')` would return `/bar/baz` because `'/bar' + '/' + 'baz'` creates an absolute path, but `'baz'` does not.
691
+
It processes the sequence from right to left, with each subsequent path added to the beginning until an absolute path is created. For example, if the path segments are `/foo`, `/bar`, and `baz`, calling `path.resolve('/foo', '/bar', 'baz')` would return `/bar/baz` because `'/bar' + '/' + 'baz'` creates an absolute path, but `'baz'` does not.
692
692
693
-
If, after processing all the segments in the given path, an absolute path hasn't been created yet, then the current working directory is used instead.
693
+
If, after processing all the segments in the given path, an absolute path hasn't been created yet, then the current working directory is used instead.
694
694
695
695
2. The `require.main.path` holds the absolute path to the directory of the entry point. The entry point is the `test.js` in our case. Or whatever file you specify while running `node file_name.js` command.
0 commit comments