diff --git a/.gitignore b/.gitignore index 439dede2ea..94cb7b737d 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,7 @@ *.log *.zip *.swp -*.DS_STORE +.DS_STORE ### STS ### .apt_generated diff --git a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/instrumentation/InstrumentationSettings.java b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/instrumentation/InstrumentationSettings.java index 7bc987b146..9c65291e6e 100644 --- a/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/instrumentation/InstrumentationSettings.java +++ b/inspectit-ocelot-config/src/main/java/rocks/inspectit/ocelot/config/model/instrumentation/InstrumentationSettings.java @@ -24,6 +24,14 @@ @NoArgsConstructor public class InstrumentationSettings { + /** + * Master switch for the instrumentation. Defines whether the agent is instrumenting classes. In case this is + * disabled, classes will not be instrumented, thus, any tracing and metric recording will NOT be possible. + * Only the instrumentation is affected by this flag. + * If this is set to false, it has the same effect as if no rules were defined. + */ + private boolean enabled = true; + /** * The configuration of internal parameters regarding the instrumentation process. */ @@ -59,7 +67,6 @@ public class InstrumentationSettings { @NotNull private Map<@NotBlank String, @Valid GenericActionSettings> actions = Collections.emptyMap(); - /** * The configuration of the defined scopes. The map's key represents an unique id for the related instrumentation scope. */ @@ -93,13 +100,13 @@ public class InstrumentationSettings { */ public void performValidation(InspectitConfig container, ViolationBuilder vios) { Set declaredMetrics = container.getMetrics().getDefinitions().keySet(); - rules.forEach((name, r) -> - r.performValidation(this, declaredMetrics, vios.atProperty("rules").atProperty(name))); + rules.forEach((name, r) -> r.performValidation(this, declaredMetrics, vios.atProperty("rules") + .atProperty(name))); HashSet verified = new HashSet<>(); // Verifies that scopes that are defined as Exclude also exist - scopes.forEach((name, s) -> - s.performValidation(name, this, vios.atProperty("scopes").atProperty(name), verified)); + scopes.forEach((name, s) -> s.performValidation(name, this, vios.atProperty("scopes") + .atProperty(name), verified)); } } diff --git a/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/instrumentation/core.yml b/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/instrumentation/core.yml index 5945c79f7f..fd16499b7b 100644 --- a/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/instrumentation/core.yml +++ b/inspectit-ocelot-config/src/main/resources/rocks/inspectit/ocelot/config/default/instrumentation/core.yml @@ -1,6 +1,12 @@ inspectit: # settings regarding the instrumentation performed on the target application instrumentation: + # Master switch for the instrumentation. Defines whether the agent is instrumenting classes. In case this is + # disabled, classes will not be instrumented, thus, any tracing and metric recording will NOT be possible. + # Only the instrumentation is affected by this flag. + # If this is set to false, it has the same effect as if no rules were defined. + enabled: true + # settings for special sensors special: # enables or disables the instrumentation to ensure context propagation across java.util.concurrent.Executor instances diff --git a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/config/InstrumentationConfigurationResolver.java b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/config/InstrumentationConfigurationResolver.java index d2b4cd7cef..578ef6ebb1 100644 --- a/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/config/InstrumentationConfigurationResolver.java +++ b/inspectit-ocelot-core/src/main/java/rocks/inspectit/ocelot/core/instrumentation/config/InstrumentationConfigurationResolver.java @@ -61,7 +61,6 @@ public class InstrumentationConfigurationResolver { @Autowired private PropagationMetaDataResolver propagationMetaDataResolver; - /** * Holds the currently active instrumentation configuration. */ @@ -79,14 +78,14 @@ private void init() { * for the given class. * * @param clazz the class for which the configuration shal lbe queried + * * @return the configuration or {@link ClassInstrumentationConfiguration#NO_INSTRUMENTATION} if this class should not be instrumented */ public ClassInstrumentationConfiguration getClassInstrumentationConfiguration(Class clazz) { - val config = currentConfig; + InstrumentationConfiguration config = currentConfig; try { - if (isIgnoredClass(clazz, config)) { + if (!config.getSource().isEnabled() || isIgnoredClass(clazz, config)) { return ClassInstrumentationConfiguration.NO_INSTRUMENTATION; - } else { TypeDescription description = TypeDescription.ForLoadedType.of(clazz); Set activeSensors = specialSensors.stream() @@ -110,6 +109,7 @@ public ClassInstrumentationConfiguration getClassInstrumentationConfiguration(Cl * Finds out for each method of the given class which rules apply and builds a {@link MethodHookConfiguration} for each instrumented method. * * @param clazz the class to check + * * @return a map mapping hook configurations to the methods which they should be applied on. */ public Map getHookConfigurations(Class clazz) { @@ -125,7 +125,8 @@ public Map getHookConfigurations(Cla Map result = new HashMap<>(); for (MethodDescription method : type.getDeclaredMethods()) { Set rulesMatchingOnMethod = narrowedRules.stream() - .filter(rule -> rule.getScopes().stream() + .filter(rule -> rule.getScopes() + .stream() .anyMatch(scope -> scope.getMethodMatcher().matches(method))) .collect(Collectors.toSet()); if (!rulesMatchingOnMethod.isEmpty()) { @@ -133,8 +134,7 @@ public Map getHookConfigurations(Cla Set matchedAndIncludedRules = resolveIncludes(config, rulesMatchingOnMethod); result.put(method, hookResolver.buildHookConfiguration(config, matchedAndIncludedRules)); } catch (Exception e) { - log.error("Could not build hook for {} of class {}", - CoreUtils.getSignature(method), clazz.getName(), e); + log.error("Could not build hook for {} of class {}", CoreUtils.getSignature(method), clazz.getName(), e); } } } @@ -147,7 +147,6 @@ public Map getHookConfigurations(Cla } return Collections.emptyMap(); - } /** @@ -155,6 +154,7 @@ public Map getHookConfigurations(Cla * * @param config the configuration which is used to resolve rule names to rules * @param rules the initial collection of rules + * * @return the set of the initial rules plus their includes */ @VisibleForTesting @@ -181,16 +181,16 @@ private void addWithIncludes(InstrumentationRule rule, InstrumentationConfigurat * * @param typeDescription the class which are the rules targeting * @param config the configuration which is used as basis for the rules + * * @return Returns a set containing rules with scopes targeting only the given type. */ private Set getNarrowedRulesFor(TypeDescription typeDescription, InstrumentationConfiguration config) { - return config.getRules().stream() - .map(rule -> Pair.of( - rule, - rule.getScopes() - .stream() - .filter(s -> s.getTypeMatcher().matches(typeDescription)) - .collect(Collectors.toSet()))) + return config.getRules() + .stream() + .map(rule -> Pair.of(rule, rule.getScopes() + .stream() + .filter(s -> s.getTypeMatcher().matches(typeDescription)) + .collect(Collectors.toSet()))) .filter(p -> !p.getRight().isEmpty()) .map(p -> p.getLeft().toBuilder().clearScopes().scopes(p.getRight()).build()) .collect(Collectors.toSet()); @@ -225,6 +225,7 @@ private InstrumentationConfiguration resolveConfiguration(InspectitConfig config * * @param clazz the class to check * @param config configuration to check for + * * @return true, if the class is ignored (=it should not be instrumented) */ @VisibleForTesting @@ -254,7 +255,10 @@ boolean isIgnoredClass(Class clazz, InstrumentationConfiguration config) { String name = clazz.getName(); - boolean isIgnored = config.getSource().getIgnoredPackages().entrySet().stream() + boolean isIgnored = config.getSource() + .getIgnoredPackages() + .entrySet() + .stream() .filter(Map.Entry::getValue) .anyMatch(e -> name.startsWith(e.getKey())); if (isIgnored) { @@ -262,7 +266,10 @@ boolean isIgnoredClass(Class clazz, InstrumentationConfiguration config) { } if (clazz.getClassLoader() == null) { - boolean isIgnoredOnBootstrap = config.getSource().getIgnoredBootstrapPackages().entrySet().stream() + boolean isIgnoredOnBootstrap = config.getSource() + .getIgnoredBootstrapPackages() + .entrySet() + .stream() .filter(Map.Entry::getValue) .anyMatch(e -> name.startsWith(e.getKey())); if (isIgnoredOnBootstrap) { diff --git a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/instrumentation/config/InstrumentationConfigurationResolverTest.java b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/instrumentation/config/InstrumentationConfigurationResolverTest.java index 615f1a4e7d..269fe3b645 100644 --- a/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/instrumentation/config/InstrumentationConfigurationResolverTest.java +++ b/inspectit-ocelot-core/src/test/java/rocks/inspectit/ocelot/core/instrumentation/config/InstrumentationConfigurationResolverTest.java @@ -59,6 +59,7 @@ void setupInstrumentationMock() { lenient().when(settings.getIgnoredBootstrapPackages()).thenReturn(Collections.emptyMap()); lenient().when(settings.getIgnoredPackages()).thenReturn(Collections.emptyMap()); lenient().when(settings.isExcludeLambdas()).thenReturn(true); + lenient().when(settings.isEnabled()).thenReturn(true); config = InstrumentationConfiguration.builder().source(settings).build(); } @@ -114,6 +115,20 @@ public void narrowingRule() throws IllegalAccessException { .containsExactly(ElementMatchers.nameEndsWithIgnoreCase("object"), ElementMatchers.any()); } + @Test + public void disabledInstrumentation() throws IllegalAccessException { + lenient().when(settings.isEnabled()).thenReturn(false); + + InstrumentationScope scope = new InstrumentationScope(ElementMatchers.any(), ElementMatchers.any()); + InstrumentationRule rule = InstrumentationRule.builder().name("name").scope(scope).build(); + config = InstrumentationConfiguration.builder().source(settings).rule(rule).build(); + FieldUtils.writeDeclaredField(resolver, "currentConfig", config, true); + + ClassInstrumentationConfiguration result = resolver.getClassInstrumentationConfiguration(Object.class); + + assertThat(result).isNotNull(); + assertThat(result).isSameAs(ClassInstrumentationConfiguration.NO_INSTRUMENTATION); + } } diff --git a/inspectit-ocelot-documentation/docs/breaking-changes/breaking-changes.md b/inspectit-ocelot-documentation/docs/breaking-changes/breaking-changes.md index 54ceb4596a..77fcf8ce1c 100644 --- a/inspectit-ocelot-documentation/docs/breaking-changes/breaking-changes.md +++ b/inspectit-ocelot-documentation/docs/breaking-changes/breaking-changes.md @@ -3,6 +3,10 @@ id: Breaking Changes title: Breaking Changes --- +## Breaking changes in 1.12.2 + +There are no breaking changes for version 1.12.2. + ## Breaking changes in 1.12.1 There are no breaking changes for version 1.12.1. @@ -11,12 +15,10 @@ There are no breaking changes for version 1.12.1. There are no breaking changes for version 1.12.0. - ## Breaking changes in 1.11.1 There are no breaking changes for version 1.11.1. - ## Breaking changes in 1.11.0 There are no breaking changes for version 1.11.0. diff --git a/inspectit-ocelot-documentation/docs/instrumentation/instrumentation.md b/inspectit-ocelot-documentation/docs/instrumentation/instrumentation.md index cdb2134fcb..049cb1af0a 100644 --- a/inspectit-ocelot-documentation/docs/instrumentation/instrumentation.md +++ b/inspectit-ocelot-documentation/docs/instrumentation/instrumentation.md @@ -12,16 +12,37 @@ Scopes are then used by [rules](instrumentation/rules.md). While scopes define w For the definition of rules, [actions](instrumentation/rules.md#actions) are a key concept. Long story short: actions allow you to specify _Java snippets in your configuration_ which will be executed to extract any data you want. This can be performance data such as the response time or any kind of business data, e.g. the shopping cart size. -> All instrumentation settings can be changed without restarting the application! They can even be changed while a previous instrumentation is still in progress. In this case the inspectIT Ocelot agent will automatically switch to the new instrumentation as soon as the configuration is loaded. +:::tip +All instrumentation settings can be changed without restarting the application! +They can even be changed while a previous instrumentation is **still in progress**. In this case the inspectIT Ocelot agent will automatically switch to the new instrumentation as soon as the configuration is loaded. +::: + +## Disabling the Instrumentation + +Since version `1.12.2`, the agent provides the option to disable its instrumentation process. +When disabled, the agent will **revert** any instrumentations done and restore the original state of all instrumented classes. +This would be like having no single rule (also none in the agent's default configuration) defined. +This results in no more runtime data (traces and metrics) being recorded from executed methods. + +```yaml +inspectit: + instrumentation: + enabled: true +``` + +:::note +Metrics which are not based on instrumentations like e.g. JVM internals (thread statistics, memory statistics, ...) are **not affected** by this option. +::: ## Naming Convention Differentiating between rules, actions and scopes as well as differentiating between keys and values in the configuration can be tricky from time to time. Therefore, to increase readability of your configuration files the following naming convention is recommended: -* Scope names always start with "s_", e.g. `s_my_scope`. -* Action names always start with "a_", e.g. `a_my_action`. -* Rule names always start with "r_", e.g. `r_my_rule`. +* Scope names always start with `s_`, e.g. `s_my_scope`. +* Action names always start with `a_`, e.g. `a_my_action`. +* Rule names always start with `r_`, e.g. `r_my_rule`. +* Context variables names should start with `d_`, e.g. `d_transaction_name`. * Fields which are defined by the user should always be put in single quotations marks, e.g. `input: 'my_input'`. This rule also applies to keys which can be entirely defined by the user, for example when defining the name of a custom action or attribute names. diff --git a/inspectit-ocelot-documentation/docs/instrumentation/process.md b/inspectit-ocelot-documentation/docs/instrumentation/process.md index 17f267eaa3..a4259958cd 100644 --- a/inspectit-ocelot-documentation/docs/instrumentation/process.md +++ b/inspectit-ocelot-documentation/docs/instrumentation/process.md @@ -3,7 +3,8 @@ id: process title: Instrumentation Process --- -The approach inspectIT Ocelot takes for instrumenting is fundamentally different from the approach of most other JVM instrumentation agents. InspectIT Ocelot does *not* instrument classes when they are loaded, the instrumentation is performed purely asynchronous in the background. +The approach inspectIT Ocelot takes for instrumenting is fundamentally different from the approach of most other JVM instrumentation agents. +InspectIT Ocelot does *not* instrument classes when they are loaded, the instrumentation is performed purely asynchronous in the background. In this background task inspectIT Ocelot essentially looks at every loaded class and performs an instrumentation if required by the active configuration. Hereby, the agent manages the classes he has to analyze in a queue. This queue is processed in batches to ensure that no CPU resources are blocked if they are required by the instrumented application. The batching is configurable using the `internal` settings: