or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

bridge-handler.mdindex.mdlevel-translation.mdlogger-adapters.mdlogmanager-integration.md

logger-adapters.mddocs/

0

# Logger Adapters

1

2

The logger adapter system provides a pluggable architecture for bridging JUL Logger instances to different Log4j implementations. It automatically selects the most appropriate adapter based on available Log4j components and supports custom adapter implementations.

3

4

## Overview

5

6

Logger adapters bridge the gap between JUL's Logger interface and Log4j's logging infrastructure. The system provides:

7

8

- **Automatic Selection**: Chooses optimal adapter based on available Log4j components

9

- **Custom Override**: Supports custom adapter implementations via configuration

10

- **Context Management**: Handles LoggerContext lifecycle and isolation

11

- **Performance Optimization**: Minimizes overhead through efficient logger creation

12

13

## Adapter Hierarchy

14

15

```

16

AbstractLoggerAdapter (abstract base)

17

├── ApiLoggerAdapter (log4j-api only)

18

└── CoreLoggerAdapter (log4j-core available)

19

```

20

21

## Adapter Selection Process

22

23

The LogManager selects adapters in the following priority order:

24

25

1. **Custom Adapter**: If `log4j.jul.LoggerAdapter` property is set

26

2. **CoreLoggerAdapter**: If log4j-core is available on classpath

27

3. **ApiLoggerAdapter**: Fallback when only log4j-api is available

28

29

```java

30

// Selection logic

31

String customAdapterClass = PropertiesUtil.getProperties()

32

.getStringProperty(Constants.LOGGER_ADAPTOR_PROPERTY);

33

34

if (customAdapterClass != null) {

35

// Try to load custom adapter

36

adapter = LoaderUtil.newCheckedInstanceOf(customAdapterClass, AbstractLoggerAdapter.class);

37

} else {

38

// Default to ApiLoggerAdapter (CoreLoggerAdapter detection is automatic)

39

adapter = new ApiLoggerAdapter();

40

}

41

```

42

43

## Usage

44

45

### Automatic Selection

46

47

Most applications use automatic adapter selection:

48

49

```java

50

// Set LogManager - adapter selection is automatic

51

System.setProperty("java.util.logging.manager", "org.apache.logging.log4j.jul.LogManager");

52

53

// Standard JUL usage - adapter works transparently

54

java.util.logging.Logger logger = java.util.logging.Logger.getLogger("com.example");

55

logger.info("Message logged through selected adapter");

56

```

57

58

### Custom Adapter Configuration

59

60

```java

61

// Configure custom adapter via system property

62

System.setProperty("log4j.jul.LoggerAdapter", "com.example.CustomLoggerAdapter");

63

64

// Or via log4j2.properties

65

// log4j.jul.LoggerAdapter=com.example.CustomLoggerAdapter

66

```

67

68

## API Reference

69

70

### AbstractLoggerAdapter

71

72

```java { .api }

73

public abstract class AbstractLoggerAdapter extends org.apache.logging.log4j.spi.AbstractLoggerAdapter<Logger> {

74

/**

75

* Gets the appropriate LoggerContext for the current execution context.

76

* Considers classloader dependency and caller class for context isolation.

77

*

78

* @return LoggerContext for creating loggers

79

*/

80

protected LoggerContext getContext();

81

82

/**

83

* Creates a new Logger instance for the given name and context.

84

* Implementation varies based on available Log4j components.

85

*

86

* @param name the logger name

87

* @param context the LoggerContext

88

* @return new Logger instance

89

*/

90

protected abstract Logger newLogger(String name, LoggerContext context);

91

}

92

```

93

94

### ApiLoggerAdapter

95

96

```java { .api }

97

public class ApiLoggerAdapter extends AbstractLoggerAdapter {

98

/**

99

* Creates ApiLogger instances using only log4j-api components.

100

* Uses MessageFormatMessageFactory for JUL-style message formatting.

101

*

102

* @param name the logger name

103

* @param context the LoggerContext

104

* @return new ApiLogger instance

105

*/

106

protected Logger newLogger(String name, LoggerContext context);

107

}

108

```

109

110

### CoreLoggerAdapter

111

112

```java { .api }

113

public class CoreLoggerAdapter extends AbstractLoggerAdapter {

114

/**

115

* Creates enhanced Logger instances when log4j-core is available.

116

* Returns CoreLogger for full functionality or ApiLogger as fallback.

117

*

118

* @param name the logger name

119

* @param context the LoggerContext

120

* @return new CoreLogger or ApiLogger instance

121

*/

122

protected Logger newLogger(String name, LoggerContext context);

123

}

124

```

125

126

## Logger Implementations

127

128

### ApiLogger

129

130

The ApiLogger provides JUL Logger implementation using only log4j-api:

131

132

```java

133

// Created by ApiLoggerAdapter

134

java.util.logging.Logger logger = java.util.logging.Logger.getLogger("example");

135

136

// Full JUL API support with some limitations

137

logger.info("Standard logging works");

138

logger.log(java.util.logging.Level.WARNING, "Parameterized message: {0}", value);

139

140

// Limitations due to log4j-api constraints

141

logger.setLevel(java.util.logging.Level.DEBUG); // Limited support

142

Logger parent = logger.getParent(); // May not be accurate

143

```

144

145

**ApiLogger Features:**

146

- Complete JUL Logger interface implementation

147

- Delegates to underlying Log4j ExtendedLogger

148

- MessageFormatMessageFactory for JUL-style parameter substitution

149

- Limited `setLevel()` and `getParent()` support due to API constraints

150

151

### CoreLogger

152

153

The CoreLogger provides enhanced functionality when log4j-core is available:

154

155

