or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

basic-logging.mdfluent-logging.mdindex.mdmarkers.mdmdc.mdservice-providers.md

service-providers.mddocs/

0

# Service Provider Interface

1

2

SLF4J uses a service provider architecture that allows different logging framework implementations to be plugged in at runtime. The Service Provider Interface (SPI) defines the contracts that logging implementations must fulfill to integrate with SLF4J.

3

4

## Capabilities

5

6

### SLF4J Service Provider

7

8

Main service provider interface for pluggable logging implementations.

9

10

```java { .api }

11

/**

12

* This interface based on ServiceLoader paradigm. It replaces the old static-binding mechanism used in SLF4J versions 1.0.x to 1.7.x.

13

*/

14

public interface SLF4JServiceProvider {

15

/**

16

* Return the instance of ILoggerFactory that LoggerFactory class should bind to

17

* @return instance of ILoggerFactory

18

*/

19

ILoggerFactory getLoggerFactory();

20

21

/**

22

* Return the instance of IMarkerFactory that MarkerFactory class should bind to

23

* @return instance of IMarkerFactory

24

*/

25

IMarkerFactory getMarkerFactory();

26

27

/**

28

* Return the instance of MDCAdapter that MDC should bind to

29

* @return instance of MDCAdapter

30

*/

31

MDCAdapter getMDCAdapter();

32

33

/**

34

* Return the maximum API version for SLF4J that the logging implementation supports

35

* @return the string API version, for example "2.0.1"

36

*/

37

String getRequestedApiVersion();

38

39

/**

40

* Initialize the logging back-end

41

* WARNING: This method is intended to be called once by LoggerFactory class and from nowhere else

42

*/

43

void initialize();

44

}

45

```

46

47

### Logger Factory Interface

48

49

Factory interface for creating Logger instances.

50

51

```java { .api }

52

/**

53

* ILoggerFactory instances manufacture Logger instances by name

54

*/

55

public interface ILoggerFactory {

56

/**

57

* Return an appropriate Logger instance as specified by the name parameter

58

* @param name the name of the Logger to return

59

* @return a Logger instance

60

*/

61

Logger getLogger(String name);

62

}

63

```

64

65

### Marker Factory Interface

66

67

Factory interface for creating and managing Marker instances.

68

69

```java { .api }

70

/**

71

* Implementations of this interface are used to manufacture Marker instances

72

*/

73

public interface IMarkerFactory {

74

/**

75

* Manufacture a Marker instance by name. If the instance has been created earlier, return the previously created instance

76

* @param name the name of the marker to be created, null value is not allowed

77

* @return a Marker instance

78

*/

79

Marker getMarker(String name);

80

81

/**

82

* Checks if the marker with the name already exists

83

* @param name marker name

84

* @return true if the marker exists, false otherwise

85

*/

86

boolean exists(String name);

87

88

/**

89

* Detach an existing marker

90

* @param name The name of the marker to detach

91

* @return whether the marker could be detached or not

92

*/

93

boolean detachMarker(String name);

94

95

/**

96

* Create a marker which is detached from this IMarkerFactory

97

* @param name marker name

98

* @return a marker detached from this factory

99

*/

100

Marker getDetachedMarker(String name);

101

}

102

```

103

104

### Service Provider Discovery

105

106

How SLF4J discovers and loads service providers.

107

108

```java { .api }

109

/**

110

* LoggerFactory methods for service provider management

111

*/

112

public final class LoggerFactory {

113

/**

114

* Explicitly set the provider class via system property

115

*/

116

public static final String PROVIDER_PROPERTY_KEY = "slf4j.provider";

117

118

/**

119

* Return the SLF4JServiceProvider in use

120

* @return provider in use

121

*/

122

static SLF4JServiceProvider getProvider();

123

124

/**

125

* Return the ILoggerFactory instance in use

126

* @return the ILoggerFactory instance in use

127

*/

128

public static ILoggerFactory getILoggerFactory();

129

}

130

```

131

132

**Usage Examples:**

133

134

