Skip to content

Commit

Permalink
Merge pull request #1706 from lilai23/feature_opentelemetry
Browse files Browse the repository at this point in the history
support install external agent in Sermant
  • Loading branch information
Sherlockhan authored Dec 23, 2024
2 parents 4ad77fd + 02633eb commit 85ba2c3
Show file tree
Hide file tree
Showing 30 changed files with 1,020 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ agent.config.enhancedClassesOutputPath=
# Enable the host service instance class to be loaded by the thread context classloader during interceptor execution. If enabled, the host class is loaded by the context classloader during interceptor execution for service governance logic use. The default value is true.
agent.config.useContextLoader=true
# List of class prefixes that need be ignored when bytecode enhancement is performed.
agent.config.ignoredPrefixes=io.sermant
agent.config.ignoredPrefixes=io.sermant,io.opentelemetry
# List of interfaces that need to be ignored when bytecode enhancement is used to search for a class. If all implementation classes of an interface do not want to be bytecode enhanced, you can configure this configuration item
agent.config.ignoredInterfaces=org.springframework.cglib.proxy.Factory
# Specifies which classes in the plugins are allowed to be bytecode enhanced (classes in the plugins are not allowed to be bytecode enhanced by default)
Expand All @@ -21,6 +21,12 @@ agent.config.preFilter.enable=false
agent.config.preFilter.path=
# File name of unmatched class name, the default file is 'unmatched_class_name.txt'
agent.config.preFilter.file=
# External agent injection
agent.config.externalAgent.injection=false
# External agent name, OTEL is tested and supported. Other agents need to be tested by developers
agent.config.externalAgent.name=OTEL
# File of external agent, example: /user/opentelemetry-javaagent.jar
agent.config.externalAgent.file=
#============================= core service configuration =============================#
# Heartbeat service switch
agent.service.heartbeat.enable=false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ agent.config.enhancedClassesOutputPath=
# Enable the host service instance class to be loaded by the thread context classloader during interceptor execution. If enabled, the host class is loaded by the context classloader during interceptor execution for service governance logic use. The default value is true.
agent.config.useContextLoader=true
# List of class prefixes that need be ignored when bytecode enhancement is performed.
agent.config.ignoredPrefixes=io.sermant
agent.config.ignoredPrefixes=io.sermant,io.opentelemetry
# List of interfaces that need to be ignored when bytecode enhancement is used to search for a class. If all implementation classes of an interface do not want to be bytecode enhanced, you can configure this configuration item
agent.config.ignoredInterfaces=org.springframework.cglib.proxy.Factory
# Specifies which classes in the plugins are allowed to be bytecode enhanced (classes in the plugins are not allowed to be bytecode enhanced by default)
Expand All @@ -21,6 +21,12 @@ agent.config.preFilter.enable=false
agent.config.preFilter.path=
# File name of unmatched class name, the default file is 'unmatched_class_name.txt'
agent.config.preFilter.file=
# External agent injection
agent.config.externalAgent.injection=false
# External agent name, OTEL is tested and supported. Other agents need to be tested by developers
agent.config.externalAgent.name=OTEL
# File of external agent, example: /user/opentelemetry-javaagent.jar
agent.config.externalAgent.file=
#============================= core service configuration =============================#
# Heartbeat service switch
agent.service.heartbeat.enable=true
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2021-2021 Huawei Technologies Co., Ltd. All rights reserved.
* Copyright (C) 2021-2024 Huawei Technologies Co., Ltd. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -17,13 +17,15 @@
package io.sermant.core;

