io.opentelemetry.javaagent.logging.application.ApplicationLoggerFactory Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of opentelemetry-javaagent-internal-logging-application Show documentation
Show all versions of opentelemetry-javaagent-internal-logging-application Show documentation
Instrumentation of Java libraries using OpenTelemetry.
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.logging.application;
import io.opentelemetry.javaagent.bootstrap.InternalLogger;
import io.opentelemetry.javaagent.bootstrap.logging.ApplicationLoggerBridge;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicBoolean;
final class ApplicationLoggerFactory extends ApplicationLoggerBridge
implements InternalLogger.Factory {
private final AtomicBoolean installed = new AtomicBoolean();
private volatile InternalLogger.Factory actual = null;
private final ConcurrentMap inMemoryLoggers =
new ConcurrentHashMap<>();
private final InMemoryLogStore inMemoryLogStore;
ApplicationLoggerFactory(InMemoryLogStore inMemoryLogStore) {
this.inMemoryLogStore = inMemoryLogStore;
}
@Override
protected void install(InternalLogger.Factory applicationLoggerFactory) {
// just use the first bridge that gets discovered and ignore the rest
if (!installed.compareAndSet(false, true)) {
applicationLoggerFactory
.create(ApplicationLoggerBridge.class.getName())
.log(
InternalLogger.Level.WARN,
"Multiple application logger implementations were provided."
+ " The javaagent will use the first bridge provided and ignore the following ones (this one).",
null);
return;
}
// flushing may cause additional classes to be loaded (e.g. slf4j loads logback, which we
// instrument), so we're doing this repeatedly to clear the in-memory store and preserve the
// log ordering
while (inMemoryLogStore.currentSize() > 0) {
inMemoryLogStore.flush(applicationLoggerFactory);
}
inMemoryLogStore.setApplicationLoggerFactory(applicationLoggerFactory);
// actually install the application logger - from this point, everything will be logged
// directly through the application logging system
inMemoryLoggers
.values()
.forEach(
logger -> logger.replaceByActualLogger(applicationLoggerFactory.create(logger.name())));
this.actual = applicationLoggerFactory;
// if there are any leftover logs left in the memory store, flush them - this will cause some
// logs to go out of order, but at least we'll not lose any of them
if (inMemoryLogStore.currentSize() > 0) {
inMemoryLogStore.flush(applicationLoggerFactory);
}
// finally, free the memory
inMemoryLogStore.freeMemory();
inMemoryLoggers.clear();
}
@Override
public InternalLogger create(String name) {
InternalLogger.Factory bridgedLoggerFactory = this.actual;
// if the bridge was already installed skip the in-memory logger
if (bridgedLoggerFactory != null) {
return bridgedLoggerFactory.create(name);
}
return inMemoryLoggers.computeIfAbsent(name, n -> new ApplicationLogger(inMemoryLogStore, n));
}
}