```java

135

// Implementing a custom service provider

136

public class MyLoggingServiceProvider implements SLF4JServiceProvider {

137

private ILoggerFactory loggerFactory;

138

private IMarkerFactory markerFactory;

139

private MDCAdapter mdcAdapter;

140

141

public MyLoggingServiceProvider() {

142

// Initialize factories during construction

143

this.loggerFactory = new MyLoggerFactory();

144

this.markerFactory = new MyMarkerFactory();

145

this.mdcAdapter = new MyMDCAdapter();

146

}

147

148

@Override

149

public ILoggerFactory getLoggerFactory() {

150

return loggerFactory;

151

}

152

153

@Override

154

public IMarkerFactory getMarkerFactory() {

155

return markerFactory;

156

}

157

158

@Override

159

public MDCAdapter getMDCAdapter() {

160

return mdcAdapter;

161

}

162

163

@Override

164

public String getRequestedApiVersion() {

165

return "2.0.17";

166

}

167

168

@Override

169

public void initialize() {

170

// Perform any necessary initialization

171

System.out.println("MyLoggingServiceProvider initialized");

172

}

173

}

174

175

// Custom logger factory implementation

176

public class MyLoggerFactory implements ILoggerFactory {

177

private final Map<String, Logger> loggerMap = new ConcurrentHashMap<>();

178

179

@Override

180

public Logger getLogger(String name) {

181

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

182

}

183

184

private Logger createLogger(String name) {

185

return new MyLogger(name);

186

}

187

}

188

```

189

190

### Location-Aware Logger Interface

191

192

Extended logger interface that provides location information.

193

194

```java { .api }

195

/**

196

* An optional interface helping integration with logging systems capable of extracting location information

197

*/

198

public interface LocationAwareLogger extends Logger {

199

// Level constants

200

public final static int TRACE_INT = 00;

201

public final static int DEBUG_INT = 10;

202

public final static int INFO_INT = 20;

203

public final static int WARN_INT = 30;

204

public final static int ERROR_INT = 40;

205

206

/**

207

* Printing method which can be used by implementation classes to receive location information

208

* @param marker The marker to be used for this event, may be null

209

* @param fqcn The fully qualified class name of the logger instance

210

* @param level One of the level integers defined in this interface

211

* @param message The message for the log event

212

* @param argArray An array of arguments to be used in conjunction with the message

213

* @param t The throwable associated with the log event, may be null

214

*/

215

public void log(Marker marker, String fqcn, int level, String message, Object[] argArray, Throwable t);

216

}

217

```

218

219

### Caller Boundary Awareness

220

221

Interface for loggers that support caller boundary detection.

222

223

```java { .api }

224

/**

225

* This interface is used by LoggingEventBuilder implementations to determine the caller boundary

226

*/

227

public interface CallerBoundaryAware {

228

/**

229

* Return the caller boundary for this Logger

230

* @return the caller boundary. Null by default.

231

*/

232

default String getCallerBoundary() {

233

return null;

234

}

235

}

236

```

237

238

### Logging Event Awareness

239

240

Interface for loggers that can handle LoggingEvent objects directly.

241

242

```java { .api }

243

/**

244

* Logger implementations which are capable of handling LoggingEvents should implement this interface

245

*/

246

public interface LoggingEventAware {

247

/**

248

* Log a LoggingEvent

249

* @param event the LoggingEvent to log

250

*/

251

void log(LoggingEvent event);

252

}

253

```

254

255

## Built-in Implementations

256

257

### No-Operation Implementations

258

259

Default implementations that perform no actual logging.

260

261

```java { .api }

262

/**

263

* NOPLogger is an implementation of Logger that performs no operation

264

*/

265

public class NOPLogger implements Logger {

266

public String getName() { return "NOP"; }

267

public boolean isTraceEnabled() { return false; }

268

public void trace(String msg) { }

269

// ... all methods do nothing

270

}

271

272

/**

273

* NOPLoggerFactory is a factory for NOPLogger instances

274

*/

275

public class NOPLoggerFactory implements ILoggerFactory {

276

public Logger getLogger(String name) {

277

return NOPLogger.NOP_LOGGER;

278

}

279

}

280

281

/**

282

* This implementation ignores all MDC operations

283

*/

284

public class NOPMDCAdapter implements MDCAdapter {

285

public void put(String key, String val) { }

286

public String get(String key) { return null; }

287

public void remove(String key) { }

288

public void clear() { }

289

// ... all methods do nothing

290

}

291

```

292

293

### Basic Implementations

294

295

Simple working implementations for basic scenarios.

296

297