import io.sermant.core.classloader.ClassLoaderManager;
import io.sermant.core.command.CommandProcessor;
import io.sermant.core.common.AgentType;
import io.sermant.core.common.BootArgsIndexer;
import io.sermant.core.common.CommonConstant;
import io.sermant.core.common.LoggerFactory;
import io.sermant.core.config.ConfigManager;
import io.sermant.core.event.EventManager;
import io.sermant.core.event.collector.FrameworkEventCollector;
import io.sermant.core.ext.ExternalAgentManager;
import io.sermant.core.notification.NotificationInfo;
import io.sermant.core.notification.NotificationManager;
import io.sermant.core.notification.SermantNotificationType;
Expand All @@ -33,6 +35,7 @@
import io.sermant.core.plugin.agent.ByteEnhanceManager;
import io.sermant.core.plugin.agent.adviser.AdviserInterface;
import io.sermant.core.plugin.agent.adviser.AdviserScheduler;
import io.sermant.core.plugin.agent.config.AgentConfig;
import io.sermant.core.plugin.agent.info.EnhancementManager;
import io.sermant.core.plugin.agent.template.DefaultAdviser;
import io.sermant.core.service.ServiceManager;
Expand Down Expand Up @@ -136,6 +139,24 @@ public static void install(String artifact, Map<String, Object> argsMap, Instrum
if (NotificationManager.isEnable()) {
NotificationManager.doNotify(new NotificationInfo(SermantNotificationType.LOAD_COMPLETE, null));
}

// cache instrumentation
CommandProcessor.cacheInstrumentation(instrumentation);

// install external agent, such as OTEL
handleExternalAgentInstallation(instrumentation);
}

