or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

core-logging.mdindex.mdmarkers.mdmessage-system.mdperformance-features.mdspi.mdstatus-system.mdthread-context.md

spi.mddocs/

0

# Service Provider Interface

1

2

Extension points for custom logger implementations, context factories, and integration with different logging backends. Enables Log4j API to work with various implementations.

3

4

## Capabilities

5

6

### LoggerContext Interface

7

8

Anchor point for logging implementations that manages loggers and their lifecycle.

9

10

```java { .api }

11

/**

12

* Anchor point for logging implementations; manages loggers

13

*/

14

public interface LoggerContext {

15

/** Get logger by name */

16

ExtendedLogger getLogger(String name);

17

18

/** Get logger with custom message factory */

19

ExtendedLogger getLogger(String name, MessageFactory messageFactory);

20

21

/** Get logger by class */

22

ExtendedLogger getLogger(Class<?> cls);

23

24

/** Check if logger exists */

25

boolean hasLogger(String name);

26

27

/** Check if logger exists with specific message factory */

28

boolean hasLogger(String name, MessageFactory messageFactory);

29

30

/** Check if logger exists by class */

31

boolean hasLogger(Class<?> cls);

32

33

/** Get external context object */

34

Object getExternalContext();

35

36

/** Get stored object by key */

37

Object getObject(String key);

38

39

/** Store object with key */

40

Object putObject(String key, Object value);

41

42

/** Remove stored object */

43

Object removeObject(String key);

44

45

/** Empty array constant */

46

LoggerContext[] EMPTY_ARRAY = new LoggerContext[0];

47

}

48

```

49

50

**Usage Examples:**

51

52

```java

53

public void demonstrateLoggerContext() {

54

// Get current logger context

55

LoggerContext context = LogManager.getContext();

56

57

// Get loggers from context

58

ExtendedLogger logger1 = context.getLogger("com.example.MyClass");

59

ExtendedLogger logger2 = context.getLogger(MyClass.class);

60

61

// Check logger existence

62

boolean exists = context.hasLogger("com.example.MyClass");

63

64

// Store and retrieve context-specific objects

65

context.putObject("config", new MyConfiguration());

66

MyConfiguration config = (MyConfiguration) context.getObject("config");

67

68

// External context (implementation-specific)

69

Object externalCtx = context.getExternalContext();

70

}

71

72

// Custom logger context implementation

73

public class CustomLoggerContext implements LoggerContext {

74

private final Map<String, ExtendedLogger> loggers = new ConcurrentHashMap<>();

75

private final Map<String, Object> objects = new ConcurrentHashMap<>();

76

private final Object externalContext;

77

78

public CustomLoggerContext(Object externalContext) {

79

this.externalContext = externalContext;

80

}

81

82

@Override

83

public ExtendedLogger getLogger(String name) {

84

return loggers.computeIfAbsent(name, this::createLogger);

85

}

86

87

@Override

88

public ExtendedLogger getLogger(String name, MessageFactory messageFactory) {

89

String key = name + "#" + messageFactory.getClass().getName();

90

return loggers.computeIfAbsent(key, k -> createLogger(name, messageFactory));

91

}

92

93

@Override

94

public boolean hasLogger(String name) {

95

return loggers.containsKey(name);

96

}

97

98

@Override

99

public Object getExternalContext() {

100

return externalContext;

101

}

102

103

@Override

104

public Object getObject(String key) {

105

return objects.get(key);

106

}

107

108

@Override

109

public Object putObject(String key, Object value) {

110

return objects.put(key, value);

111

}

112

113

@Override

114

public Object removeObject(String key) {

115

return objects.remove(key);

116

}

117

118

private ExtendedLogger createLogger(String name) {

119

return new CustomExtendedLogger(name);

120

}

121

122

private ExtendedLogger createLogger(String name, MessageFactory messageFactory) {

123

return new CustomExtendedLogger(name, messageFactory);

124

}

125

}

126

```

127

128

### LoggerContextFactory Interface

129

130

Factory for creating and managing LoggerContext instances across different classloaders and configurations.

131

132