```java { .api }

298

/**

299

* A simple implementation of MDC using InheritableThreadLocal

300

*/

301

public class BasicMDCAdapter implements MDCAdapter {

302

private InheritableThreadLocal<Map<String, String>> inheritableThreadLocal =

303

new InheritableThreadLocal<Map<String, String>>();

304

305

public void put(String key, String val);

306

public String get(String key);

307

public void remove(String key);

308

public void clear();

309

public Map<String, String> getCopyOfContextMap();

310

public void setContextMap(Map<String, String> contextMap);

311

// ... implementation details

312

}

313

314

/**

315

* BasicMarker holds references to other markers

316

*/

317

public class BasicMarker implements Marker {

318

private final String name;

319

private final List<Marker> referenceList = new ArrayList<>();

320

321

BasicMarker(String name);

322

public String getName();

323

public void add(Marker reference);

324

public boolean remove(Marker reference);

325

// ... implementation details

326

}

327

328

/**

329

* BasicMarkerFactory is a simple implementation of IMarkerFactory

330

*/

331

public class BasicMarkerFactory implements IMarkerFactory {

332

private final Map<String, Marker> markerMap = new ConcurrentHashMap<>();

333

334

public Marker getMarker(String name);

335

public boolean exists(String name);

336

public Marker getDetachedMarker(String name);

337

}

338

```

339

340

### Substitute Implementations

341

342

Temporary implementations used during SLF4J initialization.

343

344

```java { .api }

345

/**

346

* A logger implementation which logs via a delegate logger but can also buffer logging events when the delegate is null

347

*/

348

public class SubstituteLogger implements Logger {

349

private final String name;

350

private volatile Logger _delegate;

351

private final Queue<SubstituteLoggingEvent> eventQueue = new LinkedBlockingQueue<>();

352

353

public SubstituteLogger(String name);

354

public String getName();

355

void setDelegate(Logger delegate);

356

// ... delegates to _delegate when available, queues events otherwise

357

}

358

359

/**

360

* SubstituteLoggerFactory creates SubstituteLogger instances

361

*/

362

public class SubstituteLoggerFactory implements ILoggerFactory {

363

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

364

365

public Logger getLogger(String name);

366

public List<SubstituteLogger> getLoggers();

367

public void postInitialization();

368

public void clear();

369

}

370

```

371

372

## Service Provider Registration

373

374

### Java ServiceLoader

375

376

Register service providers using the standard Java ServiceLoader mechanism:

377

378

1. Create a file named `org.slf4j.spi.SLF4JServiceProvider` in `META-INF/services/`

379

2. Add the fully qualified class name of your service provider implementation

380

381

```

382

# META-INF/services/org.slf4j.spi.SLF4JServiceProvider

383

com.example.logging.MyLoggingServiceProvider

384

```

385

386

### Explicit Provider Configuration

387

388

Override automatic discovery using system property:

389

390

```java

391

// Set system property to explicitly specify provider

392

System.setProperty("slf4j.provider", "com.example.logging.MyLoggingServiceProvider");

393

394

// Or via command line

395

// -Dslf4j.provider=com.example.logging.MyLoggingServiceProvider

396

```

397

398

## Implementation Guidelines

399

400

### Service Provider Implementation

401

402

```java

403

public class CustomServiceProvider implements SLF4JServiceProvider {

404

private static final String REQUESTED_API_VERSION = "2.0.17";

405

406

private final ILoggerFactory loggerFactory;

407

private final IMarkerFactory markerFactory;

408

private final MDCAdapter mdcAdapter;

409

410

public CustomServiceProvider() {

411

// Initialize all components in constructor

412

this.loggerFactory = new CustomLoggerFactory();

413

this.markerFactory = new CustomMarkerFactory();

414

this.mdcAdapter = new CustomMDCAdapter();

415

}

416

417

@Override

418

public ILoggerFactory getLoggerFactory() {

419

return loggerFactory;

420

}

421

422

@Override

423

public IMarkerFactory getMarkerFactory() {

424

return markerFactory;

425

}

426

427

@Override

428

public MDCAdapter getMDCAdapter() {

429

return mdcAdapter;

430

}

431

432

@Override

433

public String getRequestedApiVersion() {

434

return REQUESTED_API_VERSION;

435

}

436

437

@Override

438

public void initialize() {

439

// Perform initialization tasks

440

// This method is called once by LoggerFactory

441

configureLogging();

442

validateConfiguration();

443

}

444

445

private void configureLogging() {

446

// Configure your logging backend

447

}

448

449

private void validateConfiguration() {

450

// Validate configuration and throw exceptions if invalid

451

}

452

}

453

```

454

455

### Logger Implementation

456

457

