0
# Level Translation
1
2
The level translation system provides bidirectional conversion between Java Util Logging (JUL) levels and Log4j levels, with support for custom levels and intelligent closest-match mapping for non-standard level values.
3
4
## Overview
5
6
Level translation is essential for proper log filtering and routing when bridging JUL to Log4j. The system provides:
7
8
- **Standard Mappings**: Predefined conversions between common JUL and Log4j levels
9
- **Custom Levels**: Special Log4j levels for JUL-specific levels (FINEST, CONFIG)
10
- **Dynamic Mapping**: Closest-match algorithm for custom or non-standard levels
11
- **Pluggable Converters**: Interface for custom level conversion strategies
12
13
## Standard Level Mappings
14
15
### JUL to Log4j
16
17
| JUL Level | Log4j Level | Notes |
18
|-----------|-------------|--------|
19
| ALL | ALL | Both represent unlimited logging |
20
| FINEST | FINEST | Custom Log4j level (TRACE + 100) |
21
| FINER | TRACE | Fine-grained debugging |
22
| FINE | DEBUG | Standard debugging |
23
| CONFIG | CONFIG | Custom Log4j level (INFO + 50) |
24
| INFO | INFO | Informational messages |
25
| WARNING | WARN | Warning conditions |
26
| SEVERE | ERROR | Error conditions |
27
| OFF | OFF | No logging |
28
29
### Log4j to JUL
30
31
| Log4j Level | JUL Level | Notes |
32
|-------------|-----------|--------|
33
| ALL | ALL | Both represent unlimited logging |
34
| FINEST | FINEST | Custom Log4j level maps back |
35
| TRACE | FINER | Fine-grained debugging |
36
| DEBUG | FINE | Standard debugging |
37
| CONFIG | CONFIG | Custom Log4j level maps back |
38
| INFO | INFO | Informational messages |
39
| WARN | WARNING | Warning conditions |
40
| ERROR | SEVERE | Error conditions |
41
| FATAL | SEVERE | Critical errors map to SEVERE |
42
| OFF | OFF | No logging |
43
44
## Usage
45
46
### Basic Level Conversion
47
48
```java
49
import org.apache.logging.log4j.jul.LevelTranslator;
50
import org.apache.logging.log4j.Level;
51
52
// Convert JUL level to Log4j level
53
java.util.logging.Level julLevel = java.util.logging.Level.INFO;
54
Level log4jLevel = LevelTranslator.toLevel(julLevel);
55
56
// Convert Log4j level to JUL level
57
Level log4jError = Level.ERROR;
58
java.util.logging.Level julSevere = LevelTranslator.toJavaLevel(log4jError);
59
60
// Handle null values (JUL allows null levels)
61
Level result = LevelTranslator.toLevel(null); // returns null
62
```
63
64
### Custom Level Handling
65
66
The translator automatically handles custom levels by finding the closest standard level:
67
68
```java
69
// Custom JUL level between FINE and INFO
70
java.util.logging.Level customLevel = new java.util.logging.Level("CUSTOM", 750) {};
71
72
// Automatically maps to closest Log4j level (DEBUG in this case)
73
Level mapped = LevelTranslator.toLevel(customLevel);
74
```
75
76
## API Reference
77
78
### LevelTranslator Class
79
80
```java { .api }
81
public final class LevelTranslator {
82
/**
83
* Custom Log4j level corresponding to JUL FINEST.
84
* Maps to TRACE + 100 for more specific than standard TRACE.
85
*/
86
public static final Level FINEST = Level.forName("FINEST", Level.TRACE.intLevel() + 100);
87
88
/**
89
* Custom Log4j level corresponding to JUL CONFIG.
90
* Maps to INFO + 50 for level between INFO and DEBUG.
91
*/
92
public static final Level CONFIG = Level.forName("CONFIG", Level.INFO.intLevel() + 50);
93
94
/**
95
* Converts a JUL logging Level to a Log4j logging Level.
96
* Uses pluggable LevelConverter for actual conversion.
97
*
98
* @param level JUL Level to convert, may be null per JUL specification
99
* @return converted Log4j Level or null if input was null
100
*/
101
public static Level toLevel(java.util.logging.Level level);
102
103
/**
104
* Converts a Log4j logging Level to a JUL logging Level.
105
* Uses pluggable LevelConverter for actual conversion.
106
*
107
* @param level Log4j Level to convert, must not be null
108
* @return converted JUL Level
109
*/
110
public static java.util.logging.Level toJavaLevel(Level level);
111
}
112
```
113
114
### LevelConverter Interface
115
116
```java { .api }
117
public interface LevelConverter {
118
/**
119
* Converts a JUL logging Level to a Log4j logging Level.
120
* Implementation should handle null values and custom levels.
121
*
122
* @param javaLevel JUL Level to convert, may be null per JUL specification
123
* @return converted Log4j Level or null if conversion not possible
124
*/
125
Level toLevel(java.util.logging.Level javaLevel);
126
127
/**
128
* Converts a Log4j logging Level to a JUL logging Level.
129
* Implementation should handle all standard and custom Log4j levels.
130
*
131
* @param level Log4j Level to convert, should not be null
132
* @return converted JUL Level or null if conversion not possible
133
*/
134
java.util.logging.Level toJavaLevel(Level level);
135
}
136
```
137
138
### DefaultLevelConverter Class
139
140
```java { .api }
141
public class DefaultLevelConverter implements LevelConverter {
142
/**
143
* Creates converter with standard level mappings and custom level support.
144
* Initializes bidirectional mapping tables and sorted level list for nearest matching.
145
*/
146
public DefaultLevelConverter();
147
148
/**
149
* Converts JUL level to Log4j level with custom level support.
150
* For unknown levels, finds the nearest mapped level by numeric value.
151
*
152
* @param javaLevel JUL Level to convert
153
* @return corresponding Log4j Level or nearest match for custom levels
154
*/
155
public Level toLevel(java.util.logging.Level javaLevel);
156
157
/**
158
* Converts Log4j level to JUL level using direct mapping.
159
*
160
* @param level Log4j Level to convert
161
* @return corresponding JUL Level or null if not mapped
162
*/
163
public java.util.logging.Level toJavaLevel(Level level);
164
}
165
```
166
167
## Custom Level Converter
168
169
You can provide a custom level converter implementation:
170
171
```java
172
public class CustomLevelConverter implements LevelConverter {
173
@Override
174
public Level toLevel(java.util.logging.Level javaLevel) {
175
if (javaLevel == null) return null;
176
177
// Custom conversion logic
178
if (javaLevel.getName().equals("AUDIT")) {
179
return Level.forName("AUDIT", 450); // Between INFO and WARN
180
}
181
182
// Fall back to default behavior
183
return new DefaultLevelConverter().toLevel(javaLevel);
184
}
185
186
@Override
187
public java.util.logging.Level toJavaLevel(Level level) {
188
// Custom reverse conversion
189
if ("AUDIT".equals(level.name())) {
190
return java.util.logging.Level.parse("AUDIT");
191
}
192
193
return new DefaultLevelConverter().toJavaLevel(level);
194
}
195
}
196
197
// Configure custom converter
198
System.setProperty("log4j.jul.levelConverter", "com.example.CustomLevelConverter");
199
```
200
201
## Custom Level Support
202
203
### Nearest Level Algorithm
204
205
For unknown JUL levels, the DefaultLevelConverter finds the nearest mapped level:
206
207
1. **Calculate Distance**: Compute numeric distance between custom level and all known levels
208
2. **Find Minimum**: Select the known level with smallest distance
209
3. **Cache Result**: Store mapping for future lookups
210
4. **Return Mapping**: Return the Log4j level corresponding to nearest JUL level
211
212
```java
213
// Example: Custom level with value 650 (between FINE=500 and CONFIG=700)
214
java.util.logging.Level custom = new java.util.logging.Level("CUSTOM", 650) {};
215
216
// Nearest is CONFIG (700), so maps to LevelTranslator.CONFIG
217
Level result = LevelTranslator.toLevel(custom);
218
```
219
220
### Performance Optimization
221
222
- **Caching**: Custom level mappings are cached after first lookup
223
- **Sorted Search**: Binary search through sorted level list
224
- **Static Initialization**: Standard mappings pre-populated at class loading
225
226
## Configuration
227
228
### Level Converter Selection
229
230
The level converter is selected during static initialization:
231
232
1. **Custom Converter**: If `log4j.jul.levelConverter` property is set
233
2. **Default Converter**: Falls back to DefaultLevelConverter
234
235
```java
236
// Set custom converter class
237
System.setProperty("log4j.jul.levelConverter", "com.example.MyLevelConverter");
238
```
239
240
### Requirements for Custom Converters
241
242
Custom LevelConverter implementations must:
243
- Have a public no-argument constructor
244
- Handle null input values gracefully
245
- Be thread-safe for concurrent access
246
- Provide reasonable mappings for standard levels
247
248
## Integration with Bridge Components
249
250
### LogManager Integration
251
- **Automatic Translation**: All JUL logging calls automatically use level translation
252
- **Bi-directional**: Supports both JUL→Log4j and Log4j→JUL conversions
253
- **Transparent**: No code changes required in application
254
255
### Bridge Handler Integration
256
- **Event Processing**: Translates levels for each published LogRecord
257
- **Level Propagation**: Uses reverse translation for propagating Log4j levels to JUL
258
- **Performance**: Efficient translation with minimal overhead
259
260
## Error Handling
261
262
The level translation system handles various error conditions:
263
264
```java
265
// Null handling (per JUL specification)
266
Level result = LevelTranslator.toLevel(null); // returns null
267
268
// Custom converter loading failures
269
try {
270
CustomConverter converter = LoaderUtil.newCheckedInstanceOf(className, LevelConverter.class);
271
} catch (Exception e) {
272
LOGGER.error("Could not create custom LevelConverter [{}].", className, e);
273
// Falls back to DefaultLevelConverter
274
}
275
276
// Graceful degradation for unknown levels
277
Level fallback = nearestLevel(unknownLevel); // finds closest match
278
```
279
280
## Thread Safety
281
282
All level translation components are fully thread-safe:
283
- **Static Fields**: FINEST and CONFIG levels are immutable
284
- **Concurrent Maps**: DefaultLevelConverter uses ConcurrentHashMap for caching
285
- **Immutable Mappings**: Standard level mappings are immutable after initialization
286
- **Atomic Operations**: Level lookup and caching operations are atomic