```java { .api }

133

/**

134

* Factory for creating LoggerContext instances

135

*/

136

public interface LoggerContextFactory {

137

/**

138

* Get context with full control over selection criteria

139

* @param fqcn Fully qualified class name of the caller

140

* @param loader ClassLoader to associate with context

141

* @param externalContext External context object

142

* @param currentContext Whether to return current context

143

*/

144

LoggerContext getContext(String fqcn, ClassLoader loader,

145

Object externalContext, boolean currentContext);

146

147

/**

148

* Get context with configuration location and name

149

* @param fqcn Fully qualified class name of the caller

150

* @param loader ClassLoader to associate with context

151

* @param externalContext External context object

152

* @param currentContext Whether to return current context

153

* @param configLocation Configuration file location

154

* @param name Context name

155

*/

156

LoggerContext getContext(String fqcn, ClassLoader loader,

157

Object externalContext, boolean currentContext,

158

URI configLocation, String name);

159

160

/** Remove a context */

161

void removeContext(LoggerContext context);

162

163

/** Check if context exists */

164

boolean hasContext(String fqcn, ClassLoader loader, boolean currentContext);

165

166

/**

167

* Shutdown contexts

168

* @param fqcn Fully qualified class name

169

* @param loader ClassLoader

170

* @param currentContext Whether to shutdown current context

171

* @param allContexts Whether to shutdown all contexts

172

*/

173

void shutdown(String fqcn, ClassLoader loader,

174

boolean currentContext, boolean allContexts);

175

176

/** Check if factory is dependent on ClassLoader */

177

boolean isClassLoaderDependent();

178

}

179

```

180

181

**Usage Examples:**

182

183

```java

184

// Custom logger context factory

185

public class CustomLoggerContextFactory implements LoggerContextFactory {

186

private final Map<String, LoggerContext> contexts = new ConcurrentHashMap<>();

187

188

@Override

189

public LoggerContext getContext(String fqcn, ClassLoader loader,

190

Object externalContext, boolean currentContext) {

191

String key = createContextKey(loader, externalContext);

192

return contexts.computeIfAbsent(key, k -> createContext(externalContext));

193

}

194

195

@Override

196

public LoggerContext getContext(String fqcn, ClassLoader loader,

197

Object externalContext, boolean currentContext,

198

URI configLocation, String name) {

199

String key = createContextKey(loader, externalContext, configLocation, name);

200

return contexts.computeIfAbsent(key, k -> createContext(externalContext, configLocation, name));

201

}

202

203

@Override

204

public void removeContext(LoggerContext context) {

205

contexts.values().remove(context);

206

}

207

208

@Override

209

public boolean hasContext(String fqcn, ClassLoader loader, boolean currentContext) {

210

String key = createContextKey(loader, null);

211

return contexts.containsKey(key);

212

}

213

214

@Override

215

public void shutdown(String fqcn, ClassLoader loader,

216

boolean currentContext, boolean allContexts) {

217

if (allContexts) {

218

contexts.clear();

219

} else {

220

String key = createContextKey(loader, null);

221

contexts.remove(key);

222

}

223

}

224

225

@Override

226

public boolean isClassLoaderDependent() {

227

return true; // This factory creates different contexts per ClassLoader

228

}

229

230

private String createContextKey(ClassLoader loader, Object externalContext) {

231

return loader.toString() + "#" + (externalContext != null ? externalContext.toString() : "null");

232

}

233

234

private String createContextKey(ClassLoader loader, Object externalContext,

235

URI configLocation, String name) {

236

return createContextKey(loader, externalContext) + "#" + configLocation + "#" + name;

237

}

238

239

private LoggerContext createContext(Object externalContext) {

240

return new CustomLoggerContext(externalContext);

241

}

242

243

private LoggerContext createContext(Object externalContext, URI configLocation, String name) {

244

return new CustomLoggerContext(externalContext, configLocation, name);

245

}

246

}

247

248

// Usage in application

249

public class ContextFactoryUsage {

250

251

public void demonstrateContextFactory() {

252

LoggerContextFactory factory = new CustomLoggerContextFactory();

253

254

// Get context for current classloader

255

LoggerContext context1 = factory.getContext(

256

this.getClass().getName(),

257

Thread.currentThread().getContextClassLoader(),

258

null,

259

true

260

);

261

262

// Get context with specific configuration

263

URI configUri = URI.create("classpath:log4j2-test.xml");

264

LoggerContext context2 = factory.getContext(

265

this.getClass().getName(),

266

getClass().getClassLoader(),

267

"test-app",

268

false,

269

configUri,

270

"test-context"

271

);

272

273

// Check context existence

274

boolean exists = factory.hasContext(

275

this.getClass().getName(),

276

getClass().getClassLoader(),

277

true

278

);

279

280

// Shutdown specific context

281

factory.removeContext(context1);

282

283

// Shutdown all contexts

284

factory.shutdown(null, null, false, true);

285

}

286

}

287

```

