org.neo4j.kernel.monitoring.tracing.DefaultTracers Maven / Gradle / Ivy
Show all versions of neo4j-kernel Show documentation
/*
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [https://neo4j.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package org.neo4j.kernel.monitoring.tracing;
import static java.lang.String.format;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.neo4j.kernel.monitoring.tracing.NullTracersFactory.NULL_TRACERS_NAME;
import org.neo4j.configuration.Config;
import org.neo4j.io.pagecache.tracing.PageCacheTracer;
import org.neo4j.io.pagecache.tracing.version.VersionStorageTracer;
import org.neo4j.kernel.database.NamedDatabaseId;
import org.neo4j.kernel.impl.transaction.tracing.DatabaseTracer;
import org.neo4j.lock.LockTracer;
import org.neo4j.logging.InternalLog;
import org.neo4j.monitoring.Monitors;
import org.neo4j.scheduler.JobScheduler;
import org.neo4j.service.Services;
import org.neo4j.time.SystemNanoClock;
/**
* Tracers
*
* While monitoring is a dynamic piece of infrastructure, where monitors can be wired up and torn down on the fly,
* the tracing infrastructure is static and hard-coded into the database on startup.
*
*
* Tracing is always on, though the default implementation is very light weight, incurring almost no overhead.
*
* The Tracers class
*
* This is the central hub in the tracing infrastructure.
*
*
* This class is responsible for choosing what tracing implementation to use, and for creating the relevant tracer
* components to distribute throughout the database instance.
*
*
* The tracing implementation is determined by the {@code internal.dbms.tracer} setting. Two built-in implementations
* exist: {@code default} and {@code null}. Alternative implementations can be loaded from the
* classpath by referencing their {@link org.neo4j.kernel.monitoring.tracing.TracerFactory} in a
* {@code META-INF/services/org.neo4j.kernel.monitoring.tracing.TracerFactory}, and setting
* {@code internal.dbms.tracer} to the appropriate value.
*
* Designing and implementing tracers
*
* There are two parts to tracers: the tracer implementation, which starts with the TracerFactory, and the
* subsystems that expose themselves to tracing.
*
*
* The traced subsystems are responsible for defining their own tracer and trace event interfaces, and they are
* responsible for getting hold of a tracer implementation through the Tracers class, and for producing trace
* events and feeding them with data.
*
*
* Traced subsystems define a hierarchy of events: large coarse grain events can contain smaller and more
* detailed events. Sibling events (that follow one after another in time) are typically spawned from the same
* parent event. The tracers and trace events are all defined as interfaces, and each interface should have a
* {@code NULL} field that references an implementation that does nothing, other than return other {@code NULL}
* implementations of any child event interfaces. The existing trace interfaces for transactions and the page
* cache, are good examples of this.
*
*
* The tracer implementations are responsible for implementing all the tracer and trace event interfaces in a way
* that is both fast, and robust. Robustness is important because tracer implementations are not allowed to throw
* exceptions, and they are not allowed to return {@code null} where a trace event is expected. Implementations
* may add implementation specific data to the events, if they want to report implementation specific data.
* They are also allowed to produce the {@code NULL} implementations that are associated with the various tracer
* and trace event interfaces. If, for instance, the implementation is not interested in the data that would be
* collected from a given trace event, then it can choose to use the {@code NULL} implementation. It could also
* be that something went wrong when building an event instance of the desired type, and since it cannot return
* {@code null} or throw exceptions, it is forced to return the {@code NULL} implementation.
*
*
* Tracer implementations should prefer to always return the same trace event implementation type for a given
* trace event type. Using more than one implementation type impairs JIT optimisation, as it causes the callsites
* in the traced code to no longer be monomorphic. Implementations should be built with performance in mind, as
* the code being traced is often quite important for the performance of the database.
*
*/
public class DefaultTracers implements Tracers {
private final PageCacheTracer pageCacheTracer;
private final TracerFactory tracersFactory;
private final SystemNanoClock clock;
private final InternalLog log;
/**
* Create a Tracers subsystem with the desired implementation, if it can be found and created.
*
* Otherwise, the default implementation is used, and a warning is logged to the given StringLogger.
* @param desiredImplementationName The name of the desired {@link org.neo4j.kernel.monitoring.tracing
* .TracerFactory} implementation, as given by its {@link TracerFactory#getName()} method.
* @param log A {@link InternalLog} for logging when the desired implementation cannot be created.
* @param monitors the monitoring manager
* @param jobScheduler a scheduler for async jobs
*/
public DefaultTracers(
String desiredImplementationName,
InternalLog log,
Monitors monitors,
JobScheduler jobScheduler,
SystemNanoClock clock,
Config config) {
this.clock = clock;
this.tracersFactory = createTracersFactory(desiredImplementationName, log);
this.pageCacheTracer = tracersFactory.createPageCacheTracer(monitors, jobScheduler, clock, log, config);
this.log = log;
}
@Override
public PageCacheTracer getPageCacheTracer() {
return pageCacheTracer;
}
@Override
public LockTracer getLockTracer() {
return tracersFactory.createLockTracer(clock);
}
@Override
public DatabaseTracer getDatabaseTracer(NamedDatabaseId namedDatabaseId) {
return tracersFactory.createDatabaseTracer(pageCacheTracer, clock);
}
@Override
public VersionStorageTracer getVersionStorageTracer(NamedDatabaseId namedDatabaseId) {
return tracersFactory.createVersionStorageTracer(pageCacheTracer, log);
}
private static TracerFactory createTracersFactory(String desiredImplementationName, InternalLog msgLog) {
if (NULL_TRACERS_NAME.equalsIgnoreCase(desiredImplementationName)) {
return new NullTracersFactory();
} else {
return selectTracerFactory(desiredImplementationName, msgLog);
}
}
private static TracerFactory selectTracerFactory(String desiredImplementationName, InternalLog msgLog) {
if (isBlank(desiredImplementationName)) {
return createDefaultTracerFactory();
}
try {
return Services.load(TracerFactory.class, desiredImplementationName).orElseGet(() -> {
msgLog.warn("Using default tracer implementations instead of '%s'", desiredImplementationName);
return DefaultTracers.createDefaultTracerFactory();
});
} catch (Exception e) {
msgLog.warn(
format(
"Failed to instantiate desired tracer implementations '%s', using default",
desiredImplementationName),
e);
return createDefaultTracerFactory();
}
}
private static TracerFactory createDefaultTracerFactory() {
return new DefaultTracerFactory();
}
}