```java

458

public class CustomLogger implements Logger, LocationAwareLogger, LoggingEventAware {

459

private final String name;

460

private final CustomLoggingBackend backend;

461

462

public CustomLogger(String name, CustomLoggingBackend backend) {

463

this.name = name;

464

this.backend = backend;

465

}

466

467

@Override

468

public String getName() {

469

return name;

470

}

471

472

@Override

473

public boolean isDebugEnabled() {

474

return backend.isLevelEnabled(name, Level.DEBUG);

475

}

476

477

@Override

478

public void debug(String msg) {

479

if (isDebugEnabled()) {

480

backend.log(name, Level.DEBUG, msg, null, null);

481

}

482

}

483

484

@Override

485

public void debug(String format, Object arg) {

486

if (isDebugEnabled()) {

487

FormattingTuple ft = MessageFormatter.format(format, arg);

488

backend.log(name, Level.DEBUG, ft.getMessage(), null, ft.getThrowable());

489

}

490

}

491

492

// LocationAwareLogger implementation

493

@Override

494

public void log(Marker marker, String fqcn, int level, String message, Object[] argArray, Throwable t) {

495

if (backend.isLevelEnabled(name, Level.intToLevel(level))) {

496

backend.log(name, marker, fqcn, Level.intToLevel(level), message, argArray, t);

497

}

498

}

499

500

// LoggingEventAware implementation

501

@Override

502

public void log(LoggingEvent event) {

503

if (backend.isLevelEnabled(name, event.getLevel())) {

504

backend.log(event);

505

}

506

}

507

508

// ... implement all other Logger methods

509

}

510

```

511

512

### MDC Adapter Implementation

513

514

```java

515

public class CustomMDCAdapter implements MDCAdapter {

516

private final ThreadLocal<Map<String, String>> threadLocalMap = new ThreadLocal<>();

517

private final ThreadLocal<Map<String, Deque<String>>> threadLocalDequeMap = new ThreadLocal<>();

518

519

@Override

520

public void put(String key, String val) {

521

if (key == null) {

522

throw new IllegalArgumentException("key cannot be null");

523

}

524

525

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

526

if (map == null) {

527

map = new HashMap<>();

528

threadLocalMap.set(map);

529

}

530

map.put(key, val);

531

}

532

533

@Override

534

public String get(String key) {

535

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

536

return (map != null) ? map.get(key) : null;

537

}

538

539

@Override

540

public void remove(String key) {

541

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

542

if (map != null) {

543

map.remove(key);

544

if (map.isEmpty()) {

545

threadLocalMap.remove();

546

}

547

}

548

}

549

550

@Override

551

public void clear() {

552

threadLocalMap.remove();

553

threadLocalDequeMap.remove();

554

}

555

556

@Override

557

public void pushByKey(String key, String value) {

558

Map<String, Deque<String>> dequeMap = threadLocalDequeMap.get();

559

if (dequeMap == null) {

560

dequeMap = new HashMap<>();

561

threadLocalDequeMap.set(dequeMap);

562

}

563

564

Deque<String> deque = dequeMap.computeIfAbsent(key, k -> new ArrayDeque<>());

565

deque.push(value);

566

}

567

568

@Override

569

public String popByKey(String key) {

570

Map<String, Deque<String>> dequeMap = threadLocalDequeMap.get();

571

if (dequeMap != null) {

572

Deque<String> deque = dequeMap.get(key);

573

if (deque != null && !deque.isEmpty()) {

574

return deque.pop();

575

}

576

}

577

return null;

578

}

579

580

// ... implement remaining methods

581

}

582

```

583

584

## Version Compatibility

585

586

SLF4J service providers should declare their API compatibility:

587

588

```java

589

@Override

590

public String getRequestedApiVersion() {

591

// Declare the SLF4J API version this provider supports

592

return "2.0.17";

593

}

594

```

595

596

Version compatibility matrix:

597

- **"2.0"**: Supports SLF4J 2.0.x features including fluent API

598

- **"1.8"**: Basic SLF4J 1.x compatibility

599

- **"1.7"**: Legacy SLF4J 1.7.x compatibility

600

601

## Multiple Provider Handling

602

603

When multiple providers are found on the classpath:

604

605

1. SLF4J reports the ambiguity with a warning message

606

2. The first provider found is used

607

3. All other providers are ignored

608

4. Applications can override using the `slf4j.provider` system property

609

610

## Migration from SLF4J 1.x Bindings

611

612

SLF4J 2.0 replaces the old static binding mechanism:

613

614

- **Old**: `StaticLoggerBinder` and `StaticMarkerBinder` classes

615

- **New**: `SLF4JServiceProvider` interface with `ServiceLoader`

616

- **Compatibility**: SLF4J 2.0 detects and warns about old-style bindings

617

- **Migration**: Implement `SLF4JServiceProvider` and register via `ServiceLoader`