288

289

### ExtendedLogger Interface

290

291

Extended logger interface providing additional methods for logger implementations.

292

293

```java { .api }

294

/**

295

* Extended logger interface for implementations

296

*/

297

public interface ExtendedLogger extends Logger {

298

/**

299

* Log message if the specified level is enabled

300

* @param fqcn Fully qualified class name of the caller

301

* @param level Log level

302

* @param marker Marker

303

* @param message Message

304

* @param throwable Throwable

305

*/

306

void logIfEnabled(String fqcn, Level level, Marker marker,

307

String message, Throwable throwable);

308

309

/** Log with message supplier if level enabled */

310

void logIfEnabled(String fqcn, Level level, Marker marker,

311

Supplier<?> messageSupplier, Throwable throwable);

312

313

/** Log with message object if level enabled */

314

void logIfEnabled(String fqcn, Level level, Marker marker,

315

Object message, Throwable throwable);

316

317

/** Log with Message object if level enabled */

318

void logIfEnabled(String fqcn, Level level, Marker marker,

319

Message message, Throwable throwable);

320

321

/** Log parameterized message if level enabled */

322

void logIfEnabled(String fqcn, Level level, Marker marker,

323

String message, Object... params);

324

325

/** Log parameterized message with specific parameter count if level enabled */

326

void logIfEnabled(String fqcn, Level level, Marker marker,

327

String message, Object p0);

328

void logIfEnabled(String fqcn, Level level, Marker marker,

329

String message, Object p0, Object p1);

330

// ... additional overloads for performance

331

332

/** Always log regardless of level (for internal use) */

333

void logMessage(String fqcn, Level level, Marker marker,

334

Message message, Throwable throwable);

335

}

336

```

337

338

**Usage Examples:**

339

340

```java

341

// Custom extended logger implementation

342

public class CustomExtendedLogger extends AbstractLogger implements ExtendedLogger {

343

private final String name;

344

private final Level level;

345

346

public CustomExtendedLogger(String name) {

347

this.name = name;

348

this.level = Level.INFO; // Default level

349

}

350

351

@Override

352

public void logIfEnabled(String fqcn, Level level, Marker marker,

353

String message, Throwable throwable) {

354

if (isEnabled(level, marker)) {

355

logMessage(fqcn, level, marker,

356

getMessageFactory().newMessage(message), throwable);

357

}

358

}

359

360

@Override

361

public void logIfEnabled(String fqcn, Level level, Marker marker,

362

String message, Object... params) {

363

if (isEnabled(level, marker)) {

364

logMessage(fqcn, level, marker,

365

getMessageFactory().newMessage(message, params), null);

366

}

367

}

368

369

@Override

370

public void logMessage(String fqcn, Level level, Marker marker,

371

Message message, Throwable throwable) {

372

// Actual logging implementation

373

StringBuilder sb = new StringBuilder();

374

sb.append("[").append(level).append("] ");

375

sb.append(name).append(" - ");

376

if (marker != null) {

377

sb.append(marker.getName()).append(" ");

378

}

379

sb.append(message.getFormattedMessage());

380

381

System.out.println(sb.toString());

382

383

if (throwable != null) {

384

throwable.printStackTrace(System.out);

385

}

386

}

387

388

@Override

389

public boolean isEnabled(Level level, Marker marker) {

390

return level.isMoreSpecificThan(this.level);

391

}

392

393

@Override

394

public Level getLevel() {

395

return level;

396

}

397

398

@Override

399

public String getName() {

400

return name;

401

}

402

}

403

404

// Usage of extended logger features

405

public class ExtendedLoggerUsage {

406

private static final String FQCN = ExtendedLoggerUsage.class.getName();

407

408

public void demonstrateExtendedLogger() {

409

ExtendedLogger logger = (ExtendedLogger) LogManager.getLogger();

410

411

// Use extended logger methods directly

412

logger.logIfEnabled(FQCN, Level.INFO, null, "Direct extended logging", (Throwable) null);

413

414

// Parameterized logging with FQCN

415

logger.logIfEnabled(FQCN, Level.DEBUG, null, "User {} performed action {}", "alice", "login");

416

417

// With marker and throwable

418

Marker securityMarker = MarkerManager.getMarker("SECURITY");

419

Exception ex = new RuntimeException("test");

420

logger.logIfEnabled(FQCN, Level.ERROR, securityMarker, "Security violation", ex);

421

422

// Force logging regardless of level (use with caution)

423

Message forceMsg = new SimpleMessage("Forced log message");

424

logger.logMessage(FQCN, Level.TRACE, null, forceMsg, null);

425

}

426

}

427

```

