LCOV - code coverage report
Current view: top level - pgm/util - LogFileCompressor.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 37 73 50.7 %
Date: 2022-11-19 15:00:39 Functions: 11 14 78.6 %

          Line data    Source code
       1             : // Copyright (C) 2009 The Android Open Source Project
       2             : //
       3             : // Licensed under the Apache License, Version 2.0 (the "License");
       4             : // you may not use this file except in compliance with the License.
       5             : // You may obtain a copy of the License at
       6             : //
       7             : // http://www.apache.org/licenses/LICENSE-2.0
       8             : //
       9             : // Unless required by applicable law or agreed to in writing, software
      10             : // distributed under the License is distributed on an "AS IS" BASIS,
      11             : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12             : // See the License for the specific language governing permissions and
      13             : // limitations under the License.
      14             : 
      15             : package com.google.gerrit.pgm.util;
      16             : 
      17             : import static java.util.concurrent.TimeUnit.HOURS;
      18             : import static java.util.concurrent.TimeUnit.MILLISECONDS;
      19             : 
      20             : import com.google.common.flogger.FluentLogger;
      21             : import com.google.common.io.ByteStreams;
      22             : import com.google.gerrit.extensions.events.LifecycleListener;
      23             : import com.google.gerrit.lifecycle.LifecycleModule;
      24             : import com.google.gerrit.server.config.GerritServerConfig;
      25             : import com.google.gerrit.server.config.SitePaths;
      26             : import com.google.gerrit.server.git.WorkQueue;
      27             : import com.google.inject.Inject;
      28             : import java.io.IOException;
      29             : import java.io.InputStream;
      30             : import java.io.OutputStream;
      31             : import java.nio.file.DirectoryStream;
      32             : import java.nio.file.Files;
      33             : import java.nio.file.Path;
      34             : import java.time.LocalDateTime;
      35             : import java.time.ZoneId;
      36             : import java.time.temporal.ChronoUnit;
      37             : import java.util.concurrent.Future;
      38             : import java.util.zip.GZIPOutputStream;
      39             : import org.eclipse.jgit.lib.Config;
      40             : 
      41             : /** Compresses the old error logs. */
      42             : public class LogFileCompressor implements Runnable {
      43         138 :   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
      44             : 
      45         138 :   public static class LogFileCompressorModule extends LifecycleModule {
      46             :     @Override
      47             :     protected void configure() {
      48         138 :       listener().to(Lifecycle.class);
      49         138 :     }
      50             :   }
      51             : 
      52             :   static class Lifecycle implements LifecycleListener {
      53             :     private final WorkQueue queue;
      54             :     private final LogFileCompressor compressor;
      55             :     private final boolean enabled;
      56             : 
      57             :     @Inject
      58         138 :     Lifecycle(WorkQueue queue, LogFileCompressor compressor, @GerritServerConfig Config config) {
      59         138 :       this.queue = queue;
      60         138 :       this.compressor = compressor;
      61         138 :       this.enabled = config.getBoolean("log", "compress", true);
      62         138 :     }
      63             : 
      64             :     @Override
      65             :     public void start() {
      66         138 :       if (!enabled) {
      67           0 :         return;
      68             :       }
      69             :       // compress log once and then schedule compression every day at 11:00pm
      70         138 :       queue.getDefaultQueue().execute(compressor);
      71         138 :       ZoneId zone = ZoneId.systemDefault();
      72         138 :       LocalDateTime now = LocalDateTime.now(zone);
      73         138 :       long milliSecondsUntil11pm =
      74         138 :           now.until(now.withHour(23).withMinute(0).withSecond(0).withNano(0), ChronoUnit.MILLIS);
      75             :       @SuppressWarnings("unused")
      76         138 :       Future<?> possiblyIgnoredError =
      77             :           queue
      78         138 :               .getDefaultQueue()
      79         138 :               .scheduleAtFixedRate(
      80         138 :                   compressor, milliSecondsUntil11pm, HOURS.toMillis(24), MILLISECONDS);
      81         138 :     }
      82             : 
      83             :     @Override
      84         138 :     public void stop() {}
      85             :   }
      86             : 
      87             :   private final Path logs_dir;
      88             : 
      89             :   @Inject
      90         138 :   LogFileCompressor(SitePaths site) {
      91         138 :     logs_dir = resolve(site.logs_dir);
      92         138 :   }
      93             : 
      94             :   private static Path resolve(Path p) {
      95             :     try {
      96         138 :       return p.toRealPath().normalize();
      97           0 :     } catch (IOException e) {
      98           0 :       return p.toAbsolutePath().normalize();
      99             :     }
     100             :   }
     101             : 
     102             :   @Override
     103             :   public void run() {
     104             :     try {
     105         138 :       if (!Files.isDirectory(logs_dir)) {
     106           0 :         return;
     107             :       }
     108         138 :       try (DirectoryStream<Path> list = Files.newDirectoryStream(logs_dir)) {
     109         138 :         for (Path entry : list) {
     110           8 :           if (!isLive(entry) && !isCompressed(entry) && isLogFile(entry)) {
     111           0 :             compress(entry);
     112             :           }
     113           8 :         }
     114           0 :       } catch (IOException e) {
     115           0 :         logger.atSevere().withCause(e).log("Error listing logs to compress in %s", logs_dir);
     116         138 :       }
     117           0 :     } catch (Exception e) {
     118           0 :       logger.atSevere().withCause(e).log("Failed to compress log files: %s", e.getMessage());
     119         138 :     }
     120         138 :   }
     121             : 
     122             :   private boolean isLive(Path entry) {
     123           8 :     String name = entry.getFileName().toString();
     124           8 :     return name.endsWith("_log")
     125           0 :         || name.endsWith(".log")
     126           0 :         || name.endsWith(".run")
     127           0 :         || name.endsWith(".pid")
     128           8 :         || name.endsWith(".json");
     129             :   }
     130             : 
     131             :   private boolean isCompressed(Path entry) {
     132           0 :     String name = entry.getFileName().toString();
     133           0 :     return name.endsWith(".gz") //
     134           0 :         || name.endsWith(".zip") //
     135           0 :         || name.endsWith(".bz2");
     136             :   }
     137             : 
     138             :   private boolean isLogFile(Path entry) {
     139           0 :     return Files.isRegularFile(entry);
     140             :   }
     141             : 
     142             :   private void compress(Path src) {
     143           0 :     Path dst = src.resolveSibling(src.getFileName() + ".gz");
     144           0 :     Path tmp = src.resolveSibling(".tmp." + src.getFileName());
     145             :     try {
     146           0 :       try (InputStream in = Files.newInputStream(src);
     147           0 :           OutputStream out = new GZIPOutputStream(Files.newOutputStream(tmp))) {
     148           0 :         ByteStreams.copy(in, out);
     149             :       }
     150           0 :       tmp.toFile().setReadOnly();
     151             :       try {
     152           0 :         Files.move(tmp, dst);
     153           0 :       } catch (IOException e) {
     154           0 :         throw new IOException("Cannot rename " + tmp + " to " + dst, e);
     155           0 :       }
     156           0 :       Files.delete(src);
     157           0 :     } catch (IOException e) {
     158           0 :       logger.atSevere().withCause(e).log("Cannot compress %s", src);
     159             :       try {
     160           0 :         Files.deleteIfExists(tmp);
     161           0 :       } catch (IOException e2) {
     162           0 :         logger.atWarning().withCause(e2).log("Failed to delete temporary log file %s", tmp);
     163           0 :       }
     164           0 :     }
     165           0 :   }
     166             : 
     167             :   @Override
     168             :   public String toString() {
     169         132 :     return "Log File Compressor";
     170             :   }
     171             : }

Generated by: LCOV version 1.16+git.20220603.dfeb750