--- title: "Jam 03 - Exercise 4 - Log Analysis and Refactoring" tags: - 3 ๐Ÿงช in testing - 4 ๐Ÿฅณ done - jam03 - log analysis - refactoring - code organization --- <!-- markdownlint-disable line-length single-h1 no-inline-html --> <!-- markdownlint-configure-file { "ul-indent": { "indent": 4 }, "link-fragments": {"ignore_case": true} } --> {%hackmd dJZ5TulxSDKme-3fSY4Lbw %} # Exercise 4: Log Analysis and Refactoring ## Overview - Exercise 4 In this exercise, you'll build a log analyzer that can process different log formats and extract useful debugging information. You'll start with a basic implementation and then improve its design through refactoring. ## The Problem - Exercise 4 When debugging applications in production, you often need to analyze log files that come from different sources or components. Each might use a different format, but you need to extract and analyze information from all of them. **Problem**: Create a log analyzer that can: 1. Process logs in different formats 2. Extract timestamps and log levels 3. Find error patterns 4. Generate analysis reports Here are example log entries you'll need to handle: ```text [2024-02-13 15:23:45] ERROR: Database connection failed 15:23:45 | WARNING | Cache miss for key: user_123 DEBUG [Thread-5] 15:23:45 - Retrying operation... ``` Your analyzer should be able to: - Extract timestamps from any format - Identify log levels (ERROR, WARNING, DEBUG, etc.) - Count occurrences of each log level - Find all errors within a specific time range ## Quick Java Arrays Primer Since we'll be working with multiple log entries, we need to use arrays. You'll learn much more about Java arrays in your upcoming zyBooks chapter, but for now, you just need to know how to work with an array that's provided to you. If you're familiar with Python lists, Java arrays are similar but with some key differences: 1. **Declaring Arrays** (you won't be doing this today, but you will soon!): ```java // Fixed size array of strings String[] logs = new String[3]; // Array literal (size determined by elements) String[] logs = { "first log", "second log", "third log" }; ``` 2. **Accessing Elements**: ```java // Get element (zero-based indexing, just like Python) String firstLog = logs[0]; // Set element logs[1] = "new log message"; ``` 3. **Array Length**: ```java // Length is a property, not a method int size = logs.length; // Not logs.length() like Python's len() ``` 4. **Looping Through Arrays**: ```java // Traditional for loop for (int i = 0; i < logs.length; i++) { System.out.println(logs[i]); } // Enhanced for loop (like Python's for-in) for (String log : logs) { System.out.println(log); } ``` :::info ๐Ÿ”‘ **Key Differences from Python Arrays** - Arrays have fixed size - Use `.length` property instead of `len()` - No append/remove methods (use ArrayList for that) - Type must be declared (can't mix types) For this exercise, you'll only need to: 1. Access array elements with `logs[i]` 2. Get array length with `logs.length` 3. Loop through the array (either style shown above) ::: ## Required Steps - Implementation 1. Create `LogAnalyzer.java` in your jam03 package with this skeleton found after these steps: 2. Implement the basic log analysis functionality: - Process an array of log entries - Extract timestamps and log levels - Generate a summary report 3. Test your implementation with different log formats: ```text Sample Test Cases: - "[2024-02-13 15:23:45] ERROR: Database connection failed" - "15:23:45 | WARNING | Cache miss for key: user_123" - "DEBUG [Thread-5] 15:23:45 - Retrying operation..." ``` Note: This is also a rare code block where the copy button works! ```java public class LogAnalyzer { // Sample log entries to analyze static String[] logs = { "2024/02/10 23:15:23 INFO System startup initiated", "2024/02/10 23:15:45 DEBUG Loading configuration files", "2024/02/10 23:16:01 WARNING Configuration file outdated", "2024/02/10 23:17:30 INFO Using default configuration", "2024/02/11 02:23:12 DEBUG Initializing database connection", "2024/02/11 02:25:33 ERROR Database connection failed: timeout", "2024/02/11 02:25:45 DEBUG Connection attempt failed", "2024/02/11 08:30:00 INFO System health check", "2024/02/11 15:45:22 ERROR Database connection failed: authentication", "2024/02/11 15:46:00 DEBUG Connection attempt failed", "2024/02/11 16:20:15 WARNING Falling back to secondary database", "2024/02/11 21:05:22 INFO Connecting to secondary database", "2024/02/12 03:15:45 DEBUG Secondary connection established", "2024/02/12 07:45:30 INFO Beginning data synchronization", "2024/02/12 09:22:50 DEBUG Verifying data integrity", "2024/02/12 11:30:52 WARNING Data integrity check incomplete", "2024/02/12 13:15:55 ERROR Data corruption detected in user table", "2024/02/12 14:45:00 INFO Starting automatic repair", "2024/02/12 16:30:00 DEBUG Repair procedure initiated", "2024/02/12 19:25:02 WARNING Repair may take longer than expected", "2024/02/13 00:15:05 INFO Repair progress: 25% complete", "2024/02/13 04:20:08 DEBUG Validating repaired segments", "2024/02/13 05:45:10 ERROR Repair failed: insufficient permissions", "2024/02/13 06:30:12 WARNING System running in degraded mode", "2024/02/13 10:15:15 INFO Notifying system administrator", "2024/02/13 11:45:17 DEBUG Generating diagnostic report" }; static int totalLogs = logs.length; static String mostCommonLevel = null; static int errorCount = 0; static int warningCount = 0; static int infoCount = 0; static int debugCount = 0; public static void main(String[] args) { // Regex string and pattern to match the log format and capture the log type // TODO: Implement this // Process each log entry for (String log : logs) { // Use regex here to extract relevant parts // TODO: Implement this // Increment the appropriate count based on the log level // TODO: Implement this } // Determine the most common log level // TODO: Implement this // Generate the report // TODO: Implement this System.out.println("Log Analysis Report:"); } } ``` Your analyzer should output something like: ```text Log Analysis Report: - Total Logs: 26 - Errors: 4 - Warnings: 5 - Info: 8 - Debug: 9 - Most Common Level: DEBUG ``` > ๐Ÿ” **Checkpoint**: Before moving to refactoring, verify that: > > - Your analyzer can process all log formats > - You can identify different log levels > - You can generate a basic report that matches the expected output ## Required Steps - Refactoring Now that we have working code, let's learn how to make it better organized using a technique called "refactoring". Refactoring means improving code organization without changing what the code does. Let's start with a simple example: moving the report generation code into its own method. ### 1. Identify Code to Extract Look at your code and find all the lines that generate the report. It should be the series of `System.out.println` statements that print the log analysis results: ```java System.out.println("Log Analysis Report:"); System.out.println("- Total Logs: " + totalLogs); // ... more println statements ... ``` ### 2. Extract the Method 1. In IntelliJ, select ALL the println statements that generate the report 2. Press `Cmd/Ctrl + Alt + M` (or right-click โ†’ Refactor โ†’ Extract Method) 3. In the inline dialog that appears: - Name the method `printReport` - You can click on the gear to view settings if you want, but don't change anything - Press Enter if you didn't view the settings, Click refactor if you did ![image](https://hackmd.io/_uploads/BJSloAbKyl.png) :::info ๐Ÿ”ง **What Just Happened?** IntelliJ just: 1. Created a new method called `printReport` 2. Moved the selected code into that method 3. Added a call to `printReport()` where the code used to be 4. Made sure all the variables the code needs are available ::: ### 3. Verify the Changes Your code should now: 1. Have a new method called `printReport` 2. Have all the report println statements inside that method 3. Have a call to `printReport()` in the main method 4. Still produce exactly the same output as before If anything isn't working, you can undo the refactoring with `Cmd/Ctrl + Z` and try again. :::warning ๐Ÿšง **Common Problems** - If you didn't select all the println statements, some might be left behind - If the output changed at all, something went wrong - undo and try again - If you see red underlines, you might have missed some required variables - There is no need to pass the variables into the refactored method `printReport` because it includes class variables defined outside of the main function. ::: ### 4. Understanding the Benefits This simple change made our code better because: 1. The main method is now shorter and clearer 2. The report code is grouped together in one place 3. If we want to change how the report looks, we only need to change one place 4. The name `printReport` tells us exactly what that code does > ๐Ÿ” **Checkpoint**: After extracting the report method, verify that: > > - Your program still produces exactly the same output > - All the report println statements are in the new method > - The main method has a call to `printReport()` ### Try Two More Refactorings Now that you've successfully extracted the report generation, try extracting two more pieces of functionality. Here are some candidates to consider: 1. **Finding the Most Common Level** 2. **Processing a Single Log Entry** For each one you try: 1. Select the relevant code 2. Use `Cmd/Ctrl + Alt + M` just like before 3. Give it a descriptive name 4. Verify it still works :::warning ๐Ÿšง **Watch Out!** - Make sure you select ALL the related code - Test after each refactoring - Undo (`Cmd/Ctrl + Z`) if something goes wrong - Only continue if your program still produces exactly the same output ::: > ๐Ÿ” **Final Checkpoint**: After all refactoring, verify that: > > - Your program still produces exactly the same output > - Your main method is now shorter and clearer > - Each extracted method has a single, clear purpose > - The code is easier to understand ## Save Your Work - Exercise 4 Verify what files are uncommitted: ```bash git status ``` Stage your changes: ```bash git add src/main/java/jam03/LogAnalyzer.java ``` Commit your work: ```bash git commit -m "jam03: Implement and refactor LogAnalyzer" ``` Your working directory should now be clean.