428

429

### AbstractLogger Base Class

430

431

Abstract base class providing common logger functionality that implementations can extend.

432

433

```java { .api }

434

/**

435

* Abstract base class for Logger implementations

436

*/

437

public abstract class AbstractLogger implements ExtendedLogger, Serializable {

438

/** Default message factory */

439

public static final MessageFactory DEFAULT_MESSAGE_FACTORY = ParameterizedMessageFactory.INSTANCE;

440

441

/** Default flow message factory */

442

public static final FlowMessageFactory DEFAULT_FLOW_MESSAGE_FACTORY = DefaultFlowMessageFactory.INSTANCE;

443

444

/** Get message factory for this logger */

445

@Override

446

public MessageFactory getMessageFactory() {

447

return DEFAULT_MESSAGE_FACTORY;

448

}

449

450

/** Get flow message factory for this logger */

451

@Override

452

public FlowMessageFactory getFlowMessageFactory() {

453

return DEFAULT_FLOW_MESSAGE_FACTORY;

454

}

455

456

// Abstract methods that implementations must provide

457

@Override

458

public abstract Level getLevel();

459

460

@Override

461

public abstract boolean isEnabled(Level level, Marker marker, String message, Throwable throwable);

462

463

@Override

464

public abstract boolean isEnabled(Level level, Marker marker, String message);

465

466

@Override

467

public abstract boolean isEnabled(Level level, Marker marker, String message, Object... params);

468

469

@Override

470

public abstract boolean isEnabled(Level level, Marker marker, Object message, Throwable throwable);

471

472

@Override

473

public abstract boolean isEnabled(Level level, Marker marker, Message message, Throwable throwable);

474

475

@Override

476

public abstract void logMessage(String fqcn, Level level, Marker marker,

477

Message message, Throwable throwable);

478

}

479

```

480

481

**Usage Examples:**

482

483

```java

484

// Implementing a custom logger by extending AbstractLogger

485

public class FileLogger extends AbstractLogger {

486

private final String name;

487

private final Level level;

488

private final PrintWriter writer;

489

490

public FileLogger(String name, Level level, String filename) throws IOException {

491

this.name = name;

492

this.level = level;

493

this.writer = new PrintWriter(new FileWriter(filename, true));

494

}

495

496

@Override

497

public String getName() {

498

return name;

499

}

500

501

@Override

502

public Level getLevel() {

503

return level;

504

}

505

506

@Override

507

public boolean isEnabled(Level level, Marker marker, String message, Throwable throwable) {

508

return level.isMoreSpecificThan(this.level);

509

}

510

511

@Override

512

public boolean isEnabled(Level level, Marker marker, String message) {

513

return level.isMoreSpecificThan(this.level);

514

}

515

516

@Override

517

public boolean isEnabled(Level level, Marker marker, String message, Object... params) {

518

return level.isMoreSpecificThan(this.level);

519

}

520

521

@Override

522

public boolean isEnabled(Level level, Marker marker, Object message, Throwable throwable) {

523

return level.isMoreSpecificThan(this.level);

524

}

525

526

@Override

527

public boolean isEnabled(Level level, Marker marker, Message message, Throwable throwable) {

528

return level.isMoreSpecificThan(this.level);

529

}

530

531

@Override

532

public void logMessage(String fqcn, Level level, Marker marker,

533

Message message, Throwable throwable) {

534

synchronized (writer) {

535

writer.printf("[%s] %s %s - %s%n",

536

new Date(), level, name, message.getFormattedMessage());

537

538

if (throwable != null) {

539

throwable.printStackTrace(writer);

540

}

541

542

writer.flush();

543

}

544

}

545

546

public void close() {

547

writer.close();

548

}

549

}

550

551

// Usage of custom logger

552

public class CustomLoggerUsage {

553

554

public void demonstrateCustomLogger() throws IOException {

555

// Create custom file logger

556

FileLogger fileLogger = new FileLogger("com.example.FileLogger", Level.DEBUG, "app.log");

557

558

// Use it like any other logger

559

fileLogger.info("Application started");

560

fileLogger.debug("Debug information: {}", "debug data");

561

fileLogger.error("An error occurred", new RuntimeException("test error"));

562

563

// Clean up

564

fileLogger.close();

565

}

566

}

567

```

