Skip to content

feature: Remove logback dependency by implementing native log rotation#321

Merged
xerial merged 4 commits intomainfrom
claude/remove-logback-dependency-nzzEL
Jan 16, 2026
Merged

feature: Remove logback dependency by implementing native log rotation#321
xerial merged 4 commits intomainfrom
claude/remove-logback-dependency-nzzEL

Conversation

@xerial
Copy link
Copy Markdown
Member

@xerial xerial commented Jan 16, 2026

Replace logback-core based log rotation with a custom Java standard library
implementation to reduce external dependencies. The new implementation
provides the same functionality:

  • Time-based (daily) log rotation
  • Size-based rotation when maxSizeInBytes is exceeded
  • Gzip compression of rotated files
  • Automatic cleanup of old files based on maxNumberOfFiles
  • Temp file recovery for orphaned files

Replace logback-core based log rotation with a custom Java standard library
implementation to reduce external dependencies. The new implementation
provides the same functionality:

- Time-based (daily) log rotation
- Size-based rotation when maxSizeInBytes is exceeded
- Gzip compression of rotated files
- Automatic cleanup of old files based on maxNumberOfFiles
- Temp file recovery for orphaned files
@github-actions github-actions Bot added the feature New feature label Jan 16, 2026
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @xerial, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly refactors the logging infrastructure by replacing the logback-core dependency with a custom, native log rotation solution. The primary goal is to minimize external dependencies while maintaining robust log management capabilities, including various rotation strategies, compression, and file lifecycle management.

Highlights

  • Dependency Reduction: The external logback-core dependency has been completely removed from the project, reducing the overall dependency footprint.
  • Native Log Rotation Implementation: A custom log rotation mechanism has been implemented using standard Java libraries, replacing the functionality previously provided by Logback.
  • Comprehensive Rotation Features: The new native implementation supports time-based (daily) and size-based log rotation, Gzip compression of rotated files, automatic cleanup of old files, and recovery for orphaned temporary files.
  • Refactored LogRotationHandler: The LogRotationHandler class has been entirely rewritten to manage file writing, rotation checks, file compression, and cleanup without relying on external logging frameworks.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request successfully replaces the logback-core dependency with a custom log rotation implementation using the Java standard library. The new implementation is well-structured and covers the intended features like time-based and size-based rotation, compression, and cleanup. My review focuses on improving the robustness of the new implementation. I've identified a critical issue with how the date is determined for existing log files, which would break daily rotation on application restarts. Additionally, there are a couple of medium-severity issues regarding silent error handling during the rotation process. Addressing these points will make the new log rotation handler more reliable.

Comment on lines +92 to +94
val append = logFile.exists()
currentFileSize = if append then logFile.length() else 0L
currentDate = LocalDate.now()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

critical

There is a bug in the log file initialization logic. When appending to an existing log file (e.g., after an application restart), currentDate is always set to LocalDate.now(). This will prevent daily rotation from triggering correctly if the application restarts after midnight, as it will continue to append to the previous day's log file instead of rotating it.

To fix this, currentDate should be determined from the last modified timestamp of the existing log file. This ensures that if the log file is from a previous day, it will be rotated on the first new log message.

Please also add the necessary imports: import java.time.Instant and import java.time.ZoneId.

        val append = logFile.exists()
        if (append) {
          currentFileSize = logFile.length()
          currentDate = java.time.Instant.ofEpochMilli(logFile.lastModified()).atZone(java.time.ZoneId.systemDefault()).toLocalDate()
        } else {
          currentFileSize = 0L
          currentDate = LocalDate.now()
        }

private def rotate(): Unit =
// Close current writer
if writer != null then
Try(writer.close())
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The Try(writer.close()) call currently swallows any exceptions. If closing the writer fails, it could be because the buffered data couldn't be flushed to disk, leading to silent data loss. It's important to handle this potential failure and report an error, similar to how it's handled in the close() method of the handler for consistency and robustness.

      Try(writer.close()) match {
        case Failure(e) =>
          reportError(null, toException(e), ErrorManager.CLOSE_FAILURE)
        case Success(_) =>
        // Closing succeeded
      }

Comment on lines +155 to +157
if logFile.renameTo(rotatedFile) then
// Compress the rotated file
compressFile(rotatedFile)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The logFile.renameTo(rotatedFile) operation can fail due to various reasons like file permissions or locks from other processes. Currently, such a failure is silent. This means the log file won't be rotated and will continue to grow, while rotation will be re-attempted on every new log message. To aid in diagnosing such issues, it would be beneficial to report an error when the rename operation fails.

      if logFile.renameTo(rotatedFile) then
        // Compress the rotated file
        compressFile(rotatedFile)
      else
        reportError(s"Failed to rename ${logFile} to ${rotatedFile}", null, ErrorManager.GENERIC_FAILURE)

xerial and others added 2 commits January 15, 2026 23:16
Fix compilation errors by adding explicit [File] type parameter to
Array.empty calls in LogRotationHandler. Also apply scalafmt formatting.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Use file's last modified date for currentDate when appending to existing
  log file, ensuring proper daily rotation after application restart
- Handle writer.close() errors in rotate() method
- Report error when file rename fails during rotation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@xerial
Copy link
Copy Markdown
Member Author

xerial commented Jan 16, 2026

/gemini review

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request successfully removes the logback dependency and replaces it with a custom, native Java implementation for log rotation. The new implementation covers time-based (daily) and size-based rotation, Gzip compression, and automatic cleanup of old files, aligning with the stated goals of reducing external dependencies while maintaining functionality. The changes in build.sbt correctly reflect the dependency removal. The new LogRotationHandler class introduces a comprehensive custom solution for log management.

Comment on lines +191 to +195
s"${new File(fileNameStem).getName}-${dateStr}\\.(\\d+)${java
.util
.regex
.Pattern
.quote(logFileExt)}\\.gz".r
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

The fileNameStem (or rather, its getName() part) is directly embedded into the regex pattern without being escaped. If fileNameStem contains characters that are special in regular expressions (e.g., ., *, +, ?), this could lead to incorrect matching of log files during findNextIndex. For example, if fileNameStem is my.app.log, the . characters would be treated as wildcards, potentially matching unintended files. It's best practice to quote any part of a string that is dynamically inserted into a regex pattern to ensure it's treated literally.

      s"${java.util.regex.Pattern.quote(new File(fileNameStem).getName)}-${dateStr}\\.(\\d+)${java
          .util
          .regex
          .Pattern
          .quote(logFileExt)}\\.gz".r

Quote the fileNameStem when building regex pattern to handle filenames
with special regex characters (e.g., dots in "my.app.log").

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@xerial xerial merged commit a2283e1 into main Jan 16, 2026
10 checks passed
@xerial xerial deleted the claude/remove-logback-dependency-nzzEL branch January 16, 2026 07:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants