aboutsummaryrefslogtreecommitdiffstats
path: root/logserver/src/main/java/com/yahoo/logserver/handlers/archive/LogWriter.java
blob: 832a72aaccc4aa2a3633c09fdec77489bd096cd5 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.logserver.handlers.archive;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.logging.Logger;

import java.util.logging.Level;

/**
 * This class is not thread-safe.
 *
 * @author Bjorn Borud
 */
public class LogWriter {
    private static final Logger log = Logger.getLogger(LogWriter.class.getName());

    private long bytesWritten = 0;
    private int generation;
    private int maxSize = 20 * (1024 * 1024);
    private final int resumeLimit = 95;
    private final int resumeLimitSize = (maxSize * resumeLimit / 100);
    private File currentFile;
    private Writer writer;
    private final String prefix;
    private final FilesArchived archive;

    public LogWriter(String prefix, int maxSize, FilesArchived archive) throws IOException {
        this.prefix = prefix;
        this.maxSize = maxSize;
        this.archive = archive;
        this.generation = archive.highestGen(prefix);
        writer = nextWriter();
        archive.triggerMaintenance();
    }

    /**
     * This is called when we want to rotate the output file to
     * start writing the next file.  There are two scenarios when
     * we do this:
     * <UL>
     * <LI> initial case, when we have no file
     * <LI> when we have filled the file and want to rotate it
     * </UL>
     */
    private Writer nextWriter() throws IOException {
        close();
        int maxAttempts = 1000;
        while (maxAttempts-- > 0) {
            String name = prefix + "-" + generation++;
            File f = new File(name);

            // make sure directory exists
            File dir = f.getParentFile();
            if (! dir.exists()) {
                dir.mkdirs();
            }

            // if compressed version exists we skip it
            if ((new File(name + ".gz").exists())
                    || (new File(name + ".bz2").exists())) {
                continue;
            }

            // if file does not exist we have a winner
            if (! f.exists()) {
                log.log(Level.FINE, () -> "nextWriter, new file: " + name);
                currentFile = f;
                bytesWritten = 0;
                return new FileWriter(f, true);
            }

            // just skip over directories for now
            if (! f.isFile()) {
                log.fine("nextWriter, " + name + " is a directory, skipping");
                continue;
            }

            // if the size is < resumeSizeLimit then we open it
            if (f.length() < resumeLimitSize) {
                log.fine("nextWriter, resuming " + name + ", length was " + f.length());
                currentFile = f;
                bytesWritten = f.length();
                return new FileWriter(f, true);
            } else {

                log.fine("nextWriter, not resuming " + name
                                 + " because it is bigger than "
                                 + resumeLimit
                                 + " percent of max");
            }
        }

        throw new RuntimeException("Unable to create next log file");
    }

    public void write(String str) throws IOException {
        if (writer == null) {
            writer = nextWriter();
            archive.triggerMaintenance();
        }

        bytesWritten += str.length();
        writer.write(str, 0, str.length());

        if (bytesWritten >= maxSize) {
            log.fine("logfile '"
                             + currentFile.getAbsolutePath()
                             + "' full, rotating");
            writer = nextWriter();
            archive.triggerMaintenance();
        }
    }


    public synchronized void flush() throws IOException {
        if (writer != null) {
            writer.flush();
        }
    }

    public synchronized void close() throws IOException {
        if (writer != null) {
            writer.flush();
            writer.close();
            writer = null;
        }
    }
}