568

569

### ThreadContext SPI Interfaces

570

571

Service provider interfaces for custom ThreadContext implementations.

572

573

```java { .api }

574

/**

575

* Interface for ThreadContext map implementations

576

*/

577

public interface ThreadContextMap {

578

/** Clear the context */

579

void clear();

580

581

/** Check if key exists */

582

boolean containsKey(String key);

583

584

/** Get value by key */

585

String get(String key);

586

587

/** Get copy of context as Map */

588

Map<String, String> getCopy();

589

590

/** Get immutable view of context */

591

Map<String, String> getImmutableMapOrNull();

592

593

/** Check if context is empty */

594

boolean isEmpty();

595

596

/** Put key-value pair */

597

void put(String key, String value);

598

599

/** Remove key */

600

void remove(String key);

601

}

602

603

/**

604

* Interface for ThreadContext stack implementations

605

*/

606

public interface ThreadContextStack extends ThreadContext.ContextStack {

607

/** Clear the stack */

608

void clear();

609

610

/** Check if stack contains value */

611

boolean contains(Object o);

612

613

/** Copy the stack */

614

ThreadContextStack copy();

615

616

/** Check if stack equals another */

617

boolean equals(Object obj);

618

619

/** Get stack depth */

620

int getDepth();

621

622

/** Get hash code */

623

int hashCode();

624

625

/** Check if stack is empty */

626

boolean isEmpty();

627

628

/** Get iterator */

629

Iterator<String> iterator();

630

631

/** Peek at top element */

632

String peek();

633

634

/** Pop top element */

635

String pop();

636

637

/** Push element */

638

void push(String message);

639

640

/** Get stack size */

641

int size();

642

643

/** Trim to specified depth */

644

void trim(int depth);

645

}

646

```

647

648

**Usage Examples:**

649

650

```java

651

// Custom ThreadContext map implementation

652

public class DatabaseThreadContextMap implements ThreadContextMap {

653

private final ThreadLocal<Map<String, String>> localMap =

654

ThreadLocal.withInitial(HashMap::new);

655

656

@Override

657

public void clear() {

658

localMap.get().clear();

659

// Could also persist to database here

660

}

661

662

@Override

663

public boolean containsKey(String key) {

664

return localMap.get().containsKey(key);

665

}

666

667

@Override

668

public String get(String key) {

669

return localMap.get().get(key);

670

}

671

672

@Override

673

public Map<String, String> getCopy() {

674

return new HashMap<>(localMap.get());

675

}

676

677

@Override

678

public Map<String, String> getImmutableMapOrNull() {

679

Map<String, String> map = localMap.get();

680

return map.isEmpty() ? null : Collections.unmodifiableMap(map);

681

}

682

683

@Override

684

public boolean isEmpty() {

685

return localMap.get().isEmpty();

686

}

687

688

@Override

689

public void put(String key, String value) {

690

localMap.get().put(key, value);

691

// Could also persist to database here

692

persistToDatabase(key, value);

693

}

694

695

@Override

696

public void remove(String key) {

697

localMap.get().remove(key);

698

// Could also remove from database here

699

removeFromDatabase(key);

700

}

701

702

private void persistToDatabase(String key, String value) {

703

// Database persistence logic

704

}

705

706

private void removeFromDatabase(String key) {

707

// Database removal logic

708

}

709

}

710

711

// Provider class for SPI registration

712

public class CustomProvider extends Provider {

713

714

public CustomProvider() {

715

super(10, "2.25.1"); // Priority and version

716

717

// Register custom implementations

718

put("LoggerContextFactory", "com.example.CustomLoggerContextFactory");

719

put("ThreadContextMap", "com.example.DatabaseThreadContextMap");

720

}

721

722

@Override

723

public String getVersionStr() {

724

return "2.25.1";

725

}

726

}

727

```