private static void handleExternalAgentInstallation(Instrumentation instrumentation) {
AgentConfig agentConfig = ConfigManager.getConfig(AgentConfig.class);
if (agentConfig.isExternalAgentInjection()) {
try {
ExternalAgentManager.installExternalAgent(false, agentConfig.getExternalAgentName(),
agentConfig.getExternalAgentFile(), null, instrumentation);
} catch (Exception e) {
LOGGER.severe("Failed to install external agent: " + e.getMessage());
}
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2023-2023 Huawei Technologies Co., Ltd. All rights reserved.
* Copyright (C) 2023-2024 Huawei Technologies Co., Ltd. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -43,7 +43,12 @@ public enum Command {
/**
* Enhancement query instruction
*/
CHECK_ENHANCEMENT("CHECK-ENHANCEMENT");
CHECK_ENHANCEMENT("CHECK-ENHANCEMENT"),

/**
* Install external agent instruction
*/
INSTALL_EXTERNAL_AGENT("INSTALL-EXTERNAL-AGENT");

private final String value;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2023-2023 Huawei Technologies Co., Ltd. All rights reserved.
* Copyright (C) 2023-2024 Huawei Technologies Co., Ltd. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -19,6 +19,7 @@
import io.sermant.core.common.LoggerFactory;
import io.sermant.core.utils.StringUtils;

import java.lang.instrument.Instrumentation;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
Expand All @@ -41,12 +42,15 @@ public class CommandProcessor {

private static final String COMMAND = "command";

private static Instrumentation instrumentation;

static {
COMMAND_EXECUTOR_MAP.put(Command.INSTALL_PLUGINS.getValue(), new PluginsInstallCommandExecutor());
COMMAND_EXECUTOR_MAP.put(Command.UNINSTALL_AGENT.getValue(), new AgentUnInstallCommandExecutor());
COMMAND_EXECUTOR_MAP.put(Command.UNINSTALL_PLUGINS.getValue(), new PluginsUnInstallCommandExecutor());
COMMAND_EXECUTOR_MAP.put(Command.UPDATE_PLUGINS.getValue(), new PluginsUpdateCommandExecutor());
COMMAND_EXECUTOR_MAP.put(Command.CHECK_ENHANCEMENT.getValue(), new CheckEnhancementsCommandExecutor());
COMMAND_EXECUTOR_MAP.put(Command.INSTALL_EXTERNAL_AGENT.getValue(), new ExternalAgentInstallCommandExecutor());
}

/**
Expand Down Expand Up @@ -81,4 +85,22 @@ public static void process(Map<String, String> agentArgsMap) {
DynamicAgentArgsManager.refreshAgentArgs(agentArgsMap);
commandExecutor.execute(commandArgs);
}

/**
* cache instrumentation for dynamic agent installation
*
* @param inst instrumentation
*/
public static void cacheInstrumentation(Instrumentation inst) {
instrumentation = inst;
}

/**
* get instrumentation for dynamic agent installation
*
* @return instrumentation
*/
public static Instrumentation getInstrumentation() {
return instrumentation;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,13 @@ public static void refreshAgentArgs(Map<String, String> newAgentArgs) {
public static String getAgentArg(String key) {
return AGENT_ARGS.get(key);
}

/**
* get AGENT_ARGS map
*
* @return dynamical args
*/
public static Map<String, String> getAgentArgsMap() {
return AGENT_ARGS;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright (C) 2024-2024 Sermant Authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.sermant.core.command;

import io.sermant.core.common.CommonConstant;
import io.sermant.core.common.LoggerFactory;
import io.sermant.core.ext.ExternalAgentManager;
import io.sermant.core.utils.StringUtils;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* The command executor of external agent installation
*
* @author lilai
* @since 2024-12-14
*/
public class ExternalAgentInstallCommandExecutor implements CommandExecutor {
private static final Logger LOGGER = LoggerFactory.getLogger();

@Override
public void execute(String args) {
Map<String, String> agentArgsMap = DynamicAgentArgsManager.getAgentArgsMap();
String agentPath = agentArgsMap.get(CommonConstant.AGENT_FILE_KEY);
if (StringUtils.isEmpty(agentPath)) {
LOGGER.severe("Failed to install external agent: AGENT_FILE in command args is empty");
return;
}

try {
ExternalAgentManager.installExternalAgent(true, args, agentPath, agentArgsMap,
CommandProcessor.getInstrumentation());
} catch (IOException | NoSuchMethodException | ClassNotFoundException | InvocationTargetException
| IllegalAccessException e) {
LOGGER.log(Level.SEVERE, "Failed to install external agent: {0}. Error message: {1}",
new String[]{args, e.getMessage()});
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2021-2021 Huawei Technologies Co., Ltd. All rights reserved.
* Copyright (C) 2021-2024 Huawei Technologies Co., Ltd. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -142,6 +142,11 @@ public class CommonConstant {
*/
public static final String AGENT_PATH_KEY = "agentPath";

/**
* The key of agent file in dynamic installation
*/
public static final String AGENT_FILE_KEY = "AGENT_FILE";

private CommonConstant() {
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2023-2023 Huawei Technologies Co., Ltd. All rights reserved.
* Copyright (C) 2023-2024 Huawei Technologies Co., Ltd. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -164,4 +164,20 @@ public void collectdHotPluggingEvent(FrameworkEventDefinitions frameworkEventDef
frameworkEventDefinitions.getEventType(),
new EventInfo(frameworkEventDefinitions.getName(), description)));
}

/**
* Collect OpenTelemetry Agent start event
*
* @param startMethod the method name OpenTelemetry Agent starts by
*/
public void collectOtelStartEvent(String startMethod) {
if (!eventConfig.isEnable()) {
return;
}
String description = "OpenTelemetry Agent starts by " + startMethod;
offerEvent(new Event(FrameworkEventDefinitions.OTEL_START.getScope(),
FrameworkEventDefinitions.OTEL_START.getEventLevel(),
FrameworkEventDefinitions.OTEL_START.getEventType(),
new EventInfo(FrameworkEventDefinitions.OTEL_START.getName(), description)));
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2023-2023 Huawei Technologies Co., Ltd. All rights reserved.
* Copyright (C) 2023-2024 Huawei Technologies Co., Ltd. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -74,7 +74,17 @@ public enum FrameworkEventDefinitions {
/**
* Sermant plugin update event definition
*/
SERMANT_PLUGIN_UPDATE("SERMANT_PLUGIN_UPDATE", EventType.OPERATION, EventLevel.NORMAL);
SERMANT_PLUGIN_UPDATE("SERMANT_PLUGIN_UPDATE", EventType.OPERATION, EventLevel.NORMAL),

/**
* External agent install event definition
*/
EXTERNAL_AGENT_INSTALL("EXTERNAL_AGENT_INSTALL", EventType.OPERATION, EventLevel.NORMAL),

/**
* OpenTelemetry agent start event definition
*/
OTEL_START("OTEL_START", EventType.OPERATION, EventLevel.NORMAL);

/**
* event name
Expand Down
Loading

0 comments on commit 85ba2c3

Please sign in to comment.