```java

156

// Created by CoreLoggerAdapter when log4j-core is present

157

java.util.logging.Logger logger = java.util.logging.Logger.getLogger("example");

158

159

// Enhanced functionality with full JUL API support

160

logger.setLevel(java.util.logging.Level.DEBUG); // Full support

161

Logger parent = logger.getParent(); // Accurate parent relationships

162

logger.setFilter(customFilter); // Full filter support

163

```

164

165

**CoreLogger Features:**

166

- Full JUL Logger interface implementation

167

- Complete level management support

168

- Accurate parent-child relationships

169

- Enhanced performance through log4j-core integration

170

171

## Context Management

172

173

### LoggerContext Selection

174

175

The adapter system handles LoggerContext selection based on:

176

177

1. **ClassLoader Dependency**: Uses appropriate context for classloader isolation

178

2. **Caller Context**: Determines caller class for context selection

179

3. **Factory Configuration**: Respects LoggerFactory configuration

180

181

```java

182

// Context selection logic in AbstractLoggerAdapter

183

protected LoggerContext getContext() {

184

return getContext(

185

LogManager.getFactory().isClassLoaderDependent()

186

? StackLocatorUtil.getCallerClass(java.util.logging.LogManager.class)

187

: null

188

);

189

}

190

```

191

192

### Context Isolation

193

194

Different contexts allow for:

195

- **Separate Configurations**: Each context can have independent log4j2.xml

196

- **ClassLoader Isolation**: Prevents configuration conflicts between applications

197

- **Resource Management**: Proper cleanup when contexts are destroyed

198

199

## Custom Adapter Implementation

200

201

Create custom adapters for specialized requirements:

202

203

```java

204

public class CustomLoggerAdapter extends AbstractLoggerAdapter {

205

206

@Override

207

protected Logger newLogger(String name, LoggerContext context) {

208

// Custom logger creation logic

209

ExtendedLogger log4jLogger = context.getLogger(name, customMessageFactory);

210

211

// Return custom logger implementation

212

return new CustomJulLogger(log4jLogger);

213

}

214

215

// Custom logger implementation

216

private static class CustomJulLogger extends java.util.logging.Logger {

217

private final ExtendedLogger delegate;

218

219

CustomJulLogger(ExtendedLogger delegate) {

220

super(delegate.getName(), null);

221

this.delegate = delegate;

222

}

223

224

@Override

225

public void info(String msg) {

226

// Custom logging behavior

227

delegate.info("[CUSTOM] " + msg);

228

}

229

230

// Implement other Logger methods...

231

}

232

}

233

```

234

235

### Custom Adapter Requirements

236

237

Custom adapters must:

238

- Extend `AbstractLoggerAdapter`

239

- Implement `newLogger(String, LoggerContext)` method

240

- Have a public no-argument constructor

241

- Be thread-safe for concurrent logger creation

242

- Handle context lifecycle properly

243

244

## Performance Considerations

245

246

### Adapter Selection Overhead

247

- **One-time Cost**: Adapter selection occurs once during LogManager construction

248

- **Fast Path**: No selection overhead during normal logging operations

249

- **Caching**: Created loggers are cached by the adapter registry

250

251

### Logger Creation Performance

252

- **Lazy Creation**: Loggers created on-demand when first requested

253

- **Context Reuse**: LoggerContext instances are reused across loggers

254

- **Message Factory Reuse**: Static MessageFactory instances minimize object creation

255

256

### Memory Management

257

- **Weak References**: Logger registry uses weak references to prevent memory leaks

258

- **Context Cleanup**: Proper cleanup when LoggerContext is shut down

259

- **Resource Sharing**: Shared resources between logger instances

260

261

## Error Handling

262

263

### Adapter Loading Failures

264

265

```java

266

try {

267

adapter = LoaderUtil.newCheckedInstanceOf(className, AbstractLoggerAdapter.class);

268

LOGGER.info("Using custom LoggerAdapter [{}].", className);

269

} catch (Exception e) {

270

LOGGER.error("Specified LoggerAdapter [{}] is incompatible.", className, e);

271

// Falls back to default adapter

272

adapter = new ApiLoggerAdapter();

273

}

274

```

275

276

### Logger Creation Failures

277

278

- **Graceful Degradation**: Falls back to simpler logger implementation

279

- **Error Logging**: Reports issues through StatusLogger

280

- **NoOp Fallback**: Returns NoOpLogger for recursive creation attempts

281

282

### Context Issues

283

284

- **Context Unavailable**: Creates logger with default context

285

- **Configuration Errors**: Uses default configuration if context configuration fails

286

- **Resource Cleanup**: Proper cleanup prevents resource leaks

287

288

## Thread Safety

289

290

All adapter components are fully thread-safe:

291

292

- **Adapter Selection**: One-time initialization during LogManager construction

293

- **Logger Creation**: Thread-safe logger creation and caching

294

- **Context Access**: Safe concurrent access to LoggerContext

295

- **Registry Operations**: Thread-safe operations in underlying registry

296

297

## Integration Points

298

299

### With LogManager

300

- **Automatic Integration**: LogManager automatically uses selected adapter

301

- **Configuration**: Respects adapter configuration from system properties

302

- **Lifecycle**: Adapter lifecycle tied to LogManager lifecycle

303

304

### With Log4j Components

305

- **API Integration**: Works with any log4j-api implementation

306

- **Core Integration**: Enhanced features when log4j-core is available

307

- **Context Integration**: Proper integration with LoggerContext lifecycle

308

309

### With JUL Framework

310

- **Standard Interface**: Provides standard JUL Logger interface

311

- **Handler Compatibility**: Works with existing JUL handlers when needed

312

- **Level Compatibility**: Integrates with JUL level system through translation