-
Notifications
You must be signed in to change notification settings - Fork 136
/
LogRecordBuffer.java
176 lines (149 loc) · 5.51 KB
/
LogRecordBuffer.java
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
/*
* Copyright (c) 2022, 2024 Eclipse Foundation and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package org.glassfish.main.jul.handler;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import org.glassfish.main.jul.record.GlassFishLogRecord;
/**
* @author David Matejcek
*/
class LogRecordBuffer {
private final int capacity;
private final int maxWait;
private final ArrayBlockingQueue<GlassFishLogRecord> pendingRecords;
/**
* The buffer for log records.
* <p>
* If it is full and another record is comming to the buffer, the record will wait until the
* buffer would have a free capacity, maybe forever.
* <p>
* See also the another constructor.
*
* @param capacity capacity of the buffer.
*/
public LogRecordBuffer(final int capacity) {
this(capacity, 0);
}
/**
* The buffer for log records.
* <p>
* If it is full and another record is comming to the buffer, the record will wait until the
* buffer would have a free capacity, but only for a maxWait seconds.
* <p>
* If the buffer would not have free capacity even after the maxWait time, the buffer will be
* automatically cleared, the incomming record will be lost and there will be a stacktrace in
* standard error output - but that may be redirected to JUL again, so this must be reliable.
* <ul>
* <li>After this error handling procedure the logging will be available again in full capacity
* but it's previous unprocessed log records would be lost.
* <li>If the maxWait is lower than 1, the calling thread would be blocked until some records would
* be processed. It may remain blocked forever.
* </ul>
*
* @param capacity capacity of the buffer.
* @param maxWait maximal time in seconds to wait for the free capacity. If < 1, can wait
* forever.
*/
public LogRecordBuffer(final int capacity, final int maxWait) {
this.capacity = capacity;
this.maxWait = maxWait;
this.pendingRecords = new ArrayBlockingQueue<>(capacity);
}
/**
* @return true if there are not pending records to provide.
*/
public boolean isEmpty() {
return this.pendingRecords.isEmpty();
}
public int getSize() {
return this.pendingRecords.size();
}
public int getCapacity() {
return this.capacity;
}
/**
* Waits for a record or thread interrupt signal
*
* @return {@link GlassFishLogRecord} or null if interrupted.
*/
public GlassFishLogRecord pollOrWait() {
try {
return this.pendingRecords.take();
} catch (final InterruptedException e) {
return null;
}
}
/**
* @return null if there are no pending records, first in the buffer otherwise.
*/
public GlassFishLogRecord poll() {
return this.pendingRecords.poll();
}
public void add(final GlassFishLogRecord record) {
if (maxWait > 0) {
addWithTimeout(record);
} else {
addWithUnlimitedWaiting(record);
}
}
/**
* This prevents deadlock - when the waiting is not successful, it forcibly drops all waiting records.
* Logs an error after that.
*/
private void addWithTimeout(final GlassFishLogRecord record) {
try {
if (this.pendingRecords.offer(record)) {
return;
}
if (this.pendingRecords.offer(record, this.maxWait, TimeUnit.SECONDS)) {
return;
}
} catch (final InterruptedException e) {
// do nothing
}
this.pendingRecords.clear();
// note: the record is not meaningful for the message. The cause is in another place.
this.pendingRecords.offer(new GlassFishLogRecord(Level.SEVERE, //
this + ": The buffer was forcibly cleared after " + maxWait + " s timeout for adding another log record." //
+ " Log records were lost." //
+ " It might be caused by a recursive deadlock," //
+ " you can increase the capacity or the timeout to avoid this.", false));
}
/**
* This prevents losing any records, but may end up in deadlock if the capacity is reached.
*/
private void addWithUnlimitedWaiting(final GlassFishLogRecord record) {
if (this.pendingRecords.offer(record)) {
return;
}
try {
Thread.yield();
this.pendingRecords.put(record);
} catch (final InterruptedException e) {
// do nothing
}
}
/**
* Returns simple name of this class and size/capacity
*
* @return ie.: LogRecordBuffer@2b488078[5/10000]
*/
@Override
public String toString() {
return super.toString() + "[" + getSize() + "/" + getCapacity() + "]";
}
}