Skip to content

Commit 51f6cec

Browse files
committed
add log level shifting
fixes #3736
1 parent bec2e9b commit 51f6cec

File tree

3 files changed

+85
-31
lines changed

3 files changed

+85
-31
lines changed

opengrok-indexer/src/main/java/org/opengrok/indexer/history/BoundaryChangesets.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,14 +102,12 @@ public synchronized List<String> getBoundaryChangesetIDs(String sinceRevision) t
102102
reset();
103103

104104
Level logLevel = Level.FINE;
105-
if (RuntimeEnvironment.getInstance().isPrintProgress()) {
106-
logLevel = Level.INFO;
107-
}
108105

109106
LOGGER.log(logLevel, "getting boundary changesets for {0}", repository);
110107
Statistics stat = new Statistics();
111108

112-
try (Progress progress = new Progress(LOGGER, String.format("changesets visited of %s", this.repository))) {
109+
try (Progress progress = new Progress(LOGGER, String.format("changesets visited of %s", this.repository),
110+
logLevel)) {
113111
repository.accept(sinceRevision, this::visit, progress);
114112
}
115113

opengrok-indexer/src/main/java/org/opengrok/indexer/util/Progress.java

Lines changed: 68 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,15 @@
2323
*/
2424
package org.opengrok.indexer.util;
2525

26+
import org.jetbrains.annotations.VisibleForTesting;
2627
import org.opengrok.indexer.configuration.RuntimeEnvironment;
2728

29+
import java.util.Arrays;
30+
import java.util.Comparator;
2831
import java.util.HashMap;
32+
import java.util.List;
2933
import java.util.Map;
34+
import java.util.TreeMap;
3035
import java.util.concurrent.atomic.AtomicLong;
3136
import java.util.logging.Level;
3237
import java.util.logging.Logger;
@@ -35,26 +40,37 @@
3540
* Progress reporting via logging. The idea is that for anything that has a set of items
3641
* to go through, it will ping an instance of this class for each item completed.
3742
* This class will then log based on the number of pings. The bigger the progress,
38-
* the higher log level ({@link Level} value) will be used.
43+
* the higher log level ({@link Level} value) will be used. The default base level is {@code Level.INFO}.
3944
*/
4045
public class Progress implements AutoCloseable {
4146
private final Logger logger;
4247
private final Long totalCount;
4348
private final String suffix;
4449

4550
private final AtomicLong currentCount = new AtomicLong();
46-
private final Map<Level, Integer> levelCount;
51+
private final Map<Level, Integer> levelCountMap = new TreeMap<>(Comparator.comparingInt(Level::intValue).reversed());
4752
private Thread loggerThread = null;
4853
private volatile boolean run;
4954

55+
private final Level baseLogLevel;
56+
5057
private final Object sync = new Object();
5158

5259
/**
5360
* @param logger logger instance
5461
* @param suffix string suffix to identify the operation
5562
*/
5663
public Progress(Logger logger, String suffix) {
57-
this(logger, suffix, -1);
64+
this(logger, suffix, -1, Level.INFO);
65+
}
66+
67+
/**
68+
* @param logger logger instance
69+
* @param suffix string suffix to identify the operation
70+
* @param logLevel base log level
71+
*/
72+
public Progress(Logger logger, String suffix, Level logLevel) {
73+
this(logger, suffix, -1, logLevel);
5874
}
5975

6076
/**
@@ -63,22 +79,44 @@ public Progress(Logger logger, String suffix) {
6379
* @param totalCount total count
6480
*/
6581
public Progress(Logger logger, String suffix, long totalCount) {
82+
this(logger, suffix, totalCount, Level.INFO);
83+
}
84+
85+
/**
86+
* @param logger logger instance
87+
* @param suffix string suffix to identify the operation
88+
* @param totalCount total count
89+
* @param logLevel base log level
90+
*/
91+
public Progress(Logger logger, String suffix, long totalCount, Level logLevel) {
6692
this.logger = logger;
6793
this.suffix = suffix;
94+
this.baseLogLevel = logLevel;
6895

6996
if (totalCount < 0) {
7097
this.totalCount = null;
7198
} else {
7299
this.totalCount = totalCount;
73100
}
74101

75-
levelCount = Map.of(Level.INFO, 100,
76-
Level.FINE, 50,
77-
Level.FINER, 10,
78-
Level.FINEST, 1);
79-
80-
// Assuming printProgress configuration setting cannot be changed on the fly.
81-
if (RuntimeEnvironment.getInstance().isPrintProgress()) {
102+
// Note: Level.CONFIG is missing
103+
final List<Level> standardLevels = Arrays.asList(Level.OFF, Level.SEVERE, Level.WARNING, Level.INFO,
104+
Level.FINE, Level.FINER, Level.FINEST, Level.ALL);
105+
int i = standardLevels.indexOf(baseLogLevel);
106+
for (int num : new int[]{100, 50, 10, 1}) {
107+
Level level = standardLevels.get(i);
108+
if (level == null) {
109+
break;
110+
}
111+
levelCountMap.put(level, num);
112+
if (num == 1) {
113+
break;
114+
}
115+
i++;
116+
}
117+
118+
// Assuming the printProgress configuration setting cannot be changed on the fly.
119+
if (!baseLogLevel.equals(Level.OFF) && RuntimeEnvironment.getInstance().isPrintProgress()) {
82120
spawnLogThread();
83121
}
84122
}
@@ -120,21 +158,7 @@ private void logLoop() {
120158

121159
// Do not log if there was no progress.
122160
if (cachedCount < currentCount) {
123-
if (currentCount <= 1 || (totalCount != null && currentCount == totalCount)) {
124-
currentLevel = Level.INFO;
125-
} else {
126-
if (lastLoggedChunk.getOrDefault(Level.INFO, -1L) <
127-
currentCount / levelCount.get(Level.INFO)) {
128-
currentLevel = Level.INFO;
129-
} else if (lastLoggedChunk.getOrDefault(Level.FINE, -1L) <
130-
currentCount / levelCount.get(Level.FINE)) {
131-
currentLevel = Level.FINE;
132-
} else if (lastLoggedChunk.getOrDefault(Level.FINER, -1L) <
133-
currentCount / levelCount.get(Level.FINER)) {
134-
currentLevel = Level.FINER;
135-
}
136-
}
137-
161+
currentLevel = getLevel(lastLoggedChunk, currentCount, currentLevel);
138162
logIt(lastLoggedChunk, currentCount, currentLevel);
139163
}
140164

@@ -159,9 +183,27 @@ private void logLoop() {
159183
}
160184
}
161185

186+
@VisibleForTesting
187+
Level getLevel(Map<Level, Long> lastLoggedChunk, long currentCount, Level currentLevel) {
188+
// The intention is to log the initial and final count at the base log level.
189+
if (currentCount <= 1 || (totalCount != null && currentCount == totalCount)) {
190+
currentLevel = baseLogLevel;
191+
} else {
192+
// Set the log level based on the "buckets".
193+
for (Level level : levelCountMap.keySet()) {
194+
if (lastLoggedChunk.getOrDefault(level, -1L) <
195+
currentCount / levelCountMap.get(level)) {
196+
currentLevel = level;
197+
break;
198+
}
199+
}
200+
}
201+
return currentLevel;
202+
}
203+
162204
private void logIt(Map<Level, Long> lastLoggedChunk, long currentCount, Level currentLevel) {
163205
if (logger.isLoggable(currentLevel)) {
164-
lastLoggedChunk.put(currentLevel, currentCount / levelCount.get(currentLevel));
206+
lastLoggedChunk.put(currentLevel, currentCount / levelCountMap.get(currentLevel));
165207
StringBuilder stringBuilder = new StringBuilder();
166208
stringBuilder.append("Progress: ");
167209
stringBuilder.append(currentCount);

opengrok-indexer/src/test/java/org/opengrok/indexer/util/ProgressTest.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
*/
1919

2020
/*
21-
* Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
21+
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
2222
*/
2323
package org.opengrok.indexer.util;
2424

@@ -33,12 +33,14 @@
3333
import java.util.concurrent.Executors;
3434
import java.util.concurrent.Future;
3535
import java.util.concurrent.TimeUnit;
36+
import java.util.logging.Level;
3637
import java.util.logging.Logger;
3738

3839
import static org.junit.jupiter.api.Assertions.assertNotNull;
3940
import static org.junit.jupiter.api.Assertions.assertSame;
4041
import static org.mockito.ArgumentMatchers.any;
4142
import static org.mockito.ArgumentMatchers.anyString;
43+
import static org.mockito.ArgumentMatchers.same;
4244
import static org.mockito.Mockito.atLeast;
4345
import static org.mockito.Mockito.times;
4446

@@ -49,6 +51,14 @@ public static void setup() {
4951
RuntimeEnvironment.getInstance().setPrintProgress(true);
5052
}
5153

54+
@Test
55+
void testShifting() {
56+
final Logger logger = Mockito.mock(Logger.class);
57+
try (Progress progress = new Progress(logger, "xxx")) {
58+
assertNotNull(progress);
59+
}
60+
}
61+
5262
@Test
5363
public void testProgress() throws InterruptedException {
5464
final Logger logger = Mockito.mock(Logger.class);
@@ -85,6 +95,10 @@ public void testProgress() throws InterruptedException {
8595
assertSame(loggerThread.getState(), Thread.State.TERMINATED);
8696

8797
Mockito.verify(logger, times(totalCount)).log(any(), anyString());
98+
Mockito.verify(logger, atLeast(1)).log(same(Level.INFO), anyString());
99+
Mockito.verify(logger, atLeast(2)).log(same(Level.FINE), anyString());
100+
Mockito.verify(logger, atLeast(10)).log(same(Level.FINER), anyString());
101+
Mockito.verify(logger, atLeast(50)).log(same(Level.FINEST), anyString());
88102
}
89103

90104
@Test

0 commit comments

